diff --git a/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/aad/AADAuthenticationFilterAutoConfiguration.java b/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/aad/AADAuthenticationFilterAutoConfiguration.java index 082302ad..4999a84b 100644 --- a/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/aad/AADAuthenticationFilterAutoConfiguration.java +++ b/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/aad/AADAuthenticationFilterAutoConfiguration.java @@ -11,12 +11,14 @@ import com.nimbusds.jose.util.DefaultResourceRetriever; import com.nimbusds.jose.util.ResourceRetriever; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.config.BeanDefinition; 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.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; import org.springframework.context.annotation.Scope; import org.springframework.util.ClassUtils; @@ -26,6 +28,7 @@ import java.util.HashMap; @ConditionalOnWebApplication @ConditionalOnProperty(prefix = "azure.activedirectory", value = {"client-id", "client-secret"}) @EnableConfigurationProperties({AADAuthenticationProperties.class, ServiceEndpointsProperties.class}) +@PropertySource(value = "classpath:serviceEndpoints.properties") public class AADAuthenticationFilterAutoConfiguration { private static final Logger LOG = LoggerFactory.getLogger(AADAuthenticationProperties.class); @@ -47,7 +50,7 @@ public class AADAuthenticationFilterAutoConfiguration { * @return AADAuthenticationFilter bean */ @Bean - @Scope("singleton") + @Scope(BeanDefinition.SCOPE_SINGLETON) @ConditionalOnMissingBean(AADAuthenticationFilter.class) public AADAuthenticationFilter azureADJwtTokenFilter() { LOG.info("AzureADJwtTokenFilter Constructor."); @@ -56,7 +59,7 @@ public class AADAuthenticationFilterAutoConfiguration { } @Bean - @Scope("singleton") + @Scope(BeanDefinition.SCOPE_SINGLETON) @ConditionalOnMissingBean(ResourceRetriever.class) public ResourceRetriever getJWTResourceRetriever() { return new DefaultResourceRetriever(aadAuthProps.getJwtConnectTimeout(), aadAuthProps.getJwtReadTimeout(), diff --git a/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/aad/AADOAuth2AutoConfiguration.java b/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/aad/AADOAuth2AutoConfiguration.java index f7cacab1..defe4372 100644 --- a/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/aad/AADOAuth2AutoConfiguration.java +++ b/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/aad/AADOAuth2AutoConfiguration.java @@ -19,6 +19,7 @@ import org.springframework.security.oauth2.core.oidc.user.OidcUser; @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) @ConditionalOnProperty(prefix = "azure.activedirectory", value = "tenant-id") @PropertySource("classpath:/aad-oauth2-common.properties") +@PropertySource(value = "classpath:serviceEndpoints.properties") @EnableConfigurationProperties({AADAuthenticationProperties.class, ServiceEndpointsProperties.class}) public class AADOAuth2AutoConfiguration { private AADAuthenticationProperties aadAuthProps; diff --git a/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/aad/YamlFileApplicationContextInitializer.java b/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/aad/YamlFileApplicationContextInitializer.java deleted file mode 100644 index 0ebd9813..00000000 --- a/azure-spring-boot/src/main/java/com/microsoft/azure/spring/autoconfigure/aad/YamlFileApplicationContextInitializer.java +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See LICENSE in the project root for - * license information. - */ -package com.microsoft.azure.spring.autoconfigure.aad; - -import org.springframework.boot.env.YamlPropertySourceLoader; -import org.springframework.context.ApplicationContextInitializer; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.core.env.PropertySource; -import org.springframework.core.io.Resource; - -import java.io.IOException; -import java.util.List; - -/** - * Yaml file initializer to load the specified yaml configuration file, - * by default the Spring will load the application.yml file. - *

- * In order to avoid possible overwritten by users' default yaml configuration file. - */ -public class YamlFileApplicationContextInitializer - implements ApplicationContextInitializer { - - private static final String SERVICE_ENDPOINTS_YAML = "classpath:serviceEndpoints.yml"; - - private List> yamlPropertySourceLoad(ConfigurableApplicationContext context) { - final List> serviceEndpoints; - final Resource resource = context.getResource(SERVICE_ENDPOINTS_YAML); - final YamlPropertySourceLoader sourceLoader = new YamlPropertySourceLoader(); - - try { - serviceEndpoints = sourceLoader.load("serviceEndpoints", resource); - } catch (IOException e) { - throw new IllegalStateException("Cannot load the azure service endpoints configuration", e); - } - - if (serviceEndpoints.size() != 1) { - throw new IllegalStateException("There must be only 1 azure service endpoints configuration in classpath"); - } - - return serviceEndpoints; - } - - @Override - public void initialize(ConfigurableApplicationContext applicationContext) { - final List> serviceEndpoints = yamlPropertySourceLoad(applicationContext); - - applicationContext.getEnvironment().getPropertySources().addFirst(serviceEndpoints.get(0)); - } -} - diff --git a/azure-spring-boot/src/main/resources/META-INF/spring.factories b/azure-spring-boot/src/main/resources/META-INF/spring.factories index 68393a5f..f4adfe28 100644 --- a/azure-spring-boot/src/main/resources/META-INF/spring.factories +++ b/azure-spring-boot/src/main/resources/META-INF/spring.factories @@ -1,8 +1,6 @@ org.springframework.boot.env.EnvironmentPostProcessor=com.microsoft.azure.spring.cloundfoundry.environment.VcapProcessor,\ com.microsoft.azure.keyvault.spring.KeyVaultEnvironmentPostProcessor,\ com.microsoft.azure.spring.autoconfigure.sqlserver.AlwaysEncryptedEnvironmentPostProcessor -org.springframework.context.ApplicationContextInitializer=\ -com.microsoft.azure.spring.autoconfigure.aad.YamlFileApplicationContextInitializer org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.microsoft.azure.spring.autoconfigure.cosmosdb.DocumentDBAutoConfiguration,\ com.microsoft.azure.spring.autoconfigure.cosmosdb.DocumentDbRepositoriesAutoConfiguration,\ com.microsoft.azure.spring.autoconfigure.gremlin.GremlinAutoConfiguration,\ diff --git a/azure-spring-boot/src/main/resources/serviceEndpoints.properties b/azure-spring-boot/src/main/resources/serviceEndpoints.properties new file mode 100644 index 00000000..88345042 --- /dev/null +++ b/azure-spring-boot/src/main/resources/serviceEndpoints.properties @@ -0,0 +1,9 @@ +azure.service.endpoints.cn.aadSigninUri=https://login.partner.microsoftonline.cn/ +azure.service.endpoints.cn.aadGraphApiUri=https://graph.chinacloudapi.cn/ +azure.service.endpoints.cn.aadKeyDiscoveryUri=https://login.partner.microsoftonline.cn/common/discovery/keys +azure.service.endpoints.cn.aadMembershipRestUri=https://graph.chinacloudapi.cn/me/memberOf?api-version=1.6 +azure.service.endpoints.global.aadSigninUri=https://login.microsoftonline.com/ +azure.service.endpoints.global.aadGraphApiUri=https://graph.windows.net/ +azure.service.endpoints.global.aadKeyDiscoveryUri=https://login.microsoftonline.com/common/discovery/keys/ +azure.service.endpoints.global.aadMembershipRestUri=https://graph.windows.net/me/memberOf?api-version=1.6 + diff --git a/azure-spring-boot/src/main/resources/serviceEndpoints.yml b/azure-spring-boot/src/main/resources/serviceEndpoints.yml deleted file mode 100644 index f82f307b..00000000 --- a/azure-spring-boot/src/main/resources/serviceEndpoints.yml +++ /dev/null @@ -1,13 +0,0 @@ -azure: - service: - endpoints: - cn: - aadSigninUri: https://login.partner.microsoftonline.cn/ - aadGraphApiUri: https://graph.chinacloudapi.cn/ - aadKeyDiscoveryUri: https://login.partner.microsoftonline.cn/common/discovery/keys - aadMembershipRestUri: https://graph.chinacloudapi.cn/me/memberOf?api-version=1.6 - global: - aadSigninUri: https://login.microsoftonline.com/ - aadGraphApiUri: https://graph.windows.net/ - aadKeyDiscoveryUri: https://login.microsoftonline.com/common/discovery/keys - aadMembershipRestUri: https://graph.windows.net/me/memberOf?api-version=1.6 diff --git a/azure-spring-boot/src/test/java/com/microsoft/azure/spring/autoconfigure/aad/AADAuthenticationAutoConfigurationTest.java b/azure-spring-boot/src/test/java/com/microsoft/azure/spring/autoconfigure/aad/AADAuthenticationAutoConfigurationTest.java index d37bc74d..05312a56 100644 --- a/azure-spring-boot/src/test/java/com/microsoft/azure/spring/autoconfigure/aad/AADAuthenticationAutoConfigurationTest.java +++ b/azure-spring-boot/src/test/java/com/microsoft/azure/spring/autoconfigure/aad/AADAuthenticationAutoConfigurationTest.java @@ -8,6 +8,10 @@ package com.microsoft.azure.spring.autoconfigure.aad; import org.junit.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.runner.WebApplicationContextRunner; +import org.springframework.core.env.Environment; + +import java.util.Map; + import static org.assertj.core.api.Assertions.assertThat; public class AADAuthenticationAutoConfigurationTest { @@ -26,4 +30,41 @@ public class AADAuthenticationAutoConfigurationTest { assertThat(azureADJwtTokenFilter).isExactlyInstanceOf(AADAuthenticationFilter.class); }); } + + @Test + public void serviceEndpointsCanBeOverriden() { + this.contextRunner.withPropertyValues("azure.service.endpoints.global.aadKeyDiscoveryUri=https://test/", + "azure.service.endpoints.global.aadSigninUri=https://test/", + "azure.service.endpoints.global.aadGraphApiUri=https://test/", + "azure.service.endpoints.global.aadKeyDiscoveryUri=https://test/", + "azure.service.endpoints.global.aadMembershipRestUri=https://test/") + .run(context -> { + final Environment environment = context.getEnvironment(); + assertThat(environment.getProperty("azure.service.endpoints.global.aadSigninUri")) + .isEqualTo("https://test/"); + assertThat(environment.getProperty("azure.service.endpoints.global.aadGraphApiUri")) + .isEqualTo("https://test/"); + assertThat(environment.getProperty("azure.service.endpoints.global.aadKeyDiscoveryUri")) + .isEqualTo("https://test/"); + assertThat(environment.getProperty("azure.service.endpoints.global.aadMembershipRestUri")) + .isEqualTo("https://test/"); + final ServiceEndpointsProperties serviceEndpointsProperties = + context.getBean(ServiceEndpointsProperties.class); + assertThat(serviceEndpointsProperties) + .isNotNull().extracting(ServiceEndpointsProperties::getEndpoints).isNotEmpty(); + final Map endpoints = serviceEndpointsProperties.getEndpoints(); + assertThat(endpoints).hasSize(2); + assertThat(endpoints.get("cn")).isNotNull() + .extracting(ServiceEndpoints::getAadGraphApiUri, ServiceEndpoints::getAadKeyDiscoveryUri, + ServiceEndpoints::getAadMembershipRestUri, ServiceEndpoints::getAadSigninUri) + .containsExactly("https://graph.chinacloudapi.cn/", + "https://login.partner.microsoftonline.cn/common/discovery/keys", + "https://graph.chinacloudapi.cn/me/memberOf?api-version=1.6", + "https://login.partner.microsoftonline.cn/"); + assertThat(endpoints.get("global")).isNotNull() + .extracting(ServiceEndpoints::getAadGraphApiUri, ServiceEndpoints::getAadKeyDiscoveryUri, + ServiceEndpoints::getAadMembershipRestUri, ServiceEndpoints::getAadSigninUri) + .containsExactly("https://test/", "https://test/", "https://test/", "https://test/"); + }); + } } diff --git a/azure-spring-boot/src/test/java/com/microsoft/azure/spring/autoconfigure/aad/AADOAuth2ConfigTest.java b/azure-spring-boot/src/test/java/com/microsoft/azure/spring/autoconfigure/aad/AADOAuth2ConfigTest.java index b7d06f56..96dcb4e6 100644 --- a/azure-spring-boot/src/test/java/com/microsoft/azure/spring/autoconfigure/aad/AADOAuth2ConfigTest.java +++ b/azure-spring-boot/src/test/java/com/microsoft/azure/spring/autoconfigure/aad/AADOAuth2ConfigTest.java @@ -12,12 +12,18 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.core.env.Environment; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.core.io.support.ResourcePropertySource; import org.springframework.security.oauth2.client.userinfo.OAuth2UserService; +import org.springframework.test.context.support.TestPropertySourceUtils; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + public class AADOAuth2ConfigTest { private static final String AAD_OAUTH2_MINIMUM_PROPS = "aad-backend-oauth2-minimum.properties"; private Resource testResource; @@ -66,10 +72,51 @@ public class AADOAuth2ConfigTest { testContext.getBean(OAuth2UserService.class); } - private AnnotationConfigWebApplicationContext initTestContext() { + @Test + public void testEndpointsPropertiesLoadAndOverridable() { + testContext = initTestContext("azure.service.endpoints.global.aadKeyDiscoveryUri=https://test/", + "azure.service.endpoints.global.aadSigninUri=https://test/", + "azure.service.endpoints.global.aadGraphApiUri=https://test/", + "azure.service.endpoints.global.aadKeyDiscoveryUri=https://test/", + "azure.service.endpoints.global.aadMembershipRestUri=https://test/"); + + + final Environment environment = testContext.getEnvironment(); + assertThat(environment.getProperty("azure.service.endpoints.global.aadSigninUri")) + .isEqualTo("https://test/"); + assertThat(environment.getProperty("azure.service.endpoints.global.aadGraphApiUri")) + .isEqualTo("https://test/"); + assertThat(environment.getProperty("azure.service.endpoints.global.aadKeyDiscoveryUri")) + .isEqualTo("https://test/"); + assertThat(environment.getProperty("azure.service.endpoints.global.aadMembershipRestUri")) + .isEqualTo("https://test/"); + final ServiceEndpointsProperties serviceEndpointsProperties = + testContext.getBean(ServiceEndpointsProperties.class); + assertThat(serviceEndpointsProperties) + .isNotNull().extracting(ServiceEndpointsProperties::getEndpoints).isNotEmpty(); + final Map endpoints = serviceEndpointsProperties.getEndpoints(); + assertThat(endpoints).hasSize(2); + assertThat(endpoints.get("cn")).isNotNull() + .extracting(ServiceEndpoints::getAadGraphApiUri, ServiceEndpoints::getAadKeyDiscoveryUri, + ServiceEndpoints::getAadMembershipRestUri, ServiceEndpoints::getAadSigninUri) + .containsExactly("https://graph.chinacloudapi.cn/", + "https://login.partner.microsoftonline.cn/common/discovery/keys", + "https://graph.chinacloudapi.cn/me/memberOf?api-version=1.6", + "https://login.partner.microsoftonline.cn/"); + assertThat(endpoints.get("global")).isNotNull() + .extracting(ServiceEndpoints::getAadGraphApiUri, ServiceEndpoints::getAadKeyDiscoveryUri, + ServiceEndpoints::getAadMembershipRestUri, ServiceEndpoints::getAadSigninUri) + .containsExactly("https://test/", "https://test/", "https://test/", "https://test/"); + + } + + private AnnotationConfigWebApplicationContext initTestContext(String... environment) { final AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); context.getEnvironment().getPropertySources().addLast(testPropResource); + if (environment.length > 0) { + TestPropertySourceUtils.addInlinedPropertiesToEnvironment(context, environment); + } context.register(AADOAuth2AutoConfiguration.class); context.refresh();