diff --git a/eng/code-quality-reports/src/main/resources/revapi/revapi.json b/eng/code-quality-reports/src/main/resources/revapi/revapi.json index 41e22b303156..fce21882f76b 100644 --- a/eng/code-quality-reports/src/main/resources/revapi/revapi.json +++ b/eng/code-quality-reports/src/main/resources/revapi/revapi.json @@ -405,6 +405,11 @@ "new": "method void com.mysql.cj.protocol.ServerSession::setOldStatusFlags(int)", "justification": "Method was added to an interface." }, + { + "code": "java.method.addedToInterface", + "new": "method java.lang.String com.azure.spring.cloud.core.provider.authentication.TokenCredentialOptionsProvider.TokenCredentialOptions::getTokenCredentialBeanName()", + "justification": "Support to configure token credential bean name." + }, { "code": "java.annotation.added", "old": "method com.azure.cosmos.CosmosDiagnosticsContext com.azure.cosmos.CosmosDiagnostics::getDiagnosticsContext()", diff --git a/sdk/spring/CHANGELOG.md b/sdk/spring/CHANGELOG.md index cf42dd46fb97..cb176866282a 100644 --- a/sdk/spring/CHANGELOG.md +++ b/sdk/spring/CHANGELOG.md @@ -5,6 +5,13 @@ ### Spring Cloud Azure Autoconfigure This section includes changes in `spring-cloud-azure-autoconfigure` module. +#### Features Added +- Support to configure token credential bean name. [#41977](https://github.com/Azure/azure-sdk-for-java/issues/41977). + +#### Bugs Fixed +- Fix primitive type prop (isManagedIdentityEnabled) copy issue. [#41977](https://github.com/Azure/azure-sdk-for-java/issues/41977). +- Fix to support multiple JDBC datasource prop passwordless. [#41977](https://github.com/Azure/azure-sdk-for-java/issues/41977). + #### Breaking Changes - Change the default Service Bus JMS connection factory to `JmsPoolConnectionFactory` from `CachingConnectionFactory`. Set `spring.jms.cache.enabled=true` to continue using `CachingConnectionFactory` [#42306](https://github.com/Azure/azure-sdk-for-java/pull/42306). diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/context/AzureContextUtils.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/context/AzureContextUtils.java index 381b8cf6de90..a5816594b636 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/context/AzureContextUtils.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/context/AzureContextUtils.java @@ -93,4 +93,10 @@ private AzureContextUtils() { */ public static final String PASSWORDLESS_KAFKA_PROPERTIES_BEAN_POST_PROCESSOR_BEAN_NAME = "azurePasswordlessKafkaPropertiesBeanPostProcessor"; + + /** + * Bean name of the SpringTokenCredentialProviderContextProvider for the passwordless token credential acquisition. + */ + public static final String SPRING_TOKEN_CREDENTIAL_PROVIDER_CONTEXT_BEAN_NAME = + "springTokenCredentialProviderContextProvider"; } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/context/AzureGlobalPropertiesAutoConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/context/AzureGlobalPropertiesAutoConfiguration.java index c169fbb1c6c4..f51f895b24bf 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/context/AzureGlobalPropertiesAutoConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/context/AzureGlobalPropertiesAutoConfiguration.java @@ -5,6 +5,7 @@ import com.azure.spring.cloud.autoconfigure.implementation.context.properties.AzureGlobalProperties; import org.springframework.beans.factory.aot.BeanRegistrationExcludeFilter; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.RegisteredBean; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -14,8 +15,10 @@ import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.core.env.Environment; import org.springframework.core.type.AnnotationMetadata; +import org.springframework.util.ClassUtils; import static com.azure.spring.cloud.autoconfigure.implementation.context.AzureContextUtils.AZURE_GLOBAL_PROPERTY_BEAN_NAME; +import static com.azure.spring.cloud.autoconfigure.implementation.context.AzureContextUtils.SPRING_TOKEN_CREDENTIAL_PROVIDER_CONTEXT_BEAN_NAME; import static org.springframework.beans.factory.support.BeanDefinitionBuilder.genericBeanDefinition; /** @@ -29,6 +32,9 @@ public class AzureGlobalPropertiesAutoConfiguration { static class Registrar implements EnvironmentAware, ImportBeanDefinitionRegistrar { private Environment environment; + private static final String AZURE_AUTHENTICATION_TEMPLATE_CLASS_NAME = + "com.azure.identity.extensions.implementation.template.AzureAuthenticationTemplate"; + @Override public void setEnvironment(Environment environment) { this.environment = environment; @@ -38,12 +44,14 @@ public void setEnvironment(Environment environment) { public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { if (!registry.containsBeanDefinition(AZURE_GLOBAL_PROPERTY_BEAN_NAME)) { - registry.registerBeanDefinition(AZURE_GLOBAL_PROPERTY_BEAN_NAME, - genericBeanDefinition(AzureGlobalProperties.class, - () -> Binder.get(this.environment) - .bindOrCreate(AzureGlobalProperties.PREFIX, - AzureGlobalProperties.class)) - .getBeanDefinition()); + BeanDefinitionBuilder definitionBuilder = genericBeanDefinition(AzureGlobalProperties.class, + () -> Binder.get(this.environment) + .bindOrCreate(AzureGlobalProperties.PREFIX, + AzureGlobalProperties.class)); + if (ClassUtils.isPresent(AZURE_AUTHENTICATION_TEMPLATE_CLASS_NAME, null)) { + definitionBuilder.addDependsOn(SPRING_TOKEN_CREDENTIAL_PROVIDER_CONTEXT_BEAN_NAME); + } + registry.registerBeanDefinition(AZURE_GLOBAL_PROPERTY_BEAN_NAME, definitionBuilder.getBeanDefinition()); } } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/context/AzureTokenCredentialAutoConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/context/AzureTokenCredentialAutoConfiguration.java index c6a9ee8e2760..6367bd2056be 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/context/AzureTokenCredentialAutoConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/context/AzureTokenCredentialAutoConfiguration.java @@ -37,6 +37,7 @@ import org.springframework.boot.task.TaskExecutorBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.support.GenericApplicationContext; import org.springframework.core.annotation.Order; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.util.StringUtils; @@ -55,10 +56,12 @@ public class AzureTokenCredentialAutoConfiguration extends AzureServiceConfigurationBase { private static final Logger LOGGER = LoggerFactory.getLogger(AzureTokenCredentialAutoConfiguration.class); + private final GenericApplicationContext applicationContext; private final IdentityClientProperties identityClientProperties; - AzureTokenCredentialAutoConfiguration(AzureGlobalProperties azureGlobalProperties) { + AzureTokenCredentialAutoConfiguration(GenericApplicationContext applicationContext, AzureGlobalProperties azureGlobalProperties) { super(azureGlobalProperties); + this.applicationContext = applicationContext; this.identityClientProperties = loadProperties(azureGlobalProperties, new IdentityClientProperties()); } @@ -101,6 +104,11 @@ AzureTokenCredentialResolver azureTokenCredentialResolver( return null; } + String tokenCredentialBeanName = azureProperties.getCredential().getTokenCredentialBeanName(); + if (StringUtils.hasText(tokenCredentialBeanName)) { + return this.applicationContext.getBean(tokenCredentialBeanName, TokenCredential.class); + } + final TokenCredentialOptionsProvider.TokenCredentialOptions properties = azureProperties.getCredential(); final String tenantId = azureProperties.getProfile().getTenantId(); final String clientId = properties.getClientId(); diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/context/SpringTokenCredentialProviderContextProviderAutoConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/context/SpringTokenCredentialProviderContextProviderAutoConfiguration.java new file mode 100644 index 000000000000..32abeee6edd2 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/context/SpringTokenCredentialProviderContextProviderAutoConfiguration.java @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.cloud.autoconfigure.implementation.context; + +import com.azure.identity.extensions.implementation.template.AzureAuthenticationTemplate; +import com.azure.spring.cloud.autoconfigure.implementation.jdbc.SpringTokenCredentialProviderContextProvider; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import static com.azure.spring.cloud.autoconfigure.implementation.context.AzureContextUtils.SPRING_TOKEN_CREDENTIAL_PROVIDER_CONTEXT_BEAN_NAME; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for Spring Cloud Azure {@link SpringTokenCredentialProviderContextProvider}. + * + * @since 5.17.0 + */ +@Configuration(proxyBeanMethods = false) +@ConditionalOnClass(AzureAuthenticationTemplate.class) +class SpringTokenCredentialProviderContextProviderAutoConfiguration { + + @Bean(name = SPRING_TOKEN_CREDENTIAL_PROVIDER_CONTEXT_BEAN_NAME) + @ConditionalOnMissingBean + SpringTokenCredentialProviderContextProvider springTokenCredentialProviderContextProvider() { + return new SpringTokenCredentialProviderContextProvider(); + } +} diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/data/redis/AzureLettucePasswordlessAutoConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/data/redis/AzureLettucePasswordlessAutoConfiguration.java index 07ca86c1308a..e10df681b455 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/data/redis/AzureLettucePasswordlessAutoConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/data/redis/AzureLettucePasswordlessAutoConfiguration.java @@ -4,8 +4,8 @@ package com.azure.spring.cloud.autoconfigure.implementation.data.redis; import com.azure.spring.cloud.autoconfigure.implementation.context.properties.AzureGlobalProperties; -import com.azure.spring.cloud.autoconfigure.implementation.passwordless.properties.AzureRedisPasswordlessProperties; import com.azure.spring.cloud.autoconfigure.implementation.data.redis.lettuce.AzureRedisCredentials; +import com.azure.spring.cloud.autoconfigure.implementation.passwordless.properties.AzureRedisPasswordlessProperties; import com.azure.spring.cloud.core.implementation.util.AzurePasswordlessPropertiesUtils; import io.lettuce.core.ClientOptions; import io.lettuce.core.RedisCredentials; @@ -54,8 +54,8 @@ AzureRedisPasswordlessProperties redisPasswordlessProperties() { AzureRedisCredentials azureRedisCredentials(RedisProperties redisProperties, AzureRedisPasswordlessProperties azureRedisPasswordlessProperties, AzureGlobalProperties azureGlobalProperties) { - return new AzureRedisCredentials(redisProperties.getUsername(), - mergeAzureProperties(azureGlobalProperties, azureRedisPasswordlessProperties)); + AzureRedisPasswordlessProperties redisPasswordlessProperties = mergeAzureProperties(azureGlobalProperties, azureRedisPasswordlessProperties); + return new AzureRedisCredentials(redisProperties.getUsername(), redisPasswordlessProperties); } @Bean(name = "azureLettuceClientConfigurationBuilderCustomizer") @@ -81,5 +81,4 @@ private AzureRedisPasswordlessProperties mergeAzureProperties(AzureGlobalPropert AzurePasswordlessPropertiesUtils.mergeAzureCommonProperties(azureGlobalProperties, azurePasswordlessProperties, mergedProperties); return mergedProperties; } - } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/data/redis/lettuce/AzureRedisCredentials.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/data/redis/lettuce/AzureRedisCredentials.java index 6640fcd3b1a4..059b5db1f501 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/data/redis/lettuce/AzureRedisCredentials.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/data/redis/lettuce/AzureRedisCredentials.java @@ -14,6 +14,9 @@ import org.springframework.util.StringUtils; import java.util.Objects; +import java.util.Properties; + +import static com.azure.spring.cloud.autoconfigure.implementation.util.SpringPasswordlessPropertiesUtils.enhancePasswordlessProperties; public class AzureRedisCredentials implements RedisCredentials { @@ -25,9 +28,11 @@ public class AzureRedisCredentials implements RedisCredentials { * Create instance of Azure Redis Credentials */ public AzureRedisCredentials(String username, PasswordlessProperties passwordlessProperties) { - Objects.requireNonNull(passwordlessProperties, "PasswordlessProperties is required"); + Objects.requireNonNull(passwordlessProperties, "PasswordlessProperties is required."); azureAuthenticationTemplate = new AzureAuthenticationTemplate(); - azureAuthenticationTemplate.init(passwordlessProperties.toPasswordlessProperties()); + Properties properties = passwordlessProperties.toPasswordlessProperties(); + enhancePasswordlessProperties("spring.data.redis.azure", passwordlessProperties, properties); + azureAuthenticationTemplate.init(properties); this.username = resolveUsername(azureAuthenticationTemplate, username); } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/jdbc/AzureJdbcAutoConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/jdbc/AzureJdbcAutoConfiguration.java index 2b52462cbdcf..9cb55b4266ea 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/jdbc/AzureJdbcAutoConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/jdbc/AzureJdbcAutoConfiguration.java @@ -12,7 +12,6 @@ import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.DependsOn; /** @@ -29,15 +28,7 @@ public class AzureJdbcAutoConfiguration { @Bean @ConditionalOnMissingBean - @DependsOn("springTokenCredentialProviderContextProvider") JdbcPropertiesBeanPostProcessor jdbcConfigurationPropertiesBeanPostProcessor() { return new JdbcPropertiesBeanPostProcessor(); } - - @Bean - @ConditionalOnMissingBean - SpringTokenCredentialProviderContextProvider springTokenCredentialProviderContextProvider() { - return new SpringTokenCredentialProviderContextProvider(); - } - } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/jdbc/JdbcPropertiesBeanPostProcessor.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/jdbc/JdbcPropertiesBeanPostProcessor.java index 9af7ed53bad8..fe160a3e071b 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/jdbc/JdbcPropertiesBeanPostProcessor.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/jdbc/JdbcPropertiesBeanPostProcessor.java @@ -2,20 +2,17 @@ // Licensed under the MIT License. package com.azure.spring.cloud.autoconfigure.implementation.jdbc; -import com.azure.core.credential.TokenCredential; -import com.azure.identity.extensions.implementation.credential.TokenCredentialProviderOptions; -import com.azure.identity.extensions.implementation.credential.provider.TokenCredentialProvider; -import com.azure.identity.extensions.implementation.enums.AuthProperty; import com.azure.spring.cloud.autoconfigure.implementation.context.properties.AzureGlobalProperties; +import com.azure.spring.cloud.autoconfigure.implementation.passwordless.properties.AzureJdbcPasswordlessProperties; import com.azure.spring.cloud.core.implementation.util.AzurePasswordlessPropertiesUtils; import com.azure.spring.cloud.core.implementation.util.AzureSpringIdentifier; -import com.azure.spring.cloud.service.implementation.identity.credential.provider.SpringTokenCredentialProvider; -import com.azure.spring.cloud.autoconfigure.implementation.passwordless.properties.AzureJdbcPasswordlessProperties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; +import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.bind.Binder; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; @@ -24,6 +21,7 @@ import org.springframework.core.Ordered; import org.springframework.core.PriorityOrdered; import org.springframework.core.env.Environment; +import org.springframework.core.type.AnnotatedTypeMetadata; import org.springframework.util.StringUtils; import java.util.HashMap; @@ -36,7 +34,7 @@ import static com.azure.spring.cloud.autoconfigure.implementation.jdbc.JdbcPropertyConstants.POSTGRESQL_PROPERTY_NAME_APPLICATION_NAME; import static com.azure.spring.cloud.autoconfigure.implementation.jdbc.JdbcPropertyConstants.POSTGRESQL_PROPERTY_NAME_ASSUME_MIN_SERVER_VERSION; import static com.azure.spring.cloud.autoconfigure.implementation.jdbc.JdbcPropertyConstants.POSTGRESQL_PROPERTY_VALUE_ASSUME_MIN_SERVER_VERSION; -import static com.azure.spring.cloud.service.implementation.identity.credential.provider.SpringTokenCredentialProvider.PASSWORDLESS_TOKEN_CREDENTIAL_BEAN_NAME; +import static com.azure.spring.cloud.autoconfigure.implementation.util.SpringPasswordlessPropertiesUtils.enhancePasswordlessProperties; /** @@ -45,9 +43,6 @@ class JdbcPropertiesBeanPostProcessor implements BeanPostProcessor, EnvironmentAware, ApplicationContextAware, PriorityOrdered { private static final Logger LOGGER = LoggerFactory.getLogger(JdbcPropertiesBeanPostProcessor.class); - private static final String SPRING_TOKEN_CREDENTIAL_PROVIDER_CLASS_NAME = SpringTokenCredentialProvider.class.getName(); - private static final String SPRING_CLOUD_AZURE_DATASOURCE_PREFIX = "spring.datasource.azure"; - private GenericApplicationContext applicationContext; private Environment environment; @@ -61,16 +56,25 @@ public int getOrder() { public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (bean instanceof DataSourceProperties) { DataSourceProperties dataSourceProperties = (DataSourceProperties) bean; - AzureJdbcPasswordlessProperties properties = buildAzureProperties(); - + BeanDefinition bd = applicationContext.getBeanDefinition(beanName); + String datasourcePropertiesPrefix = "spring.datasource"; + if (bd != null && bd.getSource() instanceof AnnotatedTypeMetadata metadata) { + Map annotationAttributes = metadata.getAnnotationAttributes(ConfigurationProperties.class.getName()); + if (annotationAttributes != null) { + datasourcePropertiesPrefix = (String) annotationAttributes.get("prefix"); + } + } + String passwordlessPropertiesPrefix = datasourcePropertiesPrefix + ".azure"; + AzureJdbcPasswordlessProperties properties = buildAzureProperties(passwordlessPropertiesPrefix); if (!properties.isPasswordlessEnabled()) { - LOGGER.debug("Feature passwordless authentication is not enabled, skip enhancing jdbc url."); + LOGGER.debug("Feature passwordless authentication is not enabled(bean name is {} and {}.passwordless-enabled=false), " + + "skip enhancing jdbc url.", beanName, passwordlessPropertiesPrefix); return bean; } String url = dataSourceProperties.getUrl(); if (!StringUtils.hasText(url)) { - LOGGER.debug("No 'spring.datasource.url' provided, skip enhancing jdbc url."); + LOGGER.debug("No '{}.url' provided, skip enhancing jdbc url.", datasourcePropertiesPrefix); return bean; } @@ -84,24 +88,25 @@ public Object postProcessBeforeInitialization(Object bean, String beanName) thro if (isPasswordProvided) { LOGGER.debug( "If you are using Azure hosted services," - + "it is encouraged to use the passwordless feature. " - + "Please refer to https://aka.ms/passwordless-connections."); + + "it is encouraged to use the passwordless feature ({}). " + + "Please refer to https://aka.ms/passwordless-connections.", datasourcePropertiesPrefix); return bean; } DatabaseType databaseType = connectionString.getDatabaseType(); if (!databaseType.isDatabasePluginAvailable()) { - LOGGER.debug("The jdbc plugin with provided jdbc schema is not on the classpath, skip enhancing jdbc url."); + LOGGER.debug("The jdbc plugin with provided jdbc schema is not on the classpath, " + + "skip enhancing jdbc url ({}).", datasourcePropertiesPrefix); return bean; } try { JdbcConnectionStringEnhancer enhancer = new JdbcConnectionStringEnhancer(connectionString); - enhancer.enhanceProperties(buildEnhancedProperties(databaseType, properties), true); + enhancer.enhanceProperties(buildEnhancedProperties(passwordlessPropertiesPrefix, databaseType, properties), true); enhanceUserAgent(databaseType, enhancer); ((DataSourceProperties) bean).setUrl(enhancer.getJdbcUrl()); } catch (IllegalArgumentException e) { - LOGGER.error("Inconsistent properties detected, skip enhancing jdbc url.", e); + LOGGER.error("Inconsistent properties detected, skip enhancing jdbc url ({}).", datasourcePropertiesPrefix, e); } } return bean; @@ -134,20 +139,12 @@ private void enhanceUserAgent(DatabaseType databaseType, JdbcConnectionStringEnh } } - private Map buildEnhancedProperties(DatabaseType databaseType, AzureJdbcPasswordlessProperties properties) { + private Map buildEnhancedProperties(String passwordlessPropertiesPrefix, + DatabaseType databaseType, + AzureJdbcPasswordlessProperties properties) { Map result = new HashMap<>(); - TokenCredentialProvider tokenCredentialProvider = TokenCredentialProvider.createDefault(new TokenCredentialProviderOptions(properties.toPasswordlessProperties())); - TokenCredential tokenCredential = tokenCredentialProvider.get(); - - AuthProperty.TOKEN_CREDENTIAL_BEAN_NAME.setProperty(result, PASSWORDLESS_TOKEN_CREDENTIAL_BEAN_NAME); - applicationContext.registerBean(PASSWORDLESS_TOKEN_CREDENTIAL_BEAN_NAME, TokenCredential.class, () -> tokenCredential); - - LOGGER.debug("Add SpringTokenCredentialProvider as the default token credential provider."); - AuthProperty.TOKEN_CREDENTIAL_PROVIDER_CLASS_NAME.setProperty(result, SPRING_TOKEN_CREDENTIAL_PROVIDER_CLASS_NAME); - AuthProperty.AUTHORITY_HOST.setProperty(result, properties.getProfile().getEnvironment().getActiveDirectoryEndpoint()); - + enhancePasswordlessProperties(passwordlessPropertiesPrefix, properties, result); databaseType.setDefaultEnhancedProperties(result); - return result; } @@ -161,14 +158,13 @@ public void setApplicationContext(ApplicationContext applicationContext) throws this.applicationContext = (GenericApplicationContext) applicationContext; } - private AzureJdbcPasswordlessProperties buildAzureProperties() { + private AzureJdbcPasswordlessProperties buildAzureProperties(String azureDatasourcePrefix) { AzureGlobalProperties azureGlobalProperties = applicationContext.getBean(AzureGlobalProperties.class); AzureJdbcPasswordlessProperties azurePasswordlessProperties = Binder.get(environment) - .bindOrCreate(SPRING_CLOUD_AZURE_DATASOURCE_PREFIX, AzureJdbcPasswordlessProperties.class); + .bindOrCreate(azureDatasourcePrefix, AzureJdbcPasswordlessProperties.class); AzureJdbcPasswordlessProperties mergedProperties = new AzureJdbcPasswordlessProperties(); AzurePasswordlessPropertiesUtils.mergeAzureCommonProperties(azureGlobalProperties, azurePasswordlessProperties, mergedProperties); return mergedProperties; - } } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/jms/ServiceBusJmsConnectionFactoryConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/jms/ServiceBusJmsConnectionFactoryConfiguration.java index 4851cfeddd6f..4bb40a6f6646 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/jms/ServiceBusJmsConnectionFactoryConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/jms/ServiceBusJmsConnectionFactoryConfiguration.java @@ -23,11 +23,11 @@ @Configuration(proxyBeanMethods = false) @ConditionalOnMissingBean(ConnectionFactory.class) -class ServiceBusJmsConnectionFactoryConfiguration { +class ServiceBusJmsConnectionFactoryConfiguration { - private static ServiceBusJmsConnectionFactory createJmsConnectionFactory(AzureServiceBusJmsProperties properties, + private ServiceBusJmsConnectionFactory createJmsConnectionFactory(AzureServiceBusJmsProperties serviceBusJmsProperties, ObjectProvider factoryCustomizers) { - return new ServiceBusJmsConnectionFactoryFactory(properties, + return new ServiceBusJmsConnectionFactoryFactory(serviceBusJmsProperties, factoryCustomizers.orderedStream().collect(Collectors.toList())) .createConnectionFactory(ServiceBusJmsConnectionFactory.class); } @@ -35,7 +35,8 @@ private static ServiceBusJmsConnectionFactory createJmsConnectionFactory(AzureSe @Configuration(proxyBeanMethods = false) @ConditionalOnProperty(prefix = "spring.jms.servicebus.pool", name = "enabled", havingValue = "false", matchIfMissing = true) - static class SimpleConnectionFactoryConfiguration { + class SimpleConnectionFactoryConfiguration { + @Bean @ConditionalOnProperty(prefix = "spring.jms.cache", name = "enabled", havingValue = "false") @@ -48,7 +49,7 @@ ServiceBusJmsConnectionFactory jmsConnectionFactory(AzureServiceBusJmsProperties @ConditionalOnClass(CachingConnectionFactory.class) @ConditionalOnProperty(prefix = "spring.jms.cache", name = "enabled", havingValue = "true", matchIfMissing = true) - static class CachingConnectionFactoryConfiguration { + class CachingConnectionFactoryConfiguration { @Bean CachingConnectionFactory jmsConnectionFactory(JmsProperties jmsProperties, @@ -68,7 +69,7 @@ CachingConnectionFactory jmsConnectionFactory(JmsProperties jmsProperties, @Configuration(proxyBeanMethods = false) @ConditionalOnClass({ JmsPoolConnectionFactory.class, PooledObject.class }) - static class PooledConnectionFactoryConfiguration { + class PooledConnectionFactoryConfiguration { @Bean(destroyMethod = "stop") @ConditionalOnProperty(prefix = "spring.jms.servicebus.pool", name = "enabled", havingValue = "true") diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/jms/ServiceBusJmsConnectionFactoryFactory.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/jms/ServiceBusJmsConnectionFactoryFactory.java index e3222c6f1930..b974a55f4c07 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/jms/ServiceBusJmsConnectionFactoryFactory.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/jms/ServiceBusJmsConnectionFactoryFactory.java @@ -16,17 +16,27 @@ import java.lang.reflect.InvocationTargetException; import java.util.Collections; import java.util.List; +import java.util.Properties; + +import static com.azure.spring.cloud.autoconfigure.implementation.util.SpringPasswordlessPropertiesUtils.enhancePasswordlessProperties; class ServiceBusJmsConnectionFactoryFactory { private final AzureServiceBusJmsProperties properties; private final List factoryCustomizers; + private final TokenCredentialProvider tokenCredentialProvider; ServiceBusJmsConnectionFactoryFactory(AzureServiceBusJmsProperties properties, List factoryCustomizers) { Assert.notNull(properties, "Properties must not be null"); this.properties = properties; this.factoryCustomizers = (factoryCustomizers != null) ? factoryCustomizers : Collections.emptyList(); - + if (properties.isPasswordlessEnabled()) { + Properties passwordlessProperties = properties.toPasswordlessProperties(); + enhancePasswordlessProperties(AzureServiceBusJmsProperties.PREFIX, properties, passwordlessProperties); + this.tokenCredentialProvider = TokenCredentialProvider.createDefault(new TokenCredentialProviderOptions(passwordlessProperties)); + } else { + this.tokenCredentialProvider = null; + } } T createConnectionFactory(Class factoryClass) { @@ -61,8 +71,6 @@ private T createConnectionFactoryInst if (properties.isPasswordlessEnabled()) { String hostName = properties.getNamespace() + "." + properties.getProfile().getEnvironment().getServiceBusDomainName(); - TokenCredentialProvider tokenCredentialProvider = - TokenCredentialProvider.createDefault(new TokenCredentialProviderOptions(properties.toPasswordlessProperties())); TokenCredential tokenCredential = tokenCredentialProvider.get(); factory = factoryClass.getConstructor(TokenCredential.class, String.class, ServiceBusJmsConnectionFactorySettings.class) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/jms/properties/AzureServiceBusJmsProperties.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/jms/properties/AzureServiceBusJmsProperties.java index 427309429027..807abd3f67ba 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/jms/properties/AzureServiceBusJmsProperties.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/jms/properties/AzureServiceBusJmsProperties.java @@ -194,7 +194,7 @@ public void afterPropertiesSet() throws Exception { } } else { if (!StringUtils.hasText(connectionString)) { - throw new IllegalArgumentException("'spring.jms.servicebus.connection-string' should be provided."); + throw new IllegalArgumentException("'spring.jms.servicebus.connection-string' should be provided, otherwise you should provide a bean 'StaticConnectionStringProvider'."); } } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/properties/core/authentication/TokenCredentialConfigurationProperties.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/properties/core/authentication/TokenCredentialConfigurationProperties.java index b2e6f6062f51..98000130686c 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/properties/core/authentication/TokenCredentialConfigurationProperties.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/properties/core/authentication/TokenCredentialConfigurationProperties.java @@ -47,6 +47,11 @@ public class TokenCredentialConfigurationProperties implements TokenCredentialOp */ private boolean managedIdentityEnabled = false; + /** + * Custom Get the custom {@link com.azure.core.credential.TokenCredential} bean name, it's used for Service builder factory or passwordless authentication. + */ + private String tokenCredentialBeanName; + @Override public String getClientId() { return clientId; @@ -106,6 +111,15 @@ public boolean isManagedIdentityEnabled() { return managedIdentityEnabled; } + @Override + public String getTokenCredentialBeanName() { + return tokenCredentialBeanName; + } + + public void setTokenCredentialBeanName(String tokenCredentialBeanName) { + this.tokenCredentialBeanName = tokenCredentialBeanName; + } + public void setManagedIdentityEnabled(boolean managedIdentityEnabled) { this.managedIdentityEnabled = managedIdentityEnabled; } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/resourcemanager/AzureResourceManagerAutoConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/resourcemanager/AzureResourceManagerAutoConfiguration.java index c2ee35deaa07..aa565da8994c 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/resourcemanager/AzureResourceManagerAutoConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/resourcemanager/AzureResourceManagerAutoConfiguration.java @@ -7,13 +7,18 @@ import com.azure.core.management.profile.AzureProfile; import com.azure.resourcemanager.AzureResourceManager; import com.azure.spring.cloud.autoconfigure.implementation.context.properties.AzureGlobalProperties; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.util.StringUtils; + +import static com.azure.spring.cloud.autoconfigure.implementation.context.AzureContextUtils.DEFAULT_TOKEN_CREDENTIAL_BEAN_NAME; /** @@ -35,9 +40,16 @@ public class AzureResourceManagerAutoConfiguration { @Bean @ConditionalOnMissingBean - AzureResourceManager azureResourceManager(TokenCredential tokenCredential, AzureProfile azureProfile) { + AzureResourceManager azureResourceManager(ApplicationContext applicationContext, + @Qualifier(DEFAULT_TOKEN_CREDENTIAL_BEAN_NAME) TokenCredential defaultTokenCredential, + AzureProfile azureProfile) { // TODO (xiada) Do we need to pass our User-Agent to with the management sdk? // TODO (xiada) configure the http client of arm client + TokenCredential tokenCredential = defaultTokenCredential; + String tokenCredentialBeanName = this.globalProperties.getCredential().getTokenCredentialBeanName(); + if (StringUtils.hasText(tokenCredentialBeanName)) { + tokenCredential = (TokenCredential) applicationContext.getBean(tokenCredentialBeanName); + } return AzureResourceManager.configure().authenticate(tokenCredential, azureProfile).withDefaultSubscription(); } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusMessagingAutoConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusMessagingAutoConfiguration.java index 9f49827d8859..43c1be1b0ac2 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusMessagingAutoConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusMessagingAutoConfiguration.java @@ -3,8 +3,12 @@ package com.azure.spring.cloud.autoconfigure.implementation.servicebus; +import com.azure.core.credential.TokenCredential; +import com.azure.messaging.servicebus.ServiceBusClientBuilder; import com.azure.spring.cloud.autoconfigure.implementation.condition.ConditionalOnAnyProperty; import com.azure.spring.cloud.autoconfigure.implementation.servicebus.properties.AzureServiceBusProperties; +import com.azure.spring.cloud.core.customizer.AzureServiceClientBuilderCustomizer; +import com.azure.spring.cloud.core.implementation.credential.resolver.AzureTokenCredentialResolver; import com.azure.spring.cloud.core.provider.connectionstring.ServiceConnectionStringProvider; import com.azure.spring.cloud.core.service.AzureServiceType; import com.azure.spring.messaging.ConsumerIdentifier; @@ -80,8 +84,19 @@ static class ProcessorContainerConfiguration { @ConditionalOnMissingBean ServiceBusProcessorFactory defaultServiceBusNamespaceProcessorFactory( NamespaceProperties properties, - ObjectProvider> suppliers) { - return new DefaultServiceBusNamespaceProcessorFactory(properties, suppliers.getIfAvailable()); + ObjectProvider> suppliers, + ObjectProvider tokenCredentialResolvers, + ObjectProvider defaultTokenCredentials, + ObjectProvider> clientBuilderCustomizers, + ObjectProvider> processorClientBuilderCustomizers, + ObjectProvider> sessionProcessorClientBuilderCustomizers) { + DefaultServiceBusNamespaceProcessorFactory factory = new DefaultServiceBusNamespaceProcessorFactory(properties, suppliers.getIfAvailable()); + factory.setDefaultCredential(defaultTokenCredentials.getIfAvailable()); + factory.setTokenCredentialResolver(tokenCredentialResolvers.getIfAvailable()); + clientBuilderCustomizers.orderedStream().forEach(factory::addServiceBusClientBuilderCustomizer); + processorClientBuilderCustomizers.orderedStream().forEach(factory::addBuilderCustomizer); + sessionProcessorClientBuilderCustomizers.orderedStream().forEach(factory::addSessionBuilderCustomizer); + return factory; } } @@ -92,8 +107,17 @@ static class ServiceBusTemplateConfiguration { @ConditionalOnMissingBean ServiceBusProducerFactory defaultServiceBusNamespaceProducerFactory( NamespaceProperties properties, - ObjectProvider> suppliers) { - return new DefaultServiceBusNamespaceProducerFactory(properties, suppliers.getIfAvailable()); + ObjectProvider> suppliers, + ObjectProvider tokenCredentialResolvers, + ObjectProvider defaultTokenCredentials, + ObjectProvider> clientBuilderCustomizers, + ObjectProvider> senderClientBuilderCustomizers) { + DefaultServiceBusNamespaceProducerFactory factory = new DefaultServiceBusNamespaceProducerFactory(properties, suppliers.getIfAvailable()); + factory.setDefaultCredential(defaultTokenCredentials.getIfAvailable()); + factory.setTokenCredentialResolver(tokenCredentialResolvers.getIfAvailable()); + clientBuilderCustomizers.orderedStream().forEach(factory::addServiceBusClientBuilderCustomizer); + senderClientBuilderCustomizers.orderedStream().forEach(factory::addBuilderCustomizer); + return factory; } @Bean diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/util/SpringPasswordlessPropertiesUtils.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/util/SpringPasswordlessPropertiesUtils.java new file mode 100644 index 000000000000..048534883a58 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/util/SpringPasswordlessPropertiesUtils.java @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.cloud.autoconfigure.implementation.util; + +import com.azure.identity.extensions.implementation.enums.AuthProperty; +import com.azure.spring.cloud.core.properties.PasswordlessProperties; +import com.azure.spring.cloud.service.implementation.identity.credential.provider.SpringTokenCredentialProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.StringUtils; + +import java.util.Map; +import java.util.Properties; + +/** + * Util class for passwordless properties enhancement. + */ +public final class SpringPasswordlessPropertiesUtils { + + private static final Logger LOGGER = LoggerFactory.getLogger(SpringPasswordlessPropertiesUtils.class); + + private SpringPasswordlessPropertiesUtils() { + + } + + /** + * Enhance the {@link PasswordlessProperties} implementation into the {@link Properties}. + * @param passwordlessPropertiesPrefix the prefix for the {@link PasswordlessProperties}. + * @param passwordlessProperties the {@link PasswordlessProperties} implementation. + * @param properties the {@link Properties} {@link Properties}. + */ + public static void enhancePasswordlessProperties(String passwordlessPropertiesPrefix, + PasswordlessProperties passwordlessProperties, + Properties properties) { + if (!passwordlessProperties.isPasswordlessEnabled()) { + LOGGER.debug("Feature passwordless authentication is not enabled({}.passwordless-enabled=false), " + + "skip enhancing properties.", passwordlessPropertiesPrefix); + return; + } + + String tokenCredentialBeanName = passwordlessProperties.getCredential().getTokenCredentialBeanName(); + if (StringUtils.hasText(tokenCredentialBeanName)) { + AuthProperty.TOKEN_CREDENTIAL_BEAN_NAME.setProperty(properties, tokenCredentialBeanName); + AuthProperty.TOKEN_CREDENTIAL_PROVIDER_CLASS_NAME.setProperty(properties, SpringTokenCredentialProvider.class.getName()); + } + } + + /** + * Enhance the {@link PasswordlessProperties} implementation into the Map. + * @param passwordlessPropertiesPrefix the prefix for the {@link PasswordlessProperties}. + * @param passwordlessProperties the {@link PasswordlessProperties} implementation. + * @param result the Map. + */ + public static void enhancePasswordlessProperties(String passwordlessPropertiesPrefix, + PasswordlessProperties passwordlessProperties, + Map result) { + Properties properties = passwordlessProperties.toPasswordlessProperties(); + result.forEach(properties::setProperty); + enhancePasswordlessProperties(passwordlessPropertiesPrefix, passwordlessProperties, properties); + properties.forEach((key, value) -> result.put((String) key, (String) value)); + } +} diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports index c23b97895fc2..12741d703d32 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -5,6 +5,7 @@ com.azure.spring.cloud.autoconfigure.implementation.aadb2c.configuration.AadB2cR com.azure.spring.cloud.autoconfigure.implementation.redis.AzureRedisAutoConfiguration com.azure.spring.cloud.autoconfigure.implementation.context.AzureGlobalPropertiesAutoConfiguration com.azure.spring.cloud.autoconfigure.implementation.context.AzureTokenCredentialAutoConfiguration +com.azure.spring.cloud.autoconfigure.implementation.context.SpringTokenCredentialProviderContextProviderAutoConfiguration com.azure.spring.cloud.autoconfigure.implementation.appconfiguration.AzureAppConfigurationAutoConfiguration com.azure.spring.cloud.autoconfigure.implementation.cosmos.AzureCosmosAutoConfiguration com.azure.spring.cloud.autoconfigure.implementation.eventhubs.AzureEventHubsAutoConfiguration diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/AbstractAzureServiceConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/AbstractAzureServiceConfigurationTests.java index 9285faecaf06..0a986f307174 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/AbstractAzureServiceConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/AbstractAzureServiceConfigurationTests.java @@ -11,6 +11,7 @@ import com.azure.identity.implementation.IdentityClient; import com.azure.spring.cloud.autoconfigure.implementation.context.AzureGlobalPropertiesAutoConfiguration; import com.azure.spring.cloud.autoconfigure.implementation.context.AzureTokenCredentialAutoConfiguration; +import com.azure.spring.cloud.autoconfigure.implementation.context.TestSpringTokenCredentialProviderContextProviderAutoConfiguration; import com.azure.spring.cloud.core.implementation.credential.resolver.AzureTokenCredentialResolver; import com.azure.spring.cloud.core.implementation.factory.AbstractAzureServiceClientBuilderFactory; import com.azure.spring.cloud.core.properties.AzureProperties; @@ -44,6 +45,7 @@ protected void usGovCloudShouldWorkWithClientSecretCredential() { getPropertyPrefix() + ".credential.client-secret=fake-client-secret" ) .withConfiguration(AutoConfigurations.of( + TestSpringTokenCredentialProviderContextProviderAutoConfiguration.class, AzureTokenCredentialAutoConfiguration.class, AzureGlobalPropertiesAutoConfiguration.class )) @@ -62,6 +64,7 @@ protected void usGovCloudShouldWorkWithClientCertificateCredential() { getPropertyPrefix() + ".credential.client-certificate-path=fake-client-cert-path" ) .withConfiguration(AutoConfigurations.of( + TestSpringTokenCredentialProviderContextProviderAutoConfiguration.class, AzureTokenCredentialAutoConfiguration.class, AzureGlobalPropertiesAutoConfiguration.class )) @@ -81,6 +84,7 @@ protected void usGovCloudShouldWorkWithUsernamePasswordCredential() { ) .withConfiguration(AutoConfigurations.of( AzureTokenCredentialAutoConfiguration.class, + TestSpringTokenCredentialProviderContextProviderAutoConfiguration.class, AzureGlobalPropertiesAutoConfiguration.class )) .run(context -> { diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/WebApplicationContextRunnerUtils.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/WebApplicationContextRunnerUtils.java index a619816dc9e1..89dbb10767a6 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/WebApplicationContextRunnerUtils.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/WebApplicationContextRunnerUtils.java @@ -5,6 +5,7 @@ import com.azure.spring.cloud.autoconfigure.implementation.aad.configuration.AadAutoConfiguration; import com.azure.spring.cloud.autoconfigure.implementation.context.AzureGlobalPropertiesAutoConfiguration; +import com.azure.spring.cloud.autoconfigure.implementation.context.TestSpringTokenCredentialProviderContextProviderAutoConfiguration; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration; import org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener; @@ -26,7 +27,10 @@ public static WebApplicationContextRunner oauthClientAndResourceServerRunner() { .withConfiguration(AutoConfigurations.of( HttpMessageConvertersAutoConfiguration.class, RestTemplateAutoConfiguration.class)) - .withUserConfiguration(AzureGlobalPropertiesAutoConfiguration.class, AadAutoConfiguration.class) + .withUserConfiguration( + TestSpringTokenCredentialProviderContextProviderAutoConfiguration.class, + AzureGlobalPropertiesAutoConfiguration.class, + AadAutoConfiguration.class) .withInitializer(new ConditionEvaluationReportLoggingListener()); } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfigurationTests.java index dd38d11eb2a6..ad29f3055201 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadResourceServerConfigurationTests.java @@ -2,6 +2,7 @@ // Licensed under the MIT License. package com.azure.spring.cloud.autoconfigure.implementation.aad.configuration; +import com.azure.identity.extensions.implementation.template.AzureAuthenticationTemplate; import com.azure.spring.cloud.autoconfigure.implementation.aad.configuration.properties.AadAuthenticationProperties; import com.azure.spring.cloud.autoconfigure.implementation.aad.security.AadResourceServerHttpSecurityConfigurer; import com.azure.spring.cloud.autoconfigure.implementation.context.AzureGlobalPropertiesAutoConfiguration; @@ -120,7 +121,7 @@ void useCustomJwtGrantedAuthoritiesConverter() { TestAadResourceServerConfiguration.class, AadAutoConfiguration.class) .withPropertyValues(withResourceServerPropertyValues()) - .withClassLoader(new FilteredClassLoader(ClientRegistration.class)) + .withClassLoader(new FilteredClassLoader(AzureAuthenticationTemplate.class, ClientRegistration.class)) .run(context -> { SecurityFilterChain filterChain = context.getBean(SecurityFilterChain.class); assertThat(filterChain).isNotNull(); @@ -170,7 +171,7 @@ void useCustomSecurityFilterChain() { TestAadResourceServerConfiguration.class, AadAutoConfiguration.class) .withInitializer(ConditionEvaluationReportLoggingListener.forLogLevel(LogLevel.INFO)) - .withClassLoader(new FilteredClassLoader(ClientRegistration.class)) + .withClassLoader(new FilteredClassLoader(AzureAuthenticationTemplate.class, ClientRegistration.class)) .withPropertyValues("spring.cloud.azure.active-directory.enabled=true", "spring.cloud.azure.active-directory.credential.client-id=fake-client-id" ) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadWebApplicationConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadWebApplicationConfigurationTests.java index 014ee8d96b6b..9bc6f51883cb 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadWebApplicationConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/configuration/AadWebApplicationConfigurationTests.java @@ -4,6 +4,7 @@ package com.azure.spring.cloud.autoconfigure.implementation.aad.configuration; import com.azure.spring.cloud.autoconfigure.implementation.context.AzureGlobalPropertiesAutoConfiguration; +import com.azure.spring.cloud.autoconfigure.implementation.context.TestSpringTokenCredentialProviderContextProviderAutoConfiguration; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration; @@ -39,7 +40,8 @@ void useCustomSecurityFilterChain() { .withConfiguration(AutoConfigurations.of( HttpMessageConvertersAutoConfiguration.class, RestTemplateAutoConfiguration.class)) - .withUserConfiguration(AzureGlobalPropertiesAutoConfiguration.class, + .withUserConfiguration(TestSpringTokenCredentialProviderContextProviderAutoConfiguration.class, + AzureGlobalPropertiesAutoConfiguration.class, TestSecurityFilterChain.class, AadAutoConfiguration.class) .withInitializer(ConditionEvaluationReportLoggingListener.forLogLevel(LogLevel.INFO)) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/filter/AadAuthenticationFilterPropertiesTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/filter/AadAuthenticationFilterPropertiesTests.java index cd8143d15a16..0f569fa6b33f 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/filter/AadAuthenticationFilterPropertiesTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/filter/AadAuthenticationFilterPropertiesTests.java @@ -5,6 +5,7 @@ import com.azure.spring.cloud.autoconfigure.implementation.aad.configuration.AzureAuthenticationFilterTestConfiguration; import com.azure.spring.cloud.autoconfigure.implementation.aad.configuration.properties.AadAuthenticationProperties; +import com.azure.spring.cloud.autoconfigure.implementation.context.TestSpringTokenCredentialProviderContextProviderAutoConfiguration; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; @@ -30,7 +31,8 @@ public class AadAuthenticationFilterPropertiesTests { public void canSetProperties() { try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) { configureAllRequiredProperties(context); - context.register(AzureAuthenticationFilterTestConfiguration.class); + context.register(TestSpringTokenCredentialProviderContextProviderAutoConfiguration.class, + AzureAuthenticationFilterTestConfiguration.class); context.refresh(); final AadAuthenticationProperties properties = context.getBean(AadAuthenticationProperties.class); diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/filter/ResourceRetrieverTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/filter/ResourceRetrieverTests.java index 520e56128bb1..0f6f0d4bf1b4 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/filter/ResourceRetrieverTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aad/filter/ResourceRetrieverTests.java @@ -6,6 +6,7 @@ import com.azure.spring.cloud.autoconfigure.implementation.aad.configuration.AadAuthenticationFilterAutoConfiguration; import com.azure.spring.cloud.autoconfigure.implementation.aad.security.jose.RestOperationsResourceRetriever; import com.azure.spring.cloud.autoconfigure.implementation.context.AzureGlobalPropertiesAutoConfiguration; +import com.azure.spring.cloud.autoconfigure.implementation.context.TestSpringTokenCredentialProviderContextProviderAutoConfiguration; import com.nimbusds.jose.util.ResourceRetriever; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; @@ -20,10 +21,11 @@ class ResourceRetrieverTests { private final WebApplicationContextRunner contextRunner = new WebApplicationContextRunner() .withConfiguration(AutoConfigurations.of( - AzureGlobalPropertiesAutoConfiguration.class, - AadAuthenticationFilterAutoConfiguration.class, - HttpMessageConvertersAutoConfiguration.class, - RestTemplateAutoConfiguration.class)) + TestSpringTokenCredentialProviderContextProviderAutoConfiguration.class, + AzureGlobalPropertiesAutoConfiguration.class, + AadAuthenticationFilterAutoConfiguration.class, + HttpMessageConvertersAutoConfiguration.class, + RestTemplateAutoConfiguration.class)) .withClassLoader(new FilteredClassLoader(BearerTokenAuthenticationToken.class)) .withPropertyValues( "spring.cloud.azure.active-directory.enabled=true", diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aadb2c/configuration/AadB2cAutoConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aadb2c/configuration/AadB2cAutoConfigurationTests.java index 563c0295ff00..10d4249480c4 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aadb2c/configuration/AadB2cAutoConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aadb2c/configuration/AadB2cAutoConfigurationTests.java @@ -12,6 +12,7 @@ import com.azure.spring.cloud.autoconfigure.implementation.aadb2c.security.AadB2cLogoutSuccessHandler; import com.azure.spring.cloud.autoconfigure.implementation.aadb2c.security.AadB2cOidcLoginConfigurer; import com.azure.spring.cloud.autoconfigure.implementation.context.AzureGlobalPropertiesAutoConfiguration; +import com.azure.spring.cloud.autoconfigure.implementation.context.TestSpringTokenCredentialProviderContextProviderAutoConfiguration; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import org.mockito.MockedStatic; @@ -78,11 +79,12 @@ void mapPropertiesSetting() { WebApplicationContextRunner getDefaultContextRunner() { return new WebApplicationContextRunner() .withConfiguration(AutoConfigurations.of( - AzureGlobalPropertiesAutoConfiguration.class, - WebOAuth2ClientTestApp.class, - AadB2cAutoConfiguration.class, - HttpMessageConvertersAutoConfiguration.class, - RestTemplateAutoConfiguration.class)) + TestSpringTokenCredentialProviderContextProviderAutoConfiguration.class, + AzureGlobalPropertiesAutoConfiguration.class, + WebOAuth2ClientTestApp.class, + AadB2cAutoConfiguration.class, + HttpMessageConvertersAutoConfiguration.class, + RestTemplateAutoConfiguration.class)) .withClassLoader(new FilteredClassLoader(BearerTokenAuthenticationToken.class)) .withPropertyValues(getWebappCommonPropertyValuesWithOutGlobalConfigurableItems()) .withPropertyValues(getGlobalConfigurableItems()); @@ -165,11 +167,12 @@ void testPropertiesBean() { void setDefaultValueFromAzureGlobalPropertiesTest() { new WebApplicationContextRunner() .withConfiguration(AutoConfigurations.of( - AzureGlobalPropertiesAutoConfiguration.class, - WebOAuth2ClientTestApp.class, - AadB2cAutoConfiguration.class, - HttpMessageConvertersAutoConfiguration.class, - RestTemplateAutoConfiguration.class)) + TestSpringTokenCredentialProviderContextProviderAutoConfiguration.class, + AzureGlobalPropertiesAutoConfiguration.class, + WebOAuth2ClientTestApp.class, + AadB2cAutoConfiguration.class, + HttpMessageConvertersAutoConfiguration.class, + RestTemplateAutoConfiguration.class)) .withClassLoader(new FilteredClassLoader(BearerTokenAuthenticationToken.class)) .withPropertyValues(getWebappCommonPropertyValuesWithOutGlobalConfigurableItems()) .withPropertyValues( @@ -189,11 +192,12 @@ void setDefaultValueFromAzureGlobalPropertiesTest() { }); new WebApplicationContextRunner() .withConfiguration(AutoConfigurations.of( - AzureGlobalPropertiesAutoConfiguration.class, - WebOAuth2ClientTestApp.class, - AadB2cAutoConfiguration.class, - HttpMessageConvertersAutoConfiguration.class, - RestTemplateAutoConfiguration.class)) + TestSpringTokenCredentialProviderContextProviderAutoConfiguration.class, + AzureGlobalPropertiesAutoConfiguration.class, + WebOAuth2ClientTestApp.class, + AadB2cAutoConfiguration.class, + HttpMessageConvertersAutoConfiguration.class, + RestTemplateAutoConfiguration.class)) .withClassLoader(new FilteredClassLoader(BearerTokenAuthenticationToken.class)) .withPropertyValues(getWebappCommonPropertyValuesWithOutGlobalConfigurableItems()) .withPropertyValues( diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aadb2c/configuration/AadB2cResourceServerAutoConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aadb2c/configuration/AadB2cResourceServerAutoConfigurationTests.java index c5374bf2133c..4bda51037b0d 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aadb2c/configuration/AadB2cResourceServerAutoConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aadb2c/configuration/AadB2cResourceServerAutoConfigurationTests.java @@ -9,6 +9,7 @@ import com.azure.spring.cloud.autoconfigure.implementation.aadb2c.configuration.properties.AadB2cProperties; import com.azure.spring.cloud.autoconfigure.implementation.aadb2c.security.jwt.AadB2cTrustedIssuerRepository; import com.azure.spring.cloud.autoconfigure.implementation.context.AzureGlobalPropertiesAutoConfiguration; +import com.azure.spring.cloud.autoconfigure.implementation.context.TestSpringTokenCredentialProviderContextProviderAutoConfiguration; import com.nimbusds.jose.proc.SecurityContext; import com.nimbusds.jwt.proc.DefaultJWTProcessor; import com.nimbusds.jwt.proc.JWTClaimsSetAwareJWSKeySelector; @@ -44,6 +45,7 @@ private WebApplicationContextRunner getResourceServerContextRunner() { return new WebApplicationContextRunner() .withClassLoader(new FilteredClassLoader(OAuth2LoginAuthenticationFilter.class)) .withConfiguration(AutoConfigurations.of( + TestSpringTokenCredentialProviderContextProviderAutoConfiguration.class, AzureGlobalPropertiesAutoConfiguration.class, WebResourceServerTestApp.class, AadB2cResourceServerAutoConfiguration.class, @@ -57,6 +59,7 @@ WebApplicationContextRunner getDefaultContextRunner() { return new WebApplicationContextRunner() .withConfiguration(AutoConfigurations.of( WebOAuth2ClientTestApp.class, + TestSpringTokenCredentialProviderContextProviderAutoConfiguration.class, AzureGlobalPropertiesAutoConfiguration.class, AadB2cResourceServerAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class, diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aadb2c/security/AadB2cAuthorizationRequestResolverTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aadb2c/security/AadB2cAuthorizationRequestResolverTests.java index 7f026ec54a90..1ce7ecc0c8f4 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aadb2c/security/AadB2cAuthorizationRequestResolverTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/aadb2c/security/AadB2cAuthorizationRequestResolverTests.java @@ -6,6 +6,7 @@ import com.azure.spring.cloud.autoconfigure.implementation.aadb2c.configuration.AadB2cAutoConfiguration; import com.azure.spring.cloud.autoconfigure.implementation.aadb2c.configuration.WebOAuth2ClientTestApp; import com.azure.spring.cloud.autoconfigure.implementation.context.AzureGlobalPropertiesAutoConfiguration; +import com.azure.spring.cloud.autoconfigure.implementation.context.TestSpringTokenCredentialProviderContextProviderAutoConfiguration; import jakarta.servlet.http.HttpServletRequest; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -28,6 +29,7 @@ private WebApplicationContextRunner getContextRunner() { return new WebApplicationContextRunner() .withClassLoader(new FilteredClassLoader(BearerTokenAuthenticationToken.class)) .withConfiguration(AutoConfigurations.of( + TestSpringTokenCredentialProviderContextProviderAutoConfiguration.class, AzureGlobalPropertiesAutoConfiguration.class, WebOAuth2ClientTestApp.class, AadB2cAutoConfiguration.class, diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/appconfiguration/AzureAppConfigurationAutoConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/appconfiguration/AzureAppConfigurationAutoConfigurationTests.java index b8b2852678a9..e412191e27ce 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/appconfiguration/AzureAppConfigurationAutoConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/appconfiguration/AzureAppConfigurationAutoConfigurationTests.java @@ -9,6 +9,7 @@ import com.azure.messaging.eventhubs.EventHubClientBuilder; import com.azure.spring.cloud.autoconfigure.implementation.AbstractAzureServiceConfigurationTests; import com.azure.spring.cloud.autoconfigure.implementation.TestBuilderCustomizer; +import com.azure.spring.cloud.autoconfigure.implementation.context.TestSpringTokenCredentialProviderContextProviderAutoConfiguration; import com.azure.spring.cloud.autoconfigure.implementation.context.properties.AzureGlobalProperties; import com.azure.spring.cloud.autoconfigure.implementation.keyvault.secrets.AzureKeyVaultSecretAutoConfiguration; import com.azure.spring.cloud.service.implementation.appconfiguration.ConfigurationClientBuilderFactory; @@ -28,7 +29,8 @@ class AzureAppConfigurationAutoConfigurationTests extends AbstractAzureServiceCo private static final String ENDPOINT = "https://%s.azconfig.io"; private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(AzureAppConfigurationAutoConfiguration.class)); + .withConfiguration(AutoConfigurations.of(TestSpringTokenCredentialProviderContextProviderAutoConfiguration.class, + AzureAppConfigurationAutoConfiguration.class)); @Override protected ApplicationContextRunner getMinimalContextRunner() { diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/context/AzureGlobalPropertiesAutoConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/context/AzureGlobalPropertiesAutoConfigurationTests.java index 91671ef38db7..c9ff6181e7e6 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/context/AzureGlobalPropertiesAutoConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/context/AzureGlobalPropertiesAutoConfigurationTests.java @@ -30,7 +30,8 @@ class AzureGlobalPropertiesAutoConfigurationTests { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(AzureGlobalPropertiesAutoConfiguration.class)); + .withConfiguration(AutoConfigurations.of(TestSpringTokenCredentialProviderContextProviderAutoConfiguration.class, + AzureGlobalPropertiesAutoConfiguration.class)); @Test void testAutoConfiguration() { diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/context/TestSpringTokenCredentialProviderContextProviderAutoConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/context/TestSpringTokenCredentialProviderContextProviderAutoConfiguration.java new file mode 100644 index 000000000000..9724083b8510 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/context/TestSpringTokenCredentialProviderContextProviderAutoConfiguration.java @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.cloud.autoconfigure.implementation.context; + +import com.azure.identity.extensions.implementation.template.AzureAuthenticationTemplate; +import com.azure.spring.cloud.autoconfigure.implementation.jdbc.SpringTokenCredentialProviderContextProvider; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration(proxyBeanMethods = false) +@ConditionalOnClass(AzureAuthenticationTemplate.class) +public class TestSpringTokenCredentialProviderContextProviderAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + SpringTokenCredentialProviderContextProvider springTokenCredentialProviderContextProvider() { + return new SpringTokenCredentialProviderContextProvider(); + } +} diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/cosmos/AzureCosmosAutoConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/cosmos/AzureCosmosAutoConfigurationTests.java index 52a9872241d7..2a7ec15e2b65 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/cosmos/AzureCosmosAutoConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/cosmos/AzureCosmosAutoConfigurationTests.java @@ -12,6 +12,7 @@ import com.azure.spring.cloud.autoconfigure.implementation.AbstractAzureServiceConfigurationTests; import com.azure.spring.cloud.autoconfigure.implementation.TestBuilderCustomizer; import com.azure.spring.cloud.autoconfigure.implementation.context.AzureGlobalPropertiesAutoConfiguration; +import com.azure.spring.cloud.autoconfigure.implementation.context.TestSpringTokenCredentialProviderContextProviderAutoConfiguration; import com.azure.spring.cloud.autoconfigure.implementation.cosmos.properties.AzureCosmosConnectionDetails; import com.azure.spring.cloud.autoconfigure.implementation.cosmos.properties.AzureCosmosProperties; import com.azure.spring.cloud.service.implementation.cosmos.CosmosClientBuilderFactory; @@ -35,6 +36,7 @@ class AzureCosmosAutoConfigurationTests extends AbstractAzureServiceConfiguratio static final String TEST_ENDPOINT_HTTPS = "https://test.https.documents.azure.com:443/"; private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of(AzureCosmosAutoConfiguration.class, + TestSpringTokenCredentialProviderContextProviderAutoConfiguration.class, AzureGlobalPropertiesAutoConfiguration.class)); @Override diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/data/cosmos/CosmosDataAutoConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/data/cosmos/CosmosDataAutoConfigurationTests.java index edb2cc9f9e21..09723946128d 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/data/cosmos/CosmosDataAutoConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/data/cosmos/CosmosDataAutoConfigurationTests.java @@ -6,6 +6,7 @@ import com.azure.cosmos.ConnectionMode; import com.azure.cosmos.CosmosClientBuilder; import com.azure.spring.cloud.autoconfigure.implementation.context.AzureGlobalPropertiesAutoConfiguration; +import com.azure.spring.cloud.autoconfigure.implementation.context.TestSpringTokenCredentialProviderContextProviderAutoConfiguration; import com.azure.spring.cloud.autoconfigure.implementation.cosmos.properties.AzureCosmosConnectionDetails; import com.azure.spring.data.cosmos.CosmosFactory; import com.azure.spring.data.cosmos.config.CosmosConfig; @@ -31,6 +32,7 @@ class CosmosDataAutoConfigurationTests { private static final String ENDPOINT = "https://test.documents.azure.com:443/"; private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of(CosmosDataAutoConfiguration.class, + TestSpringTokenCredentialProviderContextProviderAutoConfiguration.class, AzureGlobalPropertiesAutoConfiguration.class)); @Test diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/data/redis/AzureLettucePasswordlessAutoConfigurationTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/data/redis/AzureLettucePasswordlessAutoConfigurationTest.java index a0dc607e282c..d38784899ccf 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/data/redis/AzureLettucePasswordlessAutoConfigurationTest.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/data/redis/AzureLettucePasswordlessAutoConfigurationTest.java @@ -5,6 +5,7 @@ import com.azure.spring.cloud.autoconfigure.implementation.context.properties.AzureGlobalProperties; import com.azure.spring.cloud.autoconfigure.implementation.data.redis.lettuce.AzureRedisCredentials; +import com.azure.spring.cloud.autoconfigure.implementation.jdbc.SpringTokenCredentialProviderContextProvider; import io.lettuce.core.RedisCredentials; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; @@ -18,6 +19,8 @@ class AzureLettucePasswordlessAutoConfigurationTest { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withBean(AzureGlobalProperties.class, () -> new AzureGlobalProperties()) + .withBean("springTokenCredentialProviderContextProvider", SpringTokenCredentialProviderContextProvider.class, + SpringTokenCredentialProviderContextProvider::new) .withConfiguration(AutoConfigurations.of(AzureLettucePasswordlessAutoConfiguration.class)); @Test diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/eventgrid/AzureEventGridAutoConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/eventgrid/AzureEventGridAutoConfigurationTests.java index b70200bc177c..0ffb405541e8 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/eventgrid/AzureEventGridAutoConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/eventgrid/AzureEventGridAutoConfigurationTests.java @@ -13,6 +13,7 @@ import com.azure.spring.cloud.autoconfigure.implementation.AbstractAzureServiceConfigurationTests; import com.azure.spring.cloud.autoconfigure.implementation.TestBuilderCustomizer; import com.azure.spring.cloud.autoconfigure.implementation.context.AzureGlobalPropertiesAutoConfiguration; +import com.azure.spring.cloud.autoconfigure.implementation.context.TestSpringTokenCredentialProviderContextProviderAutoConfiguration; import com.azure.spring.cloud.autoconfigure.implementation.eventgrid.properties.AzureEventGridProperties; import com.azure.spring.cloud.service.implementation.eventgrid.factory.EventGridPublisherClientBuilderFactory; import org.junit.jupiter.api.Test; @@ -30,6 +31,7 @@ class AzureEventGridAutoConfigurationTests extends AbstractAzureServiceConfigura private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of( + TestSpringTokenCredentialProviderContextProviderAutoConfiguration.class, AzureGlobalPropertiesAutoConfiguration.class, AzureEventGridAutoConfiguration.class)); diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureEventHubsAutoConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureEventHubsAutoConfigurationTests.java index 09dacc93710c..e5cd907bcef5 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureEventHubsAutoConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/AzureEventHubsAutoConfigurationTests.java @@ -5,6 +5,7 @@ import com.azure.messaging.eventhubs.EventHubClientBuilder; import com.azure.spring.cloud.autoconfigure.implementation.AbstractAzureServiceConfigurationTests; +import com.azure.spring.cloud.autoconfigure.implementation.context.TestSpringTokenCredentialProviderContextProviderAutoConfiguration; import com.azure.spring.cloud.autoconfigure.implementation.context.properties.AzureGlobalProperties; import com.azure.spring.cloud.autoconfigure.implementation.eventhubs.properties.AzureEventHubsProperties; import com.azure.spring.cloud.core.properties.profile.AzureEnvironmentProperties; @@ -31,7 +32,8 @@ class AzureEventHubsAutoConfigurationTests extends AbstractAzureServiceConfigura EventHubClientBuilderFactory, AzureEventHubsProperties> { private static final String CONNECTION_STRING = String.format(CONNECTION_STRING_FORMAT, "test-namespace"); private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(AzureEventHubsAutoConfiguration.class)); + .withConfiguration(AutoConfigurations.of(TestSpringTokenCredentialProviderContextProviderAutoConfiguration.class, + AzureEventHubsAutoConfiguration.class)); @Override protected ApplicationContextRunner getMinimalContextRunner() { diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/kafka/AzureEventHubsKafkaConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/kafka/AzureEventHubsKafkaConfigurationTests.java index fdc30c5da879..49cb97b41304 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/kafka/AzureEventHubsKafkaConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/eventhubs/kafka/AzureEventHubsKafkaConfigurationTests.java @@ -4,6 +4,7 @@ import com.azure.spring.cloud.autoconfigure.implementation.context.AzureGlobalPropertiesAutoConfiguration; import com.azure.spring.cloud.autoconfigure.implementation.context.AzureTokenCredentialAutoConfiguration; +import com.azure.spring.cloud.autoconfigure.implementation.context.TestSpringTokenCredentialProviderContextProviderAutoConfiguration; import com.azure.spring.cloud.autoconfigure.implementation.context.properties.AzureGlobalProperties; import com.azure.spring.cloud.autoconfigure.implementation.kafka.AzureEventHubsKafkaOAuth2AutoConfiguration; import com.azure.spring.cloud.autoconfigure.implementation.kafka.AzureKafkaSpringCloudStreamConfiguration; @@ -34,8 +35,9 @@ class AzureEventHubsKafkaConfigurationTests { @SuppressWarnings("deprecation") private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of(AzureEventHubsKafkaOAuth2AutoConfiguration.class, AzureEventHubsKafkaAutoConfiguration.class, - AzureGlobalPropertiesAutoConfiguration.class, AzureTokenCredentialAutoConfiguration.class, - KafkaAutoConfiguration.class, AzureKafkaSpringCloudStreamConfiguration.class, KafkaBinderConfiguration.class)); + AzureGlobalPropertiesAutoConfiguration.class, TestSpringTokenCredentialProviderContextProviderAutoConfiguration.class, + AzureTokenCredentialAutoConfiguration.class, + KafkaAutoConfiguration.class, AzureKafkaSpringCloudStreamConfiguration.class, KafkaBinderConfiguration.class)); @SuppressWarnings("removal") diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/jdbc/AbstractAzureJdbcAutoConfigurationTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/jdbc/AbstractAzureJdbcAutoConfigurationTest.java index 761fd6e19289..ba5a605cebd5 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/jdbc/AbstractAzureJdbcAutoConfigurationTest.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/jdbc/AbstractAzureJdbcAutoConfigurationTest.java @@ -7,6 +7,7 @@ import com.azure.identity.extensions.implementation.template.AzureAuthenticationTemplate; import com.azure.spring.cloud.autoconfigure.implementation.context.AzureGlobalPropertiesAutoConfiguration; import com.azure.spring.cloud.autoconfigure.implementation.context.AzureTokenCredentialAutoConfiguration; +import com.azure.spring.cloud.autoconfigure.implementation.context.TestSpringTokenCredentialProviderContextProviderAutoConfiguration; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; @@ -29,6 +30,7 @@ abstract class AbstractAzureJdbcAutoConfigurationTest { protected final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of(AzureJdbcAutoConfiguration.class, AzureTokenCredentialAutoConfiguration.class, + TestSpringTokenCredentialProviderContextProviderAutoConfiguration.class, AzureGlobalPropertiesAutoConfiguration.class, DataSourceAutoConfiguration.class)); diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/jdbc/JdbcPropertiesBeanPostProcessorTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/jdbc/JdbcPropertiesBeanPostProcessorTest.java index 643cd0df1cb1..b9d54f3beef1 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/jdbc/JdbcPropertiesBeanPostProcessorTest.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/jdbc/JdbcPropertiesBeanPostProcessorTest.java @@ -39,10 +39,12 @@ class JdbcPropertiesBeanPostProcessorTest { private static final String POSTGRESQL_CONNECTION_STRING = "jdbc:postgresql://host/database?enableSwitch1&property1=value1"; private static final String PASSWORD = "password"; private static final String US_AUTHORITY_HOST_STRING = AuthProperty.AUTHORITY_HOST.getPropertyKey() + "=" + "https://login.microsoftonline.us/"; - public static final String PUBLIC_TOKEN_CREDENTIAL_BEAN_NAME_STRING = AuthProperty.TOKEN_CREDENTIAL_BEAN_NAME.getPropertyKey() + "=" + "passwordlessTokenCredential"; + public static final String PUBLIC_TOKEN_CREDENTIAL_BEAN_NAME_STRING = AuthProperty.TOKEN_CREDENTIAL_BEAN_NAME.getPropertyKey() + "="; private static final String POSTGRESQL_ASSUME_MIN_SERVER_VERSION = POSTGRESQL_PROPERTY_NAME_ASSUME_MIN_SERVER_VERSION + "=" + POSTGRESQL_PROPERTY_VALUE_ASSUME_MIN_SERVER_VERSION; - + protected static final String MANAGED_IDENTITY_ENABLED_DEFAULT = "azure.managedIdentityEnabled=false"; + protected static final String SCOPES_DEFAULT = "azure.scopes=https://ossrdbms-aad.database.windows.net/.default"; + private static final String DEFAULT_PASSWORDLESS_PROPERTIES_SUFFIX = ".spring.datasource.azure"; private MockEnvironment mockEnvironment; private ApplicationContext applicationContext; @@ -103,9 +105,9 @@ void shouldPostprocessWhenSwitchOn() { String expectedJdbcUrl = enhanceJdbcUrl( DatabaseType.MYSQL, MYSQL_CONNECTION_STRING, - PUBLIC_TOKEN_CREDENTIAL_BEAN_NAME_STRING, - MYSQL_USER_AGENT, - AuthProperty.TOKEN_CREDENTIAL_PROVIDER_CLASS_NAME.getPropertyKey() + "=" + SpringTokenCredentialProvider.class.getName() + MANAGED_IDENTITY_ENABLED_DEFAULT, + SCOPES_DEFAULT, + MYSQL_USER_AGENT ); assertEquals(expectedJdbcUrl, dataSourceProperties.getUrl()); @@ -148,12 +150,12 @@ void shouldGetCloudTypeFromAzureUsGov() { this.jdbcPropertiesBeanPostProcessor.postProcessBeforeInitialization(dataSourceProperties, "dataSourceProperties"); String expectedJdbcUrl = enhanceJdbcUrl( - DatabaseType.MYSQL, - MYSQL_CONNECTION_STRING, - MYSQL_USER_AGENT, - AuthProperty.TOKEN_CREDENTIAL_PROVIDER_CLASS_NAME.getPropertyKey() + "=" + SpringTokenCredentialProvider.class.getName(), - US_AUTHORITY_HOST_STRING, - PUBLIC_TOKEN_CREDENTIAL_BEAN_NAME_STRING + DatabaseType.MYSQL, + MYSQL_CONNECTION_STRING, + MANAGED_IDENTITY_ENABLED_DEFAULT, + SCOPES_DEFAULT, + MYSQL_USER_AGENT, + US_AUTHORITY_HOST_STRING ); assertEquals(expectedJdbcUrl, dataSourceProperties.getUrl()); @@ -170,9 +172,9 @@ void mySqlUserAgentShouldConfigureIfConnectionAttributesIsEmpty() { String expectedJdbcUrl = enhanceJdbcUrl( DatabaseType.MYSQL, MYSQL_CONNECTION_STRING, - PUBLIC_TOKEN_CREDENTIAL_BEAN_NAME_STRING, - MYSQL_USER_AGENT, - AuthProperty.TOKEN_CREDENTIAL_PROVIDER_CLASS_NAME.getPropertyKey() + "=" + SpringTokenCredentialProvider.class.getName() + MANAGED_IDENTITY_ENABLED_DEFAULT, + SCOPES_DEFAULT, + MYSQL_USER_AGENT ); assertEquals(expectedJdbcUrl, dataSourceProperties.getUrl()); @@ -192,8 +194,8 @@ void mySqlUserAgentShouldConfigureIfConnectionAttributesIsNotEmpty() { String expectedJdbcUrl = enhanceJdbcUrl( DatabaseType.MYSQL, baseUrl + ",_extension_version:" + AzureSpringIdentifier.AZURE_SPRING_MYSQL_OAUTH, - AuthProperty.TOKEN_CREDENTIAL_PROVIDER_CLASS_NAME.getPropertyKey() + "=" + SpringTokenCredentialProvider.class.getName(), - PUBLIC_TOKEN_CREDENTIAL_BEAN_NAME_STRING + MANAGED_IDENTITY_ENABLED_DEFAULT, + SCOPES_DEFAULT ); assertEquals(expectedJdbcUrl, dataSourceProperties.getUrl()); @@ -213,8 +215,8 @@ void mySqlUserAgentShouldConfigureIfConnectionAttributes() { String expectedJdbcUrl = enhanceJdbcUrl( DatabaseType.MYSQL, baseUrl + ",_extension_version:" + AzureSpringIdentifier.AZURE_SPRING_MYSQL_OAUTH, - AuthProperty.TOKEN_CREDENTIAL_PROVIDER_CLASS_NAME.getPropertyKey() + "=" + SpringTokenCredentialProvider.class.getName(), - PUBLIC_TOKEN_CREDENTIAL_BEAN_NAME_STRING + MANAGED_IDENTITY_ENABLED_DEFAULT, + SCOPES_DEFAULT ); assertEquals(expectedJdbcUrl, dataSourceProperties.getUrl()); } @@ -231,9 +233,9 @@ void postgreSqlUserAgentShouldConfigureIfNonApplicationNameProvided() { String expectedJdbcUrl = enhanceJdbcUrl( DatabaseType.POSTGRESQL, baseUrl, - AuthProperty.TOKEN_CREDENTIAL_PROVIDER_CLASS_NAME.getPropertyKey() + "=" + SpringTokenCredentialProvider.class.getName(), + MANAGED_IDENTITY_ENABLED_DEFAULT, + SCOPES_DEFAULT, APPLICATION_NAME.getName() + "=" + AzureSpringIdentifier.AZURE_SPRING_POSTGRESQL_OAUTH, - PUBLIC_TOKEN_CREDENTIAL_BEAN_NAME_STRING, POSTGRESQL_ASSUME_MIN_SERVER_VERSION ); @@ -254,12 +256,47 @@ void postgreSqlUserAgentShouldNotConfigureIfApplicationNameExists() { String expectedJdbcUrl = enhanceJdbcUrl( DatabaseType.POSTGRESQL, baseUrl, - AuthProperty.TOKEN_CREDENTIAL_PROVIDER_CLASS_NAME.getPropertyKey() + "=" + SpringTokenCredentialProvider.class.getName(), - PUBLIC_TOKEN_CREDENTIAL_BEAN_NAME_STRING, + MANAGED_IDENTITY_ENABLED_DEFAULT, + SCOPES_DEFAULT, POSTGRESQL_ASSUME_MIN_SERVER_VERSION ); assertEquals(expectedJdbcUrl, dataSourceProperties.getUrl()); } + @Test + void useDefaultTokenCredential() { + DataSourceProperties dataSourceProperties = new DataSourceProperties(); + dataSourceProperties.setUrl(MYSQL_CONNECTION_STRING); + this.mockEnvironment.setProperty("spring.datasource.azure.passwordless-enabled", "true"); + this.jdbcPropertiesBeanPostProcessor.postProcessBeforeInitialization(dataSourceProperties, "dataSourceProperties"); + String expectedJdbcUrl = enhanceJdbcUrl( + DatabaseType.MYSQL, + MYSQL_CONNECTION_STRING, + MANAGED_IDENTITY_ENABLED_DEFAULT, + SCOPES_DEFAULT, + MYSQL_USER_AGENT + ); + assertEquals(expectedJdbcUrl, dataSourceProperties.getUrl()); + } + + @Test + void useTokenCredentialBeanName() { + DataSourceProperties dataSourceProperties = new DataSourceProperties(); + dataSourceProperties.setUrl(MYSQL_CONNECTION_STRING); + String tokenCredentialBeanName = "test-bean-name"; + this.mockEnvironment.setProperty("spring.datasource.azure.passwordless-enabled", "true"); + this.mockEnvironment.setProperty("spring.datasource.azure.credential.token-credential-bean-name", tokenCredentialBeanName); + this.jdbcPropertiesBeanPostProcessor.postProcessBeforeInitialization(dataSourceProperties, "dataSourceProperties"); + String expectedJdbcUrl = enhanceJdbcUrl( + DatabaseType.MYSQL, + MYSQL_CONNECTION_STRING, + MANAGED_IDENTITY_ENABLED_DEFAULT, + SCOPES_DEFAULT, + MYSQL_USER_AGENT, + AuthProperty.TOKEN_CREDENTIAL_PROVIDER_CLASS_NAME.getPropertyKey() + "=" + SpringTokenCredentialProvider.class.getName(), + PUBLIC_TOKEN_CREDENTIAL_BEAN_NAME_STRING + tokenCredentialBeanName + ); + assertEquals(expectedJdbcUrl, dataSourceProperties.getUrl()); + } } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/jdbc/JdbcPropertiesBeanPostProcessorWithApplicationContextRunnerTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/jdbc/JdbcPropertiesBeanPostProcessorWithApplicationContextRunnerTest.java index 34badc584b5f..4dbb77bd62cd 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/jdbc/JdbcPropertiesBeanPostProcessorWithApplicationContextRunnerTest.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/jdbc/JdbcPropertiesBeanPostProcessorWithApplicationContextRunnerTest.java @@ -8,7 +8,6 @@ import com.azure.spring.cloud.autoconfigure.implementation.context.AzureGlobalPropertiesAutoConfiguration; import com.azure.spring.cloud.autoconfigure.implementation.context.AzureTokenCredentialAutoConfiguration; import com.azure.spring.cloud.autoconfigure.implementation.context.properties.AzureGlobalProperties; -import com.azure.spring.cloud.service.implementation.identity.credential.provider.SpringTokenCredentialProvider; import com.azure.spring.cloud.autoconfigure.implementation.passwordless.properties.AzureJdbcPasswordlessProperties; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; @@ -19,6 +18,8 @@ import org.springframework.core.env.ConfigurableEnvironment; import static com.azure.spring.cloud.autoconfigure.implementation.jdbc.JdbcConnectionStringUtils.enhanceJdbcUrl; +import static com.azure.spring.cloud.autoconfigure.implementation.jdbc.JdbcPropertiesBeanPostProcessorTest.MANAGED_IDENTITY_ENABLED_DEFAULT; +import static com.azure.spring.cloud.autoconfigure.implementation.jdbc.JdbcPropertiesBeanPostProcessorTest.SCOPES_DEFAULT; import static com.azure.spring.cloud.autoconfigure.implementation.jdbc.MySqlAzureJdbcAutoConfigurationTest.MYSQL_USER_AGENT; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -31,6 +32,8 @@ class JdbcPropertiesBeanPostProcessorWithApplicationContextRunnerTest { public static final String PUBLIC_TOKEN_CREDENTIAL_BEAN_NAME_STRING = AuthProperty.TOKEN_CREDENTIAL_BEAN_NAME.getPropertyKey() + "=" + "passwordlessTokenCredential"; private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withBean("springTokenCredentialProviderContextProvider", SpringTokenCredentialProviderContextProvider.class, + SpringTokenCredentialProviderContextProvider::new) .withConfiguration(AutoConfigurations.of(AzureJdbcAutoConfiguration.class, DataSourceProperties.class, AzureJdbcPasswordlessProperties.class, @@ -73,10 +76,10 @@ void mySqlAuthPluginOnClassPath() { String expectedJdbcUrl = enhanceJdbcUrl( DatabaseType.MYSQL, MYSQL_CONNECTION_STRING, - PUBLIC_TOKEN_CREDENTIAL_BEAN_NAME_STRING, PUBLIC_AUTHORITY_HOST_STRING, - MYSQL_USER_AGENT, - AuthProperty.TOKEN_CREDENTIAL_PROVIDER_CLASS_NAME.getPropertyKey() + "=" + SpringTokenCredentialProvider.class.getName() + MANAGED_IDENTITY_ENABLED_DEFAULT, + SCOPES_DEFAULT, + MYSQL_USER_AGENT ); assertEquals(expectedJdbcUrl, dataSourceProperties.getUrl()); } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/jdbc/MySqlAzureJdbcAutoConfigurationTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/jdbc/MySqlAzureJdbcAutoConfigurationTest.java index a743dd418be7..80b0c216784c 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/jdbc/MySqlAzureJdbcAutoConfigurationTest.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/jdbc/MySqlAzureJdbcAutoConfigurationTest.java @@ -9,6 +9,8 @@ import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; import org.springframework.boot.test.context.FilteredClassLoader; +import static com.azure.spring.cloud.autoconfigure.implementation.jdbc.JdbcPropertiesBeanPostProcessorTest.MANAGED_IDENTITY_ENABLED_DEFAULT; +import static com.azure.spring.cloud.autoconfigure.implementation.jdbc.JdbcPropertiesBeanPostProcessorTest.SCOPES_DEFAULT; import static com.azure.spring.cloud.autoconfigure.implementation.jdbc.JdbcPropertyConstants.MYSQL_AUTH_PLUGIN_CLASS_NAME; import static com.azure.spring.cloud.autoconfigure.implementation.jdbc.JdbcPropertyConstants.MYSQL_PROPERTY_CONNECTION_ATTRIBUTES_ATTRIBUTE_EXTENSION_VERSION; import static com.azure.spring.cloud.autoconfigure.implementation.jdbc.JdbcPropertyConstants.MYSQL_PROPERTY_CONNECTION_ATTRIBUTES_KV_DELIMITER; @@ -82,10 +84,10 @@ void enhanceUrlWithDefaultCredential() { DatabaseType.MYSQL, false, connectionString, - PUBLIC_TOKEN_CREDENTIAL_BEAN_NAME_STRING, PUBLIC_AUTHORITY_HOST_STRING, - AUTHPROPERTY_TOKENCREDENTIALPROVIDERCLASSNAME_PROPERTY, - MYSQL_USER_AGENT + MYSQL_USER_AGENT, + MANAGED_IDENTITY_ENABLED_DEFAULT, + SCOPES_DEFAULT ); assertEquals(expectedUrl, dataSourceProperties.getUrl()); }); @@ -109,9 +111,12 @@ void enhanceUrlWithCustomCredential() { false, connectionString, PUBLIC_AUTHORITY_HOST_STRING, - AUTHPROPERTY_CREDENTIAL_BEAN_NAME, - AUTHPROPERTY_TOKENCREDENTIALPROVIDERCLASSNAME_PROPERTY, - MYSQL_USER_AGENT + MYSQL_USER_AGENT, + "azure.clientId=fake-clientId", + "azure.clientSecret=fake-clientSecret", + MANAGED_IDENTITY_ENABLED_DEFAULT, + SCOPES_DEFAULT, + "azure.tenantId=fake-tenantId" ); assertEquals(expectedUrl, dataSourceProperties.getUrl()); diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/jdbc/PostgreSqlAzureJdbcAutoConfigurationTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/jdbc/PostgreSqlAzureJdbcAutoConfigurationTest.java index 301faa2d8902..7ec7353b962e 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/jdbc/PostgreSqlAzureJdbcAutoConfigurationTest.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/jdbc/PostgreSqlAzureJdbcAutoConfigurationTest.java @@ -9,6 +9,8 @@ import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; import org.springframework.boot.test.context.FilteredClassLoader; +import static com.azure.spring.cloud.autoconfigure.implementation.jdbc.JdbcPropertiesBeanPostProcessorTest.MANAGED_IDENTITY_ENABLED_DEFAULT; +import static com.azure.spring.cloud.autoconfigure.implementation.jdbc.JdbcPropertiesBeanPostProcessorTest.SCOPES_DEFAULT; import static com.azure.spring.cloud.autoconfigure.implementation.jdbc.JdbcPropertyConstants.POSTGRESQL_PROPERTY_NAME_APPLICATION_NAME; import static com.azure.spring.cloud.autoconfigure.implementation.jdbc.JdbcPropertyConstants.POSTGRESQL_PROPERTY_NAME_ASSUME_MIN_SERVER_VERSION; import static com.azure.spring.cloud.autoconfigure.implementation.jdbc.JdbcPropertyConstants.POSTGRESQL_PROPERTY_NAME_AUTHENTICATION_PLUGIN_CLASSNAME; @@ -70,10 +72,10 @@ void enhanceUrlWithDefaultCredential() { DatabaseType.POSTGRESQL, false, connectionString, - PUBLIC_TOKEN_CREDENTIAL_BEAN_NAME_STRING, PUBLIC_AUTHORITY_HOST_STRING, + MANAGED_IDENTITY_ENABLED_DEFAULT, + SCOPES_DEFAULT, POSTGRESQL_USER_AGENT, - AUTHPROPERTY_TOKENCREDENTIALPROVIDERCLASSNAME_PROPERTY, POSTGRESQL_ASSUME_MIN_SERVER_VERSION ); assertEquals(expectedUrl, dataSourceProperties.getUrl()); @@ -96,10 +98,13 @@ void enhanceUrlWithCustomCredential() { false, connectionString, PUBLIC_AUTHORITY_HOST_STRING, - AUTHPROPERTY_CREDENTIAL_BEAN_NAME, - AUTHPROPERTY_TOKENCREDENTIALPROVIDERCLASSNAME_PROPERTY, POSTGRESQL_USER_AGENT, - POSTGRESQL_ASSUME_MIN_SERVER_VERSION + POSTGRESQL_ASSUME_MIN_SERVER_VERSION, + "azure.clientId=fake-clientId", + "azure.clientSecret=fake-clientSecret", + MANAGED_IDENTITY_ENABLED_DEFAULT, + SCOPES_DEFAULT, + "azure.tenantId=fake-tenantId" ); assertEquals(expectedUrl, dataSourceProperties.getUrl()); }); diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/jms/ServiceBusJmsAutoConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/jms/ServiceBusJmsAutoConfigurationTests.java index 3f5dc92485db..b4992251b0aa 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/jms/ServiceBusJmsAutoConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/jms/ServiceBusJmsAutoConfigurationTests.java @@ -4,6 +4,7 @@ package com.azure.spring.cloud.autoconfigure.implementation.jms; import com.azure.spring.cloud.autoconfigure.implementation.context.properties.AzureGlobalProperties; +import com.azure.spring.cloud.autoconfigure.implementation.jdbc.SpringTokenCredentialProviderContextProvider; import com.azure.spring.cloud.autoconfigure.implementation.jms.properties.AzureServiceBusJmsProperties; import com.azure.spring.cloud.core.provider.connectionstring.StaticConnectionStringProvider; import com.azure.spring.cloud.core.service.AzureServiceType; @@ -45,7 +46,11 @@ class ServiceBusJmsAutoConfigurationTests { new AzureServiceBusJmsPropertiesEnvironmentPostProcessor(); private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withBean(AzureGlobalProperties.class, AzureGlobalProperties::new) - .withConfiguration(AutoConfigurations.of(JmsAutoConfiguration.class, ServiceBusJmsAutoConfiguration.class)) + .withBean("springTokenCredentialProviderContextProvider", SpringTokenCredentialProviderContextProvider.class, + SpringTokenCredentialProviderContextProvider::new) + .withConfiguration(AutoConfigurations.of( + JmsAutoConfiguration.class, + ServiceBusJmsAutoConfiguration.class)) .withInitializer(context -> processor.postProcessEnvironment(context.getEnvironment(), null)); private void testQueueJmsListenerContainerFactoryWithCustomSettings(AssertableApplicationContext loaded) { diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/jms/properties/AzureServiceBusJmsPropertiesTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/jms/properties/AzureServiceBusJmsPropertiesTests.java index 334323482132..313e5e4fa505 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/jms/properties/AzureServiceBusJmsPropertiesTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/jms/properties/AzureServiceBusJmsPropertiesTests.java @@ -32,7 +32,8 @@ void connectionStringNotValid() { Exception ex = assertThrows(IllegalArgumentException.class, prop::afterPropertiesSet); - String expectedMessage = "'spring.jms.servicebus.connection-string' should be provided."; + String expectedMessage = "'spring.jms.servicebus.connection-string' should be provided, " + + "otherwise you should provide a bean 'StaticConnectionStringProvider'."; String actualMessage = ex.getMessage(); LOGGER.log(LogLevel.VERBOSE, () -> "message:" + actualMessage); assertTrue(actualMessage.contains(expectedMessage)); diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/kafka/AzureKafkaOAuth2BinderConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/kafka/AzureKafkaOAuth2BinderConfigurationTests.java index 929a0d82bdf4..645cbd7f4158 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/kafka/AzureKafkaOAuth2BinderConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/kafka/AzureKafkaOAuth2BinderConfigurationTests.java @@ -7,6 +7,7 @@ import com.azure.identity.ManagedIdentityCredential; import com.azure.spring.cloud.autoconfigure.implementation.context.AzureGlobalPropertiesAutoConfiguration; import com.azure.spring.cloud.autoconfigure.implementation.context.AzureTokenCredentialAutoConfiguration; +import com.azure.spring.cloud.autoconfigure.implementation.context.TestSpringTokenCredentialProviderContextProviderAutoConfiguration; import com.azure.spring.cloud.autoconfigure.implementation.context.properties.AzureGlobalProperties; import com.azure.spring.cloud.core.credential.AzureCredentialResolver; import com.azure.spring.cloud.service.implementation.kafka.KafkaOAuth2AuthenticateCallbackHandler; @@ -44,6 +45,7 @@ class AzureKafkaOAuth2BinderConfigurationTests extends AbstractAzureKafkaOAuth2A private final ApplicationContextRunner contextRunnerWithoutEventHubsURL = new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of(AzureEventHubsKafkaOAuth2AutoConfiguration.class, + TestSpringTokenCredentialProviderContextProviderAutoConfiguration.class, AzureGlobalPropertiesAutoConfiguration.class, AzureTokenCredentialAutoConfiguration.class, KafkaAutoConfiguration.class, AzureKafkaSpringCloudStreamConfiguration.class, KafkaBinderConfiguration.class)); diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/kafka/AzureKafkaOAuth2BootConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/kafka/AzureKafkaOAuth2BootConfigurationTests.java index 0f018403653f..17e8189087ba 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/kafka/AzureKafkaOAuth2BootConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/kafka/AzureKafkaOAuth2BootConfigurationTests.java @@ -8,6 +8,7 @@ import com.azure.identity.ManagedIdentityCredential; import com.azure.spring.cloud.autoconfigure.implementation.context.AzureGlobalPropertiesAutoConfiguration; import com.azure.spring.cloud.autoconfigure.implementation.context.AzureTokenCredentialAutoConfiguration; +import com.azure.spring.cloud.autoconfigure.implementation.context.TestSpringTokenCredentialProviderContextProviderAutoConfiguration; import com.azure.spring.cloud.autoconfigure.implementation.context.properties.AzureGlobalProperties; import com.azure.spring.cloud.core.credential.AzureCredentialResolver; import com.azure.spring.cloud.service.implementation.kafka.KafkaOAuth2AuthenticateCallbackHandler; @@ -41,6 +42,7 @@ class AzureKafkaOAuth2BootConfigurationTests extends AbstractAzureKafkaOAuth2Aut private final ApplicationContextRunner contextRunnerWithoutEventHubsURL = new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of(AzureEventHubsKafkaOAuth2AutoConfiguration.class, + TestSpringTokenCredentialProviderContextProviderAutoConfiguration.class, AzureGlobalPropertiesAutoConfiguration.class, AzureTokenCredentialAutoConfiguration.class, KafkaAutoConfiguration.class)); diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/keyvault/AzureKeyVaultAutoConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/keyvault/AzureKeyVaultAutoConfigurationTests.java index 06cd1c40f2dc..1bb847fd016e 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/keyvault/AzureKeyVaultAutoConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/keyvault/AzureKeyVaultAutoConfigurationTests.java @@ -3,6 +3,7 @@ package com.azure.spring.cloud.autoconfigure.implementation.keyvault; +import com.azure.identity.extensions.implementation.template.AzureAuthenticationTemplate; import com.azure.security.keyvault.certificates.CertificateAsyncClient; import com.azure.security.keyvault.certificates.CertificateClient; import com.azure.security.keyvault.certificates.CertificateClientBuilder; @@ -18,6 +19,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.runner.ApplicationContextRunner; import static org.assertj.core.api.Assertions.assertThat; @@ -30,6 +32,7 @@ class AzureKeyVaultAutoConfigurationTests { private static final String ENDPOINT = "https:/%s.vault.azure.net/"; private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() + .withClassLoader(new FilteredClassLoader(AzureAuthenticationTemplate.class)) .withBean(AzureGlobalProperties.class, AzureGlobalProperties::new) .withConfiguration(AutoConfigurations.of(AzureKeyVaultCertificateAutoConfiguration.class)) .withConfiguration(AutoConfigurations.of(AzureKeyVaultSecretAutoConfiguration.class)); diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/keyvault/certificates/AzureKeyVaultCertificateAutoConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/keyvault/certificates/AzureKeyVaultCertificateAutoConfigurationTests.java index 33543a634e2d..a3c1f3b40f84 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/keyvault/certificates/AzureKeyVaultCertificateAutoConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/keyvault/certificates/AzureKeyVaultCertificateAutoConfigurationTests.java @@ -11,6 +11,7 @@ import com.azure.spring.cloud.autoconfigure.implementation.AbstractAzureServiceConfigurationTests; import com.azure.spring.cloud.autoconfigure.implementation.TestBuilderCustomizer; import com.azure.spring.cloud.autoconfigure.implementation.context.AzureGlobalPropertiesAutoConfiguration; +import com.azure.spring.cloud.autoconfigure.implementation.context.TestSpringTokenCredentialProviderContextProviderAutoConfiguration; import com.azure.spring.cloud.autoconfigure.implementation.keyvault.certificates.properties.AzureKeyVaultCertificateProperties; import com.azure.spring.cloud.service.implementation.keyvault.certificates.CertificateClientBuilderFactory; import org.junit.jupiter.api.Test; @@ -30,6 +31,7 @@ class AzureKeyVaultCertificateAutoConfigurationTests extends AbstractAzureServic private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of(AzureKeyVaultCertificateAutoConfiguration.class, + TestSpringTokenCredentialProviderContextProviderAutoConfiguration.class, AzureGlobalPropertiesAutoConfiguration.class)); @Override diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/keyvault/environment/KeyVaultPropertySourceTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/keyvault/environment/KeyVaultPropertySourceTests.java index 14b6f2504431..1747c0cfe468 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/keyvault/environment/KeyVaultPropertySourceTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/keyvault/environment/KeyVaultPropertySourceTests.java @@ -114,7 +114,7 @@ public void setTestSpringRelaxedBindingNames() { } @Test - @Timeout(5) + @Timeout(6) public void refreshTwoKeyVaultsPropertySources() throws InterruptedException { CountDownLatch latchForRefreshing = new CountDownLatch(2); new SecretRefreshing(latchForRefreshing, "KeyVault1", "test1", diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/keyvault/secrets/AzureKeyVaultSecretAutoConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/keyvault/secrets/AzureKeyVaultSecretAutoConfigurationTests.java index b53ba7442e2b..d3767e76603c 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/keyvault/secrets/AzureKeyVaultSecretAutoConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/keyvault/secrets/AzureKeyVaultSecretAutoConfigurationTests.java @@ -11,6 +11,7 @@ import com.azure.spring.cloud.autoconfigure.implementation.AbstractAzureServiceConfigurationTests; import com.azure.spring.cloud.autoconfigure.implementation.TestBuilderCustomizer; import com.azure.spring.cloud.autoconfigure.implementation.context.AzureGlobalPropertiesAutoConfiguration; +import com.azure.spring.cloud.autoconfigure.implementation.context.TestSpringTokenCredentialProviderContextProviderAutoConfiguration; import com.azure.spring.cloud.autoconfigure.implementation.keyvault.secrets.properties.AzureKeyVaultPropertySourceProperties; import com.azure.spring.cloud.autoconfigure.implementation.keyvault.secrets.properties.AzureKeyVaultSecretProperties; import com.azure.spring.cloud.service.implementation.keyvault.secrets.SecretClientBuilderFactory; @@ -35,6 +36,7 @@ class AzureKeyVaultSecretAutoConfigurationTests extends AbstractAzureServiceConf private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of( + TestSpringTokenCredentialProviderContextProviderAutoConfiguration.class, AzureGlobalPropertiesAutoConfiguration.class, AzureKeyVaultSecretAutoConfiguration.class)); diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/passwordless/MergeAzureCommonPropertiesTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/passwordless/MergeAzureCommonPropertiesTest.java index 9b4db99190af..56a9b5666059 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/passwordless/MergeAzureCommonPropertiesTest.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/passwordless/MergeAzureCommonPropertiesTest.java @@ -10,6 +10,7 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; class MergeAzureCommonPropertiesTest { @@ -21,6 +22,7 @@ void testGetPropertiesFromGlobalProperties() { globalProperties.getCredential().setPassword("global-password"); globalProperties.getCredential().setUsername("global-user-name"); globalProperties.getCredential().setManagedIdentityEnabled(true); + globalProperties.getCredential().setTokenCredentialBeanName("my-token-credential"); globalProperties.getProfile().setCloudType(AzureProfileOptionsProvider.CloudType.AZURE_CHINA); globalProperties.getProfile().setSubscriptionId("global-sub"); globalProperties.getProfile().setTenantId("global-tenant-id"); @@ -35,7 +37,8 @@ void testGetPropertiesFromGlobalProperties() { assertEquals("global-client-secret", result.getCredential().getClientSecret()); assertEquals("global-password", result.getCredential().getPassword()); assertEquals("global-user-name", result.getCredential().getUsername()); - assertEquals(false, result.getCredential().isManagedIdentityEnabled()); + assertTrue(result.getCredential().isManagedIdentityEnabled()); + assertEquals("my-token-credential", result.getCredential().getTokenCredentialBeanName()); assertEquals(AzureProfileOptionsProvider.CloudType.AZURE_CHINA, result.getProfile().getCloudType()); assertEquals("global-sub", result.getProfile().getSubscriptionId()); assertEquals("global-tenant-id", result.getProfile().getTenantId()); @@ -52,6 +55,7 @@ void testGetPropertiesFromAzurePasswordlessProperties() { passwordlessProperties.getCredential().setPassword("password"); passwordlessProperties.getCredential().setUsername("user-name"); passwordlessProperties.getCredential().setManagedIdentityEnabled(true); + passwordlessProperties.getCredential().setTokenCredentialBeanName("my-token-credential"); passwordlessProperties.getProfile().setCloudType(AzureProfileOptionsProvider.CloudType.AZURE_US_GOVERNMENT); passwordlessProperties.getProfile().setSubscriptionId("sub"); passwordlessProperties.getProfile().setTenantId("tenant-id"); @@ -64,7 +68,8 @@ void testGetPropertiesFromAzurePasswordlessProperties() { assertEquals("client-secret", result.getCredential().getClientSecret()); assertEquals("password", result.getCredential().getPassword()); assertEquals("user-name", result.getCredential().getUsername()); - assertEquals(true, result.getCredential().isManagedIdentityEnabled()); + assertTrue(result.getCredential().isManagedIdentityEnabled()); + assertEquals("my-token-credential", result.getCredential().getTokenCredentialBeanName()); assertEquals(AzureProfileOptionsProvider.CloudType.AZURE_US_GOVERNMENT, result.getProfile().getCloudType()); assertEquals("sub", result.getProfile().getSubscriptionId()); assertEquals("tenant-id", result.getProfile().getTenantId()); @@ -93,7 +98,7 @@ void testGetPropertiesFromGlobalAndPasswordlessProperties() { passwordlessProperties.setScopes("scope"); passwordlessProperties.getCredential().setClientSecret("client-secret"); passwordlessProperties.getCredential().setPassword("password"); - passwordlessProperties.getCredential().setManagedIdentityEnabled(true); + passwordlessProperties.getCredential().setTokenCredentialBeanName("my-token-credential"); passwordlessProperties.getProfile().setCloudType(AzureProfileOptionsProvider.CloudType.AZURE_US_GOVERNMENT); passwordlessProperties.getProfile().setSubscriptionId("sub"); @@ -105,7 +110,8 @@ void testGetPropertiesFromGlobalAndPasswordlessProperties() { assertEquals("client-secret", result.getCredential().getClientSecret()); assertEquals("password", result.getCredential().getPassword()); assertEquals("global-user-name", result.getCredential().getUsername()); - assertEquals(true, result.getCredential().isManagedIdentityEnabled()); + assertTrue(result.getCredential().isManagedIdentityEnabled()); + assertEquals("my-token-credential", result.getCredential().getTokenCredentialBeanName()); assertEquals(AzureProfileOptionsProvider.CloudType.AZURE_US_GOVERNMENT, result.getProfile().getCloudType()); assertEquals("sub", result.getProfile().getSubscriptionId()); assertEquals("global-tenant-id", result.getProfile().getTenantId()); diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/resourcemanager/AzureEventHubsResourceManagerAutoConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/resourcemanager/AzureEventHubsResourceManagerAutoConfigurationTests.java index bef554db47ac..0600371159f8 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/resourcemanager/AzureEventHubsResourceManagerAutoConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/resourcemanager/AzureEventHubsResourceManagerAutoConfigurationTests.java @@ -5,11 +5,15 @@ import com.azure.messaging.eventhubs.EventHubClientBuilder; import com.azure.resourcemanager.AzureResourceManager; +import com.azure.spring.cloud.autoconfigure.implementation.context.AzureGlobalPropertiesAutoConfiguration; +import com.azure.spring.cloud.autoconfigure.implementation.context.TestSpringTokenCredentialProviderContextProviderAutoConfiguration; import com.azure.spring.cloud.autoconfigure.implementation.context.properties.AzureGlobalProperties; import com.azure.spring.cloud.autoconfigure.implementation.eventhubs.properties.AzureEventHubsProperties; import com.azure.spring.cloud.resourcemanager.implementation.connectionstring.EventHubsArmConnectionStringProvider; import com.azure.spring.cloud.resourcemanager.implementation.provisioning.EventHubsProvisioner; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.runner.ApplicationContextRunner; @@ -84,4 +88,25 @@ void testAzureEventHubsResourceManagerAutoConfigurationBeans() { .withBean(AzureEventHubsProperties.class, AzureEventHubsProperties::new) .run(context -> assertThat(context).hasSingleBean(EventHubsProvisioner.class)); } + + @ParameterizedTest + @ValueSource(strings = { + "spring.cloud.azure.eventhubs.connection-string=Endpoint=sb://eventhub-test-1.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=ByyyxxxUw=", + "spring.cloud.azure.credential.token-credential-bean-name=my-token-credential", + "spring.cloud.azure.eventhubs.token-credential-bean-name=my-token-credential" + }) + void testNotCreateProviderBeanWhenMissingPropertiesConfigured(String missingProperty) { + this.contextRunner + .withUserConfiguration(TestSpringTokenCredentialProviderContextProviderAutoConfiguration.class, AzureGlobalPropertiesAutoConfiguration.class) + .withBean(AzureResourceManager.class, () -> mock(AzureResourceManager.class)) + .withPropertyValues( + "spring.cloud.azure.profile.tenant-id=test-tenant", + "spring.cloud.azure.profile.subscription-id=test-subscription-id", + missingProperty + ) + .run(context -> { + assertThat(context).hasSingleBean(AzureResourceManager.class); + assertThat(context).doesNotHaveBean(EventHubsArmConnectionStringProvider.class); + }); + } } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/resourcemanager/AzureResourceManagerAutoConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/resourcemanager/AzureResourceManagerAutoConfigurationTests.java index 45c5f96a996f..0a23489ffbd3 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/resourcemanager/AzureResourceManagerAutoConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/resourcemanager/AzureResourceManagerAutoConfigurationTests.java @@ -6,6 +6,7 @@ import com.azure.core.management.profile.AzureProfile; import com.azure.resourcemanager.AzureResourceManager; import com.azure.spring.cloud.autoconfigure.implementation.context.AzureGlobalPropertiesAutoConfiguration; +import com.azure.spring.cloud.autoconfigure.implementation.context.TestSpringTokenCredentialProviderContextProviderAutoConfiguration; import com.azure.spring.cloud.autoconfigure.implementation.context.properties.AzureGlobalProperties; import com.azure.spring.cloud.core.properties.resource.AzureResourceMetadata; import org.junit.jupiter.api.Test; @@ -84,7 +85,8 @@ void testWithoutAzureResourceMetadataClass() { @Test void testAzureProfileWithAzureDefault() { this.contextRunner - .withUserConfiguration(AzureGlobalPropertiesAutoConfiguration.class) + .withUserConfiguration(TestSpringTokenCredentialProviderContextProviderAutoConfiguration.class, + AzureGlobalPropertiesAutoConfiguration.class) .withBean(AzureResourceManager.class, () -> mock(AzureResourceManager.class)) .withPropertyValues( "spring.cloud.azure.profile.tenant-id=test-tenant-id", @@ -103,7 +105,8 @@ void testAzureProfileWithAzureDefault() { @Test void testAzureProfileWithAzureChina() { this.contextRunner - .withUserConfiguration(AzureGlobalPropertiesAutoConfiguration.class) + .withUserConfiguration(TestSpringTokenCredentialProviderContextProviderAutoConfiguration.class, + AzureGlobalPropertiesAutoConfiguration.class) .withBean(AzureResourceManager.class, () -> mock(AzureResourceManager.class)) .withPropertyValues( "spring.cloud.azure.profile.tenant-id=test-tenant-id", diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/resourcemanager/AzureServiceBusResourceManagerAutoConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/resourcemanager/AzureServiceBusResourceManagerAutoConfigurationTests.java index 05669cafdb3a..51c41efa3d68 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/resourcemanager/AzureServiceBusResourceManagerAutoConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/resourcemanager/AzureServiceBusResourceManagerAutoConfigurationTests.java @@ -5,11 +5,15 @@ import com.azure.messaging.servicebus.ServiceBusClientBuilder; import com.azure.resourcemanager.AzureResourceManager; +import com.azure.spring.cloud.autoconfigure.implementation.context.AzureGlobalPropertiesAutoConfiguration; +import com.azure.spring.cloud.autoconfigure.implementation.context.TestSpringTokenCredentialProviderContextProviderAutoConfiguration; import com.azure.spring.cloud.autoconfigure.implementation.context.properties.AzureGlobalProperties; import com.azure.spring.cloud.autoconfigure.implementation.servicebus.properties.AzureServiceBusProperties; import com.azure.spring.cloud.resourcemanager.implementation.connectionstring.ServiceBusArmConnectionStringProvider; import com.azure.spring.cloud.resourcemanager.implementation.provisioning.ServiceBusProvisioner; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.runner.ApplicationContextRunner; @@ -71,4 +75,26 @@ void testAzureServiceBusResourceManagerAutoConfigurationBeans() { assertThat(context).hasSingleBean(ServiceBusProvisioner.class); }); } + + @ParameterizedTest + @ValueSource(strings = { + "spring.cloud.azure.eventhubs.connection-string=Endpoint=sb://test.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=key", + "spring.cloud.azure.credential.token-credential-bean-name=my-token-credential", + "spring.cloud.azure.servicebus.credential.token-credential-bean-name=my-token-credential" + }) + void testNotCreateProviderBeanWhenMissingPropertiesConfigured(String missingProperty) { + this.contextRunner + .withUserConfiguration(TestSpringTokenCredentialProviderContextProviderAutoConfiguration.class, + AzureGlobalPropertiesAutoConfiguration.class) + .withBean(AzureResourceManager.class, () -> mock(AzureResourceManager.class)) + .withPropertyValues( + "spring.cloud.azure.profile.tenant-id=test-tenant", + "spring.cloud.azure.profile.subscription-id=test-subscription-id", + missingProperty + ) + .run(context -> { + assertThat(context).hasSingleBean(AzureResourceManager.class); + assertThat(context).doesNotHaveBean(ServiceBusArmConnectionStringProvider.class); + }); + } } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/resourcemanager/AzureStorageQueueResourceManagerAutoConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/resourcemanager/AzureStorageQueueResourceManagerAutoConfigurationTests.java index 31dfa2aed298..93425aa9a545 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/resourcemanager/AzureStorageQueueResourceManagerAutoConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/resourcemanager/AzureStorageQueueResourceManagerAutoConfigurationTests.java @@ -4,11 +4,15 @@ package com.azure.spring.cloud.autoconfigure.implementation.resourcemanager; import com.azure.resourcemanager.AzureResourceManager; +import com.azure.spring.cloud.autoconfigure.implementation.context.AzureGlobalPropertiesAutoConfiguration; +import com.azure.spring.cloud.autoconfigure.implementation.context.TestSpringTokenCredentialProviderContextProviderAutoConfiguration; import com.azure.spring.cloud.autoconfigure.implementation.context.properties.AzureGlobalProperties; import com.azure.spring.cloud.autoconfigure.implementation.storage.queue.properties.AzureStorageQueueProperties; import com.azure.spring.cloud.resourcemanager.implementation.connectionstring.StorageQueueArmConnectionStringProvider; import com.azure.storage.queue.QueueServiceClientBuilder; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.FilteredClassLoader; import org.springframework.boot.test.context.runner.ApplicationContextRunner; @@ -57,4 +61,26 @@ void testStorageQueueResourceManagerWithoutArmConnectionStringProviderClass() { .withBean(AzureResourceManager.class, () -> mock(AzureResourceManager.class)) .run(context -> assertThat(context).doesNotHaveBean(AzureStorageQueueResourceManagerAutoConfiguration.class)); } + + @ParameterizedTest + @ValueSource(strings = { + "spring.cloud.azure.storage.queue.connection-string=DefaultEndpointsProtocol=https;AccountName=test;AccountKey=key;EndpointSuffix=core.windows.net", + "spring.cloud.azure.credential.token-credential-bean-name=my-token-credential", + "spring.cloud.azure.storage.queue.credential.token-credential-bean-name=my-token-credential" + }) + void testNotCreateProviderBeanWhenMissingPropertiesConfigured(String missingProperty) { + this.contextRunner + .withUserConfiguration(TestSpringTokenCredentialProviderContextProviderAutoConfiguration.class, + AzureGlobalPropertiesAutoConfiguration.class) + .withBean(AzureResourceManager.class, () -> mock(AzureResourceManager.class)) + .withPropertyValues( + "spring.cloud.azure.profile.tenant-id=test-tenant", + "spring.cloud.azure.profile.subscription-id=test-subscription-id", + missingProperty + ) + .run(context -> { + assertThat(context).hasSingleBean(AzureResourceManager.class); + assertThat(context).doesNotHaveBean(StorageQueueArmConnectionStringProvider.class); + }); + } } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusAutoConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusAutoConfigurationTests.java index 92b4aff290d7..c4b66847fc11 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusAutoConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/servicebus/AzureServiceBusAutoConfigurationTests.java @@ -7,6 +7,7 @@ import com.azure.messaging.servicebus.ServiceBusClientBuilder; import com.azure.messaging.servicebus.models.ServiceBusReceiveMode; import com.azure.spring.cloud.autoconfigure.implementation.AbstractAzureServiceConfigurationTests; +import com.azure.spring.cloud.autoconfigure.implementation.context.TestSpringTokenCredentialProviderContextProviderAutoConfiguration; import com.azure.spring.cloud.autoconfigure.implementation.context.properties.AzureGlobalProperties; import com.azure.spring.cloud.autoconfigure.implementation.servicebus.properties.AzureServiceBusProperties; import com.azure.spring.cloud.core.properties.profile.AzureEnvironmentProperties; @@ -34,7 +35,8 @@ class AzureServiceBusAutoConfigurationTests extends AbstractAzureServiceConfigur ServiceBusClientBuilderFactory, AzureServiceBusProperties> { private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withConfiguration(AutoConfigurations.of(AzureServiceBusAutoConfiguration.class)); + .withConfiguration(AutoConfigurations.of(TestSpringTokenCredentialProviderContextProviderAutoConfiguration.class, + AzureServiceBusAutoConfiguration.class)); @Override protected ApplicationContextRunner getMinimalContextRunner() { diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/storage/blob/AzureStorageBlobAutoConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/storage/blob/AzureStorageBlobAutoConfigurationTests.java index 0c2d29c4677a..4d5ab81ea8a9 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/storage/blob/AzureStorageBlobAutoConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/storage/blob/AzureStorageBlobAutoConfigurationTests.java @@ -6,7 +6,8 @@ import com.azure.data.appconfiguration.ConfigurationClientBuilder; import com.azure.spring.cloud.autoconfigure.implementation.AbstractAzureServiceConfigurationTests; import com.azure.spring.cloud.autoconfigure.implementation.TestBuilderCustomizer; -import com.azure.spring.cloud.autoconfigure.implementation.context.properties.AzureGlobalProperties; +import com.azure.spring.cloud.autoconfigure.implementation.context.AzureGlobalPropertiesAutoConfiguration; +import com.azure.spring.cloud.autoconfigure.implementation.context.TestSpringTokenCredentialProviderContextProviderAutoConfiguration; import com.azure.spring.cloud.autoconfigure.implementation.storage.blob.properties.AzureStorageBlobConnectionDetails; import com.azure.spring.cloud.autoconfigure.implementation.storage.blob.properties.AzureStorageBlobProperties; import com.azure.spring.cloud.service.implementation.storage.blob.BlobServiceClientBuilderFactory; @@ -34,8 +35,8 @@ class AzureStorageBlobAutoConfigurationTests extends AbstractAzureServiceConfigu private static final String STORAGE_CONNECTION_STRING_PATTERN = "DefaultEndpointsProtocol=https;AccountName=%s;AccountKey=%s;EndpointSuffix=core.windows.net"; private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withBean(AzureGlobalProperties.class, AzureGlobalProperties::new) - .withConfiguration(AutoConfigurations.of(AzureStorageBlobAutoConfiguration.class)); + .withConfiguration(AutoConfigurations.of(TestSpringTokenCredentialProviderContextProviderAutoConfiguration.class, + AzureGlobalPropertiesAutoConfiguration.class, AzureStorageBlobAutoConfiguration.class)); @Override protected ApplicationContextRunner getMinimalContextRunner() { diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/storage/queue/AzureStorageQueueAutoConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/storage/queue/AzureStorageQueueAutoConfigurationTests.java index e172d0fb9b89..f44c7fcb8844 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/storage/queue/AzureStorageQueueAutoConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/storage/queue/AzureStorageQueueAutoConfigurationTests.java @@ -6,7 +6,8 @@ import com.azure.data.appconfiguration.ConfigurationClientBuilder; import com.azure.spring.cloud.autoconfigure.implementation.AbstractAzureServiceConfigurationTests; import com.azure.spring.cloud.autoconfigure.implementation.TestBuilderCustomizer; -import com.azure.spring.cloud.autoconfigure.implementation.context.properties.AzureGlobalProperties; +import com.azure.spring.cloud.autoconfigure.implementation.context.AzureGlobalPropertiesAutoConfiguration; +import com.azure.spring.cloud.autoconfigure.implementation.context.TestSpringTokenCredentialProviderContextProviderAutoConfiguration; import com.azure.spring.cloud.autoconfigure.implementation.storage.queue.properties.AzureStorageQueueConnectionDetails; import com.azure.spring.cloud.autoconfigure.implementation.storage.queue.properties.AzureStorageQueueProperties; import com.azure.spring.cloud.service.implementation.storage.queue.QueueServiceClientBuilderFactory; @@ -32,8 +33,8 @@ class AzureStorageQueueAutoConfigurationTests extends AbstractAzureServiceConfig private static final String STORAGE_CONNECTION_STRING_PATTERN = "DefaultEndpointsProtocol=https;AccountName=%s;AccountKey=%s;EndpointSuffix=core.windows.net"; private final ApplicationContextRunner contextRunner = new ApplicationContextRunner() - .withBean(AzureGlobalProperties.class, AzureGlobalProperties::new) - .withConfiguration(AutoConfigurations.of(AzureStorageQueueAutoConfiguration.class)); + .withConfiguration(AutoConfigurations.of(TestSpringTokenCredentialProviderContextProviderAutoConfiguration.class, + AzureGlobalPropertiesAutoConfiguration.class, AzureStorageQueueAutoConfiguration.class)); @Override protected ApplicationContextRunner getMinimalContextRunner() { diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/useragent/http/IdentityUserAgentTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/useragent/http/IdentityUserAgentTests.java index 4843af5a875a..a7c078a7fa52 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/useragent/http/IdentityUserAgentTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/useragent/http/IdentityUserAgentTests.java @@ -11,6 +11,7 @@ import com.azure.identity.ClientSecretCredentialBuilder; import com.azure.spring.cloud.autoconfigure.implementation.context.AzureGlobalPropertiesAutoConfiguration; import com.azure.spring.cloud.autoconfigure.implementation.context.AzureTokenCredentialAutoConfiguration; +import com.azure.spring.cloud.autoconfigure.implementation.context.TestSpringTokenCredentialProviderContextProviderAutoConfiguration; import com.azure.spring.cloud.core.customizer.AzureServiceClientBuilderCustomizer; import com.azure.spring.cloud.core.implementation.util.AzureSpringIdentifier; import org.junit.jupiter.api.Test; @@ -38,6 +39,7 @@ class IdentityUserAgentTests { void userAgentTest(CapturedOutput output) { new ApplicationContextRunner() .withConfiguration(AutoConfigurations.of( + TestSpringTokenCredentialProviderContextProviderAutoConfiguration.class, AzureTokenCredentialAutoConfiguration.class, AzureGlobalPropertiesAutoConfiguration.class )) diff --git a/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/implementation/credential/descriptor/AuthenticationDescriptor.java b/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/implementation/credential/descriptor/AuthenticationDescriptor.java index 9cdaf3ff686f..8e63dfee180d 100644 --- a/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/implementation/credential/descriptor/AuthenticationDescriptor.java +++ b/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/implementation/credential/descriptor/AuthenticationDescriptor.java @@ -27,7 +27,7 @@ public interface AuthenticationDescriptor { /** * Get the consumer function for credential. - * @return the cunsumer function. + * @return the consumer function. */ Consumer getConsumer(); } diff --git a/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/implementation/factory/AbstractAzureServiceClientBuilderFactory.java b/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/implementation/factory/AbstractAzureServiceClientBuilderFactory.java index a2e8c565d8f2..04f997fa5940 100644 --- a/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/implementation/factory/AbstractAzureServiceClientBuilderFactory.java +++ b/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/implementation/factory/AbstractAzureServiceClientBuilderFactory.java @@ -19,6 +19,9 @@ import com.azure.spring.cloud.core.provider.connectionstring.ServiceConnectionStringProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; import org.springframework.util.StringUtils; import java.util.ArrayList; @@ -34,12 +37,14 @@ * * @param Type of the service client builder */ -public abstract class AbstractAzureServiceClientBuilderFactory implements AzureServiceClientBuilderFactory { +public abstract class AbstractAzureServiceClientBuilderFactory implements AzureServiceClientBuilderFactory, ApplicationContextAware { private static final Logger LOGGER = LoggerFactory.getLogger(AbstractAzureServiceClientBuilderFactory.class); private static final TokenCredential DEFAULT_DEFAULT_TOKEN_CREDENTIAL = new DefaultAzureCredentialBuilder().build(); private static final AzureTokenCredentialResolver DEFAULT_TOKEN_CREDENTIAL_RESOLVER = new AzureTokenCredentialResolver(); + private ApplicationContext applicationContext; + /** * Create an instance of Azure sdk client builder. * @return The service client builder. @@ -185,6 +190,21 @@ protected void configureConfiguration(T builder) { @SuppressWarnings({ "rawtypes", "unchecked" }) protected void configureCredential(T builder) { List> descriptors = getAuthenticationDescriptors(builder); + String tokenCredentialBeanName = getAzureProperties().getCredential().getTokenCredentialBeanName(); + if (applicationContext != null && StringUtils.hasText(tokenCredentialBeanName)) { + Optional> tokenCredentialDescriptor = descriptors.stream() + .filter(auth -> TokenCredential.class == auth.getAzureCredentialType()) + .findFirst(); + if (tokenCredentialDescriptor.isPresent()) { + LOGGER.debug("Will configure the custom token credential bean ({}) for {}.", + tokenCredentialBeanName, builder.getClass().getSimpleName()); + TokenCredential customTokenCredential = applicationContext.getBean(tokenCredentialBeanName, TokenCredential.class); + ((AuthenticationDescriptor) tokenCredentialDescriptor.get()).getConsumer().accept(customTokenCredential); + credentialConfigured = true; + return; + } + } + Object azureCredential = resolveAzureCredential(getAzureProperties(), descriptors); if (azureCredential == null) { LOGGER.debug("No authentication credential configured for class {}.", builder.getClass().getSimpleName()); @@ -207,7 +227,7 @@ protected void configureCredential(T builder) { } /** - * Configure the connection string to the builder. It will try to resolve a connection string from the + * Configure the connection string to the builder if the credential is not yet configured. It will try to resolve a connection string from the * {@link AzureProperties}, if it is a {@link ConnectionStringProvider} instance. If no connection string found from * the {@link AzureProperties}, it will check if any {@link ServiceConnectionStringProvider} is provided and get the * connection string from the provider if set. If a connection string is resolved successfully, the @@ -216,6 +236,10 @@ protected void configureCredential(T builder) { * @param builder The service client builder. */ protected void configureConnectionString(T builder) { + if (credentialConfigured) { + return; + } + AzureProperties azureProperties = getAzureProperties(); // connection string set to properties will advantage the one from connection string provider @@ -343,4 +367,9 @@ public void setTokenCredentialResolver(AzureCredentialResolver LOGGER.debug("Will ignore the 'null' token credential resolver."); } } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; + } } diff --git a/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/implementation/util/AzurePasswordlessPropertiesUtils.java b/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/implementation/util/AzurePasswordlessPropertiesUtils.java index 54df0ceecbe7..6af576a029d8 100644 --- a/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/implementation/util/AzurePasswordlessPropertiesUtils.java +++ b/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/implementation/util/AzurePasswordlessPropertiesUtils.java @@ -6,14 +6,8 @@ import com.azure.spring.cloud.core.properties.AzureProperties; import com.azure.spring.cloud.core.properties.PasswordlessProperties; import org.springframework.beans.BeanUtils; -import org.springframework.beans.BeanWrapper; -import org.springframework.beans.BeanWrapperImpl; -import java.beans.PropertyDescriptor; -import java.util.HashSet; -import java.util.Objects; -import java.util.Set; -import java.util.function.Predicate; +import static com.azure.spring.cloud.core.implementation.util.AzurePropertiesUtils.copyPropertiesIgnoreNull; /** * Util class for AzurePasswordlessProperties. @@ -42,8 +36,10 @@ public static void copyAzureCommonProperties( } /** - * Copy common properties from source {@link PasswordlessProperties} object to target {@link T extends PasswordlessProperties} object. Ignore the source - * value if it is null. + * Copy common properties from source {@link PasswordlessProperties} object to target {@link T extends PasswordlessProperties} object. + * Ignore the source value: + * 1. if it is null. + * 2. if it's a primitive type and value is the default value. * * @param source The source {@link PasswordlessProperties} object. * @param target The target object. @@ -74,34 +70,4 @@ public static void mergeAzureCommonProperties copyAzureCommonProperties(defaultProperties, target); copyAzureCommonPropertiesIgnoreNull(properties, target); } - - /** - * Copy common properties from source object to target object. Ignore the source value if it is null. - * - * @param source The source object. - * @param target The target object. - */ - public static void copyPropertiesIgnoreNull(Object source, Object target) { - BeanUtils.copyProperties(source, target, findNullPropertyNames(source)); - } - - private static String[] findPropertyNames(Object source, Predicate predicate) { - final Set emptyNames = new HashSet<>(); - - final BeanWrapper beanWrapper = new BeanWrapperImpl(source); - PropertyDescriptor[] pds = beanWrapper.getPropertyDescriptors(); - - for (PropertyDescriptor pd : pds) { - Object srcValue = beanWrapper.getPropertyValue(pd.getName()); - if (predicate.test(srcValue)) { - emptyNames.add(pd.getName()); - } - } - return emptyNames.toArray(new String[0]); - } - - private static String[] findNullPropertyNames(Object source) { - return findPropertyNames(source, Objects::isNull); - } - } diff --git a/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/implementation/util/AzurePropertiesUtils.java b/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/implementation/util/AzurePropertiesUtils.java index 2ba0581a5fcf..c5574e20a0ae 100644 --- a/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/implementation/util/AzurePropertiesUtils.java +++ b/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/implementation/util/AzurePropertiesUtils.java @@ -14,7 +14,10 @@ import java.util.HashSet; import java.util.Objects; import java.util.Set; -import java.util.function.Predicate; +import java.util.function.BiFunction; + +import static com.azure.spring.cloud.core.implementation.util.ClassUtils.isPrimitiveDefaultValue; +import static com.azure.spring.cloud.core.implementation.util.ClassUtils.isPrimitiveNonDefaultValue; /** * @@ -96,7 +99,10 @@ public static void mergeAzureCommonProperties(AzureP } /** - * Copy common properties from source object to target object. Ignore the source value if it is null. + * Copy common properties from source object to target object. + * Ignore the source value: + * 1. if it is null. + * 2. if it's a primitive type and value is the default value. * * @param source The source object. * @param target The target object. @@ -106,7 +112,10 @@ public static void copyPropertiesIgnoreNull(Object source, Object target) { } /** - * Copy common properties from source object to target object. Ignore the target value if it is nonnull. + * Copy common properties from source object to target object. + * Ignore the target value: + * 1. if it is nonnull. + * 2. it's a primitive type and value is not the default value. * * @param source The source object. * @param target The target object. @@ -134,7 +143,7 @@ private static void copyHttpClientProperties(AzurePr } } - private static String[] findPropertyNames(Object source, Predicate predicate) { + private static String[] findPropertyNames(Object source, BiFunction, Object, Boolean> function) { final Set emptyNames = new HashSet<>(); final BeanWrapper beanWrapper = new BeanWrapperImpl(source); @@ -142,18 +151,20 @@ private static String[] findPropertyNames(Object source, Predicate predi for (PropertyDescriptor pd : pds) { Object srcValue = beanWrapper.getPropertyValue(pd.getName()); - if (predicate.test(srcValue)) { + if (function.apply(pd.getPropertyType(), srcValue)) { emptyNames.add(pd.getName()); } } return emptyNames.toArray(new String[0]); } - private static String[] findNullPropertyNames(Object source) { - return findPropertyNames(source, Objects::isNull); + static String[] findNullPropertyNames(Object source) { + return findPropertyNames(source, (propertyType, srcValue) -> + Objects.isNull(srcValue) || isPrimitiveDefaultValue(propertyType, srcValue)); } - private static String[] findNonNullPropertyNames(Object source) { - return findPropertyNames(source, Objects::nonNull); + static String[] findNonNullPropertyNames(Object source) { + return findPropertyNames(source, (propertyType, srcValue) -> + Objects.nonNull(srcValue) && isPrimitiveNonDefaultValue(propertyType, srcValue)); } } diff --git a/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/implementation/util/ClassUtils.java b/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/implementation/util/ClassUtils.java new file mode 100644 index 000000000000..5c18926e2da0 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/implementation/util/ClassUtils.java @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.cloud.core.implementation.util; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; + +public final class ClassUtils { + + private ClassUtils() { + + } + + private static final Map, Function> PRIMITIVE_DEFAULT_VALUE_CHECKER; + static { + PRIMITIVE_DEFAULT_VALUE_CHECKER = new HashMap<>(8); + PRIMITIVE_DEFAULT_VALUE_CHECKER.put(boolean.class, (v) -> !((Boolean) v)); + PRIMITIVE_DEFAULT_VALUE_CHECKER.put(byte.class, (v) -> (Byte) v == 0); + PRIMITIVE_DEFAULT_VALUE_CHECKER.put(char.class, (v) -> (Character) v == '\u0000'); + PRIMITIVE_DEFAULT_VALUE_CHECKER.put(short.class, (v) -> (Short) v == 0); + PRIMITIVE_DEFAULT_VALUE_CHECKER.put(int.class, (v) -> (Integer) v == 0); + PRIMITIVE_DEFAULT_VALUE_CHECKER.put(long.class, (v) -> (Long) v == 0); + PRIMITIVE_DEFAULT_VALUE_CHECKER.put(double.class, (v) -> (Double) v == 0d); + PRIMITIVE_DEFAULT_VALUE_CHECKER.put(float.class, (v) -> (Float) v == 0.0f); + } + + /** + * Check if it's a primitive type property with default init value. + * @param type Type class to be checked. + * @param value Value to be checked. + * @return True if it's a primitive type property with default init value. + */ + public static boolean isPrimitiveDefaultValue(Class type, Object value) { + return PRIMITIVE_DEFAULT_VALUE_CHECKER.containsKey(type) + && PRIMITIVE_DEFAULT_VALUE_CHECKER.get(type).apply(value); + } + + /** + * Check if it's a primitive type property and has a non default value. + * @param type Type class to be checked. + * @param value Value to be checked. + * @return True if it's a primitive type property and has a non default value. + */ + public static boolean isPrimitiveNonDefaultValue(Class type, Object value) { + return PRIMITIVE_DEFAULT_VALUE_CHECKER.containsKey(type) + && !PRIMITIVE_DEFAULT_VALUE_CHECKER.get(type).apply(value); + } + +} diff --git a/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/properties/authentication/TokenCredentialProperties.java b/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/properties/authentication/TokenCredentialProperties.java index 929108547cb2..e76b759b489d 100644 --- a/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/properties/authentication/TokenCredentialProperties.java +++ b/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/properties/authentication/TokenCredentialProperties.java @@ -53,6 +53,11 @@ public TokenCredentialProperties() { */ private boolean managedIdentityEnabled; + /** + * Custom Get the custom {@link com.azure.core.credential.TokenCredential} bean name, it's used for Service builder factory or passwordless authentication. + */ + private String tokenCredentialBeanName; + @Override public String getClientId() { return clientId; @@ -143,4 +148,17 @@ public boolean isManagedIdentityEnabled() { public void setManagedIdentityEnabled(boolean managedIdentityEnabled) { this.managedIdentityEnabled = managedIdentityEnabled; } + + @Override + public String getTokenCredentialBeanName() { + return tokenCredentialBeanName; + } + + /** + * Set the token credential bean name. + * @param tokenCredentialBeanName the bean name. + */ + public void setTokenCredentialBeanName(String tokenCredentialBeanName) { + this.tokenCredentialBeanName = tokenCredentialBeanName; + } } diff --git a/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/provider/authentication/TokenCredentialOptionsProvider.java b/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/provider/authentication/TokenCredentialOptionsProvider.java index e2e2624b4629..451b2c04e582 100644 --- a/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/provider/authentication/TokenCredentialOptionsProvider.java +++ b/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/provider/authentication/TokenCredentialOptionsProvider.java @@ -62,6 +62,11 @@ interface TokenCredentialOptions { */ boolean isManagedIdentityEnabled(); + /** + * Get the custom {@link com.azure.core.credential.TokenCredential} bean name. + * @return the token credential bean name. + */ + String getTokenCredentialBeanName(); } } diff --git a/sdk/spring/spring-cloud-azure-core/src/test/java/com/azure/spring/cloud/core/implementation/util/AzurePasswordlessPropertiesUtilsTest.java b/sdk/spring/spring-cloud-azure-core/src/test/java/com/azure/spring/cloud/core/implementation/util/AzurePasswordlessPropertiesUtilsTest.java index dc552df767f7..b3e969dcbc5d 100644 --- a/sdk/spring/spring-cloud-azure-core/src/test/java/com/azure/spring/cloud/core/implementation/util/AzurePasswordlessPropertiesUtilsTest.java +++ b/sdk/spring/spring-cloud-azure-core/src/test/java/com/azure/spring/cloud/core/implementation/util/AzurePasswordlessPropertiesUtilsTest.java @@ -53,6 +53,7 @@ void testCopyPropertiesToNewObjectShouldEqual() { source.credential.setUsername("credential-username-A"); source.credential.setPassword("credential-password-A"); source.credential.setManagedIdentityEnabled(true); + source.credential.setTokenCredentialBeanName("my-token-credential"); final PasswordlessPropertiesB target = new PasswordlessPropertiesB(); AzurePasswordlessPropertiesUtils.copyAzureCommonProperties(source, target); @@ -68,6 +69,8 @@ void testCopyPropertiesToNewObjectShouldEqual() { assertEquals("credential-client-cert-password-A", target.credential.getClientCertificatePassword()); assertEquals("credential-username-A", target.credential.getUsername()); assertEquals("credential-password-A", target.credential.getPassword()); + assertTrue(target.credential.isManagedIdentityEnabled()); + assertEquals("my-token-credential", target.credential.getTokenCredentialBeanName()); } @@ -98,6 +101,7 @@ void testCopyPropertiesToObjectWithSameFieldsSetShouldOverrideWithNull() { assertEquals("credential-client-cert-password-B", target.credential.getClientCertificatePassword()); assertEquals("credential-username-B", target.credential.getUsername()); assertEquals("credential-password-B", target.credential.getPassword()); + assertTrue(target.credential.isManagedIdentityEnabled()); AzurePropertiesA source = new AzurePropertiesA(); source.client.setApplicationId("client-application-id-A"); @@ -107,6 +111,7 @@ void testCopyPropertiesToObjectWithSameFieldsSetShouldOverrideWithNull() { source.retry.getExponential().setMaxRetries(13); source.retry.getExponential().setMaxDelay(Duration.ofSeconds(14)); source.credential.setClientId("credential-client-id-A"); + source.credential.setTokenCredentialBeanName("my-token-credential"); AzurePasswordlessPropertiesUtils.copyAzureCommonProperties(source, target); @@ -117,6 +122,8 @@ void testCopyPropertiesToObjectWithSameFieldsSetShouldOverrideWithNull() { assertEquals(AzureEnvironment.AZURE_CHINA.getActiveDirectoryEndpoint(), target.profile.getEnvironment().getActiveDirectoryEndpoint()); assertEquals(AzureEnvironment.AZURE_CHINA.getActiveDirectoryGraphApiVersion(), target.profile.getEnvironment().getActiveDirectoryGraphApiVersion()); assertEquals("credential-client-id-A", target.credential.getClientId()); + assertEquals("my-token-credential", target.credential.getTokenCredentialBeanName()); +// assertTrue(target.credential.isManagedIdentityEnabled()); assertNull(target.credential.getClientSecret()); assertNull(target.credential.getClientCertificatePath()); assertNull(target.credential.getClientCertificatePassword()); @@ -174,6 +181,7 @@ void testCopyPropertiesIgnoresNullToObjectWithSameFieldsSetShouldOverrideWithout assertEquals("credential-client-cert-password-B", target.credential.getClientCertificatePassword()); assertEquals("credential-username-B", target.credential.getUsername()); assertEquals("credential-password-B", target.credential.getPassword()); + assertTrue(target.credential.isManagedIdentityEnabled()); assertTrue(target.isPasswordlessEnabled()); assertEquals("fake-scopes", target.getScopes()); diff --git a/sdk/spring/spring-cloud-azure-core/src/test/java/com/azure/spring/cloud/core/util/AzurePropertiesUtilsTests.java b/sdk/spring/spring-cloud-azure-core/src/test/java/com/azure/spring/cloud/core/util/AzurePropertiesUtilsTests.java index 07a17e1464a7..b14a69f9e7dd 100644 --- a/sdk/spring/spring-cloud-azure-core/src/test/java/com/azure/spring/cloud/core/util/AzurePropertiesUtilsTests.java +++ b/sdk/spring/spring-cloud-azure-core/src/test/java/com/azure/spring/cloud/core/util/AzurePropertiesUtilsTests.java @@ -31,6 +31,7 @@ import static com.azure.spring.cloud.core.provider.RetryOptionsProvider.RetryMode.FIXED; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; class AzurePropertiesUtilsTests { @@ -404,6 +405,8 @@ void testCopyPropertiesIgnoreNullToObjectWithDifferentFieldsSetShouldMerge() { void testCopyPropertiesSourceNotChanged() { AzurePropertiesA source = new AzurePropertiesA(); source.credential.setClientId("client-id-A"); + source.credential.setManagedIdentityEnabled(true); + source.credential.setTokenCredentialBeanName("my-token-credential"); source.getProfile().setCloudType(AZURE); AzurePropertiesB target = new AzurePropertiesB(); @@ -411,6 +414,8 @@ void testCopyPropertiesSourceNotChanged() { AzurePropertiesUtils.copyAzureCommonProperties(source, target); assertEquals("client-id-A", target.credential.getClientId()); + assertEquals("my-token-credential", target.credential.getTokenCredentialBeanName()); + assertTrue(target.credential.isManagedIdentityEnabled()); // Update target will not affect source target.retry.getExponential().setBaseDelay(Duration.ofSeconds(2)); diff --git a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/identity/credential/provider/SpringTokenCredentialProvider.java b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/identity/credential/provider/SpringTokenCredentialProvider.java index adf839122f5c..440b2fbb6f2f 100644 --- a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/identity/credential/provider/SpringTokenCredentialProvider.java +++ b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/identity/credential/provider/SpringTokenCredentialProvider.java @@ -5,6 +5,7 @@ import com.azure.core.credential.TokenCredential; import com.azure.identity.extensions.implementation.credential.TokenCredentialProviderOptions; +import com.azure.identity.extensions.implementation.credential.provider.DefaultTokenCredentialProvider; import com.azure.identity.extensions.implementation.credential.provider.TokenCredentialProvider; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; @@ -13,7 +14,7 @@ import java.util.Objects; /** - * TokenCredentialProvider contains spring context. + * TokenCredentialProvider implementation for Spring context. */ public class SpringTokenCredentialProvider implements TokenCredentialProvider, ApplicationContextAware { @@ -23,6 +24,12 @@ public class SpringTokenCredentialProvider implements TokenCredentialProvider, A private ApplicationContext applicationContext; private String tokenCredentialBeanName = DEFAULT_TOKEN_CREDENTIAL_BEAN_NAME; + /** + * Create a {@link SpringTokenCredentialProvider} instance with a {@link TokenCredentialProviderOptions}. + * If option token-credential-bean-name is specified, it will get this bean from application context, + * otherwise it will delegate {@link DefaultTokenCredentialProvider} to create a token credential. + * @param options the token credential provider options. + */ public SpringTokenCredentialProvider(TokenCredentialProviderOptions options) { String beanName = options == null ? null : options.getTokenCredentialBeanName(); if (beanName != null && !beanName.isEmpty()) { diff --git a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/servicebus/factory/AbstractServiceBusSubClientBuilderFactory.java b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/servicebus/factory/AbstractServiceBusSubClientBuilderFactory.java index baca3060b5a2..e8177a24cdb2 100644 --- a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/servicebus/factory/AbstractServiceBusSubClientBuilderFactory.java +++ b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/servicebus/factory/AbstractServiceBusSubClientBuilderFactory.java @@ -76,6 +76,21 @@ protected AbstractServiceBusSubClientBuilderFactory(ServiceBusClientBuilder serv } } + @Override + protected void configureCredential(T builder) { + // skip to avoid overriding the parent builder's credentials. + } + + @Override + protected void configureConnectionString(T builder) { + // skip to avoid overriding the parent builder's credentials. + } + + @Override + protected void configureDefaultCredential(T builder) { + // skip to avoid overriding the parent builder's credentials. + } + protected boolean isShareServiceBusClientBuilder() { return shareServiceBusClientBuilder; } diff --git a/sdk/spring/spring-cloud-azure-stream-binder-servicebus/src/main/java/com/azure/spring/cloud/stream/binder/servicebus/implementation/config/ServiceBusBinderConfiguration.java b/sdk/spring/spring-cloud-azure-stream-binder-servicebus/src/main/java/com/azure/spring/cloud/stream/binder/servicebus/implementation/config/ServiceBusBinderConfiguration.java index 4a22c91216ac..4e26622665ae 100644 --- a/sdk/spring/spring-cloud-azure-stream-binder-servicebus/src/main/java/com/azure/spring/cloud/stream/binder/servicebus/implementation/config/ServiceBusBinderConfiguration.java +++ b/sdk/spring/spring-cloud-azure-stream-binder-servicebus/src/main/java/com/azure/spring/cloud/stream/binder/servicebus/implementation/config/ServiceBusBinderConfiguration.java @@ -121,9 +121,11 @@ ServiceBusMessageChannelBinder serviceBusBinder(ServiceBusChannelProvisioner cha ServiceBusProducerFactoryCustomizer defaultServiceBusProducerFactoryCustomizer( AzureTokenCredentialResolver azureTokenCredentialResolver, @Qualifier(DEFAULT_TOKEN_CREDENTIAL_BEAN_NAME) TokenCredential defaultAzureCredential, + ObjectProvider> clientBuilderCustomizers, ObjectProvider> senderClientBuilderCustomizers) { return new DefaultProducerFactoryCustomizer(defaultAzureCredential, azureTokenCredentialResolver, + clientBuilderCustomizers, senderClientBuilderCustomizers); } @@ -149,13 +151,16 @@ static class DefaultProducerFactoryCustomizer implements ServiceBusProducerFacto private final TokenCredential defaultCredential; private final AzureTokenCredentialResolver tokenCredentialResolver; + private final ObjectProvider> clientBuilderCustomizers; private final ObjectProvider> senderClientBuilderCustomizers; DefaultProducerFactoryCustomizer(TokenCredential defaultCredential, AzureTokenCredentialResolver azureTokenCredentialResolver, + ObjectProvider> clientBuilderCustomizers, ObjectProvider> senderClientBuilderCustomizers) { this.defaultCredential = defaultCredential; this.tokenCredentialResolver = azureTokenCredentialResolver; + this.clientBuilderCustomizers = clientBuilderCustomizers; this.senderClientBuilderCustomizers = senderClientBuilderCustomizers; } @@ -167,10 +172,15 @@ public void customize(ServiceBusProducerFactory factory) { defaultFactory.setDefaultCredential(defaultCredential); defaultFactory.setTokenCredentialResolver(tokenCredentialResolver); + clientBuilderCustomizers.orderedStream().forEach(defaultFactory::addServiceBusClientBuilderCustomizer); senderClientBuilderCustomizers.orderedStream().forEach(defaultFactory::addBuilderCustomizer); } } + ObjectProvider> getClientBuilderCustomizers() { + return clientBuilderCustomizers; + } + ObjectProvider> getSenderClientBuilderCustomizers() { return senderClientBuilderCustomizers; } diff --git a/sdk/spring/spring-cloud-azure-stream-binder-servicebus/src/test/java/com/azure/spring/cloud/stream/binder/servicebus/implementation/config/ServiceBusBinderConfigurationTests.java b/sdk/spring/spring-cloud-azure-stream-binder-servicebus/src/test/java/com/azure/spring/cloud/stream/binder/servicebus/implementation/config/ServiceBusBinderConfigurationTests.java index 337342aac1b0..1611bf16157a 100644 --- a/sdk/spring/spring-cloud-azure-stream-binder-servicebus/src/test/java/com/azure/spring/cloud/stream/binder/servicebus/implementation/config/ServiceBusBinderConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-stream-binder-servicebus/src/test/java/com/azure/spring/cloud/stream/binder/servicebus/implementation/config/ServiceBusBinderConfigurationTests.java @@ -173,39 +173,21 @@ void processorFactoryCustomizerShouldBeConfigured() { @Test void producerBuilderCustomizerShouldBeConfiguredToProducerFactoryCustomizer() { - this.contextRunner - .withBean(ServiceBusProvisioner.class, () -> mock(ServiceBusProvisioner.class)) - .withPropertyValues("spring.cloud.azure.servicebus.namespace=fake-namespace") - .withBean("producer-customizer1", ServiceBusSenderClientBuilderCustomizer.class, ServiceBusSenderClientBuilderCustomizer::new) - .withBean("processor-customizer1", ServiceBusProcessorClientBuilderCustomizer.class, ServiceBusProcessorClientBuilderCustomizer::new) - .withBean("processor-customizer2", ServiceBusProcessorClientBuilderCustomizer.class, ServiceBusProcessorClientBuilderCustomizer::new) - .withBean("session-processor-customizer1", ServiceBusSessionProcessorClientBuilderCustomizer.class, ServiceBusSessionProcessorClientBuilderCustomizer::new) - .withBean("session-processor-customizer2", ServiceBusSessionProcessorClientBuilderCustomizer.class, ServiceBusSessionProcessorClientBuilderCustomizer::new) - .withBean("session-processor-customizer3", ServiceBusSessionProcessorClientBuilderCustomizer.class, ServiceBusSessionProcessorClientBuilderCustomizer::new) - .withBean("other-customizer1", OtherBuilderCustomizer.class, OtherBuilderCustomizer::new) + getCustomizedApplicationContextRunner() .run(context -> { assertThat(context).hasSingleBean(ServiceBusProducerFactoryCustomizer.class); ServiceBusProducerFactoryCustomizer clientFactoryCustomizer = context.getBean(ServiceBusProducerFactoryCustomizer.class); ServiceBusBinderConfiguration.DefaultProducerFactoryCustomizer defaultFactoryCustomizer = (ServiceBusBinderConfiguration.DefaultProducerFactoryCustomizer) clientFactoryCustomizer; + assertEquals(1, (int) defaultFactoryCustomizer.getClientBuilderCustomizers().stream().count()); assertEquals(1, (int) defaultFactoryCustomizer.getSenderClientBuilderCustomizers().stream().count()); }); } @Test void processorBuilderCustomizerShouldBeConfiguredToProcessorFactoryCustomizer() { - this.contextRunner - .withBean(ServiceBusProvisioner.class, () -> mock(ServiceBusProvisioner.class)) - .withPropertyValues("spring.cloud.azure.servicebus.namespace=fake-namespace") - .withBean("client-customizer1", ServiceBusClientBuilderCustomizer.class, ServiceBusClientBuilderCustomizer::new) - .withBean("producer-customizer1", ServiceBusSenderClientBuilderCustomizer.class, ServiceBusSenderClientBuilderCustomizer::new) - .withBean("processor-customizer1", ServiceBusProcessorClientBuilderCustomizer.class, ServiceBusProcessorClientBuilderCustomizer::new) - .withBean("processor-customizer2", ServiceBusProcessorClientBuilderCustomizer.class, ServiceBusProcessorClientBuilderCustomizer::new) - .withBean("session-processor-customizer1", ServiceBusSessionProcessorClientBuilderCustomizer.class, ServiceBusSessionProcessorClientBuilderCustomizer::new) - .withBean("session-processor-customizer2", ServiceBusSessionProcessorClientBuilderCustomizer.class, ServiceBusSessionProcessorClientBuilderCustomizer::new) - .withBean("session-processor-customizer3", ServiceBusSessionProcessorClientBuilderCustomizer.class, ServiceBusSessionProcessorClientBuilderCustomizer::new) - .withBean("other-customizer1", OtherBuilderCustomizer.class, OtherBuilderCustomizer::new) + getCustomizedApplicationContextRunner() .run(context -> { assertThat(context).hasSingleBean(ServiceBusProcessorFactoryCustomizer.class); ServiceBusProcessorFactoryCustomizer clientFactoryCustomizer = context.getBean(ServiceBusProcessorFactoryCustomizer.class); @@ -218,6 +200,20 @@ void processorBuilderCustomizerShouldBeConfiguredToProcessorFactoryCustomizer() }); } + private ApplicationContextRunner getCustomizedApplicationContextRunner() { + return this.contextRunner + .withBean(ServiceBusProvisioner.class, () -> mock(ServiceBusProvisioner.class)) + .withPropertyValues("spring.cloud.azure.servicebus.namespace=fake-namespace") + .withBean("client-customizer1", ServiceBusClientBuilderCustomizer.class, ServiceBusClientBuilderCustomizer::new) + .withBean("producer-customizer1", ServiceBusSenderClientBuilderCustomizer.class, ServiceBusSenderClientBuilderCustomizer::new) + .withBean("processor-customizer1", ServiceBusProcessorClientBuilderCustomizer.class, ServiceBusProcessorClientBuilderCustomizer::new) + .withBean("processor-customizer2", ServiceBusProcessorClientBuilderCustomizer.class, ServiceBusProcessorClientBuilderCustomizer::new) + .withBean("session-processor-customizer1", ServiceBusSessionProcessorClientBuilderCustomizer.class, ServiceBusSessionProcessorClientBuilderCustomizer::new) + .withBean("session-processor-customizer2", ServiceBusSessionProcessorClientBuilderCustomizer.class, ServiceBusSessionProcessorClientBuilderCustomizer::new) + .withBean("session-processor-customizer3", ServiceBusSessionProcessorClientBuilderCustomizer.class, ServiceBusSessionProcessorClientBuilderCustomizer::new) + .withBean("other-customizer1", OtherBuilderCustomizer.class, OtherBuilderCustomizer::new); + } + private static class ServiceBusClientBuilderCustomizer implements AzureServiceClientBuilderCustomizer { @Override