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 bdd7e064b9846..8561cad9d7b5b 100644
--- a/eng/code-quality-reports/src/main/resources/revapi/revapi.json
+++ b/eng/code-quality-reports/src/main/resources/revapi/revapi.json
@@ -136,6 +136,12 @@
"old": "method java\\.lang\\.String com\\.azure\\.spring\\.cloud\\.autoconfigure\\.jms\\.properties\\.AzureServiceBusJmsProperties::(getPassword|getRemoteUrl|getUsername)\\(\\)",
"justification": "Remove some meaningless jms properties"
},
+ {
+ "code" : "java.annotation.attributeValueChanged",
+ "old" : "class com.azure.spring.cloud.autoconfigure.jms.ServiceBusJmsAutoConfiguration",
+ "new" : "class com.azure.spring.cloud.autoconfigure.jms.ServiceBusJmsAutoConfiguration",
+ "justification": "Import ServiceBusJmsPasswordlessConfiguration.class"
+ },
{
"regex": true,
"code": "java\\.method\\.removed",
diff --git a/sdk/boms/spring-cloud-azure-dependencies/pom.xml b/sdk/boms/spring-cloud-azure-dependencies/pom.xml
index 1b0e9bf7d7d7e..a619c9cdc8c48 100644
--- a/sdk/boms/spring-cloud-azure-dependencies/pom.xml
+++ b/sdk/boms/spring-cloud-azure-dependencies/pom.xml
@@ -51,7 +51,7 @@
com.azure
azure-sdk-bom
- 1.2.9
+ 1.2.10
pom
import
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/passwordless/AzurePasswordlessEnvironmentPostProcessor.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/passwordless/AzurePasswordlessEnvironmentPostProcessor.java
new file mode 100644
index 0000000000000..fa46fef2af5fb
--- /dev/null
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/passwordless/AzurePasswordlessEnvironmentPostProcessor.java
@@ -0,0 +1,43 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.azure.spring.cloud.autoconfigure.implementation.passwordless;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor;
+import org.springframework.boot.env.EnvironmentPostProcessor;
+import org.springframework.core.Ordered;
+import org.springframework.core.env.ConfigurableEnvironment;
+import org.springframework.core.env.PropertiesPropertySource;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * Add properties to 'spring.cloud.function.ineligible-definitions' to filter ineligible functions that used by passwordless autoconfigurations.
+ *
+ * @since 4.7.0
+ */
+public class AzurePasswordlessEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered {
+
+ /**
+ * The order value of the {@link AzurePasswordlessEnvironmentPostProcessor}.
+ */
+ public static final int ORDER = ConfigDataEnvironmentPostProcessor.ORDER + 2;
+
+ @Override
+ public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
+ Properties properties = new Properties();
+ List passwordlessCredentialSupplier = new ArrayList<>();
+ passwordlessCredentialSupplier.add("azureRedisCredentialSupplier");
+ passwordlessCredentialSupplier.add("azureServiceBusJmsCredentialSupplier");
+ properties.setProperty("spring.cloud.function.ineligible-definitions", String.join(",", passwordlessCredentialSupplier));
+ environment.getPropertySources().addLast(new PropertiesPropertySource("passwordless", properties));
+ }
+
+ @Override
+ public int getOrder() {
+ return ORDER;
+ }
+}
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/jdbc/JdbcPropertiesBeanPostProcessor.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/jdbc/JdbcPropertiesBeanPostProcessor.java
index aa1aa765cc996..7e16de6c04045 100644
--- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/jdbc/JdbcPropertiesBeanPostProcessor.java
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/jdbc/JdbcPropertiesBeanPostProcessor.java
@@ -3,15 +3,17 @@
package com.azure.spring.cloud.autoconfigure.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.context.AzureGlobalProperties;
import com.azure.spring.cloud.autoconfigure.implementation.jdbc.DatabaseType;
import com.azure.spring.cloud.autoconfigure.implementation.jdbc.JdbcConnectionString;
import com.azure.spring.cloud.autoconfigure.implementation.jdbc.JdbcConnectionStringEnhancer;
-import com.azure.spring.cloud.core.implementation.credential.resolver.AzureTokenCredentialResolver;
+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.service.implementation.passwordless.AzurePasswordlessProperties;
+import com.azure.spring.cloud.service.implementation.passwordless.AzureJdbcPasswordlessProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
@@ -35,7 +37,6 @@
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.core.implementation.util.AzurePropertiesUtils.copyPropertiesIgnoreTargetNonNull;
import static com.azure.spring.cloud.service.implementation.identity.credential.provider.SpringTokenCredentialProvider.PASSWORDLESS_TOKEN_CREDENTIAL_BEAN_NAME;
@@ -55,7 +56,7 @@ class JdbcPropertiesBeanPostProcessor implements BeanPostProcessor, EnvironmentA
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof DataSourceProperties) {
DataSourceProperties dataSourceProperties = (DataSourceProperties) bean;
- AzurePasswordlessProperties properties = buildAzureProperties();
+ AzureJdbcPasswordlessProperties properties = buildAzureProperties();
if (!properties.isPasswordlessEnabled()) {
LOGGER.debug("Feature passwordless authentication is not enabled, skip enhancing jdbc url.");
@@ -128,17 +129,15 @@ private void enhanceUserAgent(DatabaseType databaseType, JdbcConnectionStringEnh
}
}
- private Map buildEnhancedProperties(DatabaseType databaseType, AzurePasswordlessProperties properties) {
+ private Map buildEnhancedProperties(DatabaseType databaseType, AzureJdbcPasswordlessProperties properties) {
Map result = new HashMap<>();
- AzureTokenCredentialResolver resolver = applicationContext.getBean(AzureTokenCredentialResolver.class);
- TokenCredential tokenCredential = resolver.resolve(properties);
+ TokenCredentialProvider tokenCredentialProvider = TokenCredentialProvider.createDefault(new TokenCredentialProviderOptions(properties.toPasswordlessProperties()));
+ TokenCredential tokenCredential = tokenCredentialProvider.get();
- if (tokenCredential != null) {
- LOGGER.debug("Add SpringTokenCredentialProvider as the default token credential provider.");
- AuthProperty.TOKEN_CREDENTIAL_BEAN_NAME.setProperty(result, PASSWORDLESS_TOKEN_CREDENTIAL_BEAN_NAME);
- applicationContext.registerBean(PASSWORDLESS_TOKEN_CREDENTIAL_BEAN_NAME, TokenCredential.class, () -> tokenCredential);
- }
+ 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());
@@ -157,12 +156,14 @@ public void setApplicationContext(ApplicationContext applicationContext) throws
this.applicationContext = (GenericApplicationContext) applicationContext;
}
- private AzurePasswordlessProperties buildAzureProperties() {
+ private AzureJdbcPasswordlessProperties buildAzureProperties() {
AzureGlobalProperties azureGlobalProperties = applicationContext.getBean(AzureGlobalProperties.class);
- AzurePasswordlessProperties azurePasswordlessProperties = Binder.get(environment)
- .bindOrCreate(SPRING_CLOUD_AZURE_DATASOURCE_PREFIX, AzurePasswordlessProperties.class);
- copyPropertiesIgnoreTargetNonNull(azureGlobalProperties.getProfile(), azurePasswordlessProperties.getProfile());
- copyPropertiesIgnoreTargetNonNull(azureGlobalProperties.getCredential(), azurePasswordlessProperties.getCredential());
- return azurePasswordlessProperties;
+ AzureJdbcPasswordlessProperties azurePasswordlessProperties = Binder.get(environment)
+ .bindOrCreate(SPRING_CLOUD_AZURE_DATASOURCE_PREFIX, 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/jms/AzureServiceBusJmsCredentialSupplier.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/jms/AzureServiceBusJmsCredentialSupplier.java
new file mode 100644
index 0000000000000..a54f916913b56
--- /dev/null
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/jms/AzureServiceBusJmsCredentialSupplier.java
@@ -0,0 +1,37 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.azure.spring.cloud.autoconfigure.jms;
+
+import com.azure.identity.extensions.implementation.template.AzureAuthenticationTemplate;
+
+import java.util.Properties;
+import java.util.function.Supplier;
+
+/**
+ * AzureServiceBusJmsCredentialSupplier that provides a String as the password to connect Azure ServiceBus.
+ *
+ * @since 4.7.0
+ */
+public class AzureServiceBusJmsCredentialSupplier implements Supplier {
+
+ private final AzureAuthenticationTemplate azureAuthenticationTemplate;
+
+ /**
+ * Create {@link AzureServiceBusJmsCredentialSupplier} instance.
+ * @param properties properties to initialize AzureServiceBusJmsCredentialSupplier.
+ */
+ public AzureServiceBusJmsCredentialSupplier(Properties properties) {
+ azureAuthenticationTemplate = new AzureAuthenticationTemplate();
+ azureAuthenticationTemplate.init(properties);
+ }
+
+ @Override
+ public String get() {
+ return azureAuthenticationTemplate.getTokenAsPassword();
+ }
+
+ AzureServiceBusJmsCredentialSupplier(AzureAuthenticationTemplate azureAuthenticationTemplate) {
+ this.azureAuthenticationTemplate = azureAuthenticationTemplate;
+ }
+}
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/jms/ServiceBusJmsAutoConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/jms/ServiceBusJmsAutoConfiguration.java
index 412ebcc113fe7..d003479c08028 100644
--- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/jms/ServiceBusJmsAutoConfiguration.java
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/jms/ServiceBusJmsAutoConfiguration.java
@@ -4,8 +4,10 @@
package com.azure.spring.cloud.autoconfigure.jms;
import com.azure.spring.cloud.autoconfigure.condition.ConditionalOnMissingProperty;
+import com.azure.spring.cloud.autoconfigure.context.AzureGlobalProperties;
import com.azure.spring.cloud.autoconfigure.jms.properties.AzureServiceBusJmsProperties;
import com.azure.spring.cloud.autoconfigure.resourcemanager.AzureServiceBusResourceManagerAutoConfiguration;
+import com.azure.spring.cloud.core.implementation.util.AzurePasswordlessPropertiesUtils;
import com.azure.spring.cloud.core.provider.connectionstring.ServiceConnectionStringProvider;
import com.azure.spring.cloud.core.service.AzureServiceType;
import org.apache.qpid.jms.JmsConnectionExtensions;
@@ -31,6 +33,7 @@
import java.util.HashMap;
import java.util.Map;
+import static com.azure.spring.cloud.core.implementation.util.AzureSpringIdentifier.AZURE_SPRING_PASSWORDLESS_SERVICE_BUS;
import static com.azure.spring.cloud.core.implementation.util.AzureSpringIdentifier.AZURE_SPRING_SERVICE_BUS;
/**
@@ -45,17 +48,27 @@
AzureServiceBusResourceManagerAutoConfiguration.class })
@ConditionalOnProperty(value = "spring.jms.servicebus.enabled", matchIfMissing = true)
@ConditionalOnClass({ ConnectionFactory.class, JmsConnectionFactory.class, JmsTemplate.class })
-@EnableConfigurationProperties({ AzureServiceBusJmsProperties.class, JmsProperties.class })
-@Import({ ServiceBusJmsConnectionFactoryConfiguration.class, ServiceBusJmsContainerConfiguration.class })
+@EnableConfigurationProperties({ JmsProperties.class })
+@Import({ ServiceBusJmsPasswordlessConfiguration.class, ServiceBusJmsConnectionFactoryConfiguration.class, ServiceBusJmsContainerConfiguration.class })
public class ServiceBusJmsAutoConfiguration {
+ @Bean
+ AzureServiceBusJmsProperties serviceBusJmsProperties(AzureGlobalProperties azureGlobalProperties) {
+ AzureServiceBusJmsProperties properties = new AzureServiceBusJmsProperties();
+ return mergeAzureProperties(azureGlobalProperties, properties);
+ }
+
@Bean
@ConditionalOnExpression("'premium'.equalsIgnoreCase('${spring.jms.servicebus.pricing-tier}')")
- ServiceBusJmsConnectionFactoryCustomizer amqpOpenPropertiesCustomizer() {
+ ServiceBusJmsConnectionFactoryCustomizer amqpOpenPropertiesCustomizer(ObjectProvider azureServiceBusJmsCredentialSupplier) {
return factory -> {
final Map properties = new HashMap<>();
properties.put("com.microsoft:is-client-provider", true);
- properties.put("user-agent", AZURE_SPRING_SERVICE_BUS);
+ if (azureServiceBusJmsCredentialSupplier.getIfAvailable() != null) {
+ properties.put("user-agent", AZURE_SPRING_PASSWORDLESS_SERVICE_BUS);
+ } else {
+ properties.put("user-agent", AZURE_SPRING_SERVICE_BUS);
+ }
//set user agent
factory.setExtension(JmsConnectionExtensions.AMQP_OPEN_PROPERTIES.toString(),
(connection, uri) -> properties);
@@ -75,4 +88,10 @@ static AzureServiceBusJmsPropertiesBeanPostProcessor azureServiceBusJmsPropertie
ObjectProvider> connectionStringProviders) {
return new AzureServiceBusJmsPropertiesBeanPostProcessor(connectionStringProviders);
}
+
+ private AzureServiceBusJmsProperties mergeAzureProperties(AzureGlobalProperties azureGlobalProperties, AzureServiceBusJmsProperties azurePasswordlessProperties) {
+ AzureServiceBusJmsProperties mergedProperties = new AzureServiceBusJmsProperties();
+ AzurePasswordlessPropertiesUtils.mergeAzureCommonProperties(azureGlobalProperties, azurePasswordlessProperties, mergedProperties);
+ return mergedProperties;
+ }
}
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/jms/ServiceBusJmsConnectionFactoryFactory.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/jms/ServiceBusJmsConnectionFactoryFactory.java
index fd0f5905af41c..f5965851f5efa 100644
--- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/jms/ServiceBusJmsConnectionFactoryFactory.java
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/jms/ServiceBusJmsConnectionFactoryFactory.java
@@ -57,20 +57,26 @@ private void setPrefetchPolicy(T fact
private T createConnectionFactoryInstance(Class factoryClass) {
try {
T factory;
- ServiceBusConnectionString serviceBusConnectionString = new ServiceBusConnectionString(properties.getConnectionString());
- String host = serviceBusConnectionString.getEndpointUri().getHost();
+ if (properties.isPasswordlessEnabled()) {
+ String remoteUrl = String.format(AMQP_URI_FORMAT,
+ properties.getNamespace() + "." + properties.getProfile().getEnvironment().getServiceBusDomainName(),
+ properties.getIdleTimeout().toMillis());
+ factory = factoryClass.getConstructor(String.class).newInstance(remoteUrl);
+ } else {
+ ServiceBusConnectionString serviceBusConnectionString = new ServiceBusConnectionString(properties.getConnectionString());
+ String host = serviceBusConnectionString.getEndpointUri().getHost();
- String remoteUrl = String.format(AMQP_URI_FORMAT, host, properties.getIdleTimeout().toMillis());
- String username = serviceBusConnectionString.getSharedAccessKeyName();
- String password = serviceBusConnectionString.getSharedAccessKey();
+ String remoteUrl = String.format(AMQP_URI_FORMAT, host, properties.getIdleTimeout().toMillis());
+ String username = serviceBusConnectionString.getSharedAccessKeyName();
+ String password = serviceBusConnectionString.getSharedAccessKey();
- if (StringUtils.hasLength(username) && StringUtils.hasLength(password)) {
- factory = factoryClass.getConstructor(String.class, String.class, String.class)
- .newInstance(username, password, remoteUrl);
- } else {
- factory = factoryClass.getConstructor(String.class).newInstance(remoteUrl);
+ if (StringUtils.hasLength(username) && StringUtils.hasLength(password)) {
+ factory = factoryClass.getConstructor(String.class, String.class, String.class)
+ .newInstance(username, password, remoteUrl);
+ } else {
+ factory = factoryClass.getConstructor(String.class).newInstance(remoteUrl);
+ }
}
-
return factory;
} catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
throw new IllegalStateException("Unable to create JmsConnectionFactory", ex);
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/jms/ServiceBusJmsPasswordlessConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/jms/ServiceBusJmsPasswordlessConfiguration.java
new file mode 100644
index 0000000000000..537eba723c89f
--- /dev/null
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/jms/ServiceBusJmsPasswordlessConfiguration.java
@@ -0,0 +1,40 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.azure.spring.cloud.autoconfigure.jms;
+
+import com.azure.spring.cloud.autoconfigure.jms.properties.AzureServiceBusJmsProperties;
+import org.apache.qpid.jms.JmsConnectionExtensions;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * {@link EnableAutoConfiguration Auto-configuration} for Azure Service Bus JMS passwordless support.
+ *
+ * @since 4.7.0
+ */
+@Configuration(proxyBeanMethods = false)
+@ConditionalOnProperty(value = "spring.jms.servicebus.passwordless-enabled", havingValue = "true")
+class ServiceBusJmsPasswordlessConfiguration {
+
+ @Bean
+ @ConditionalOnMissingBean
+ AzureServiceBusJmsCredentialSupplier azureServiceBusJmsCredentialSupplier(AzureServiceBusJmsProperties azureServiceBusJmsProperties) {
+ return new AzureServiceBusJmsCredentialSupplier(azureServiceBusJmsProperties.toPasswordlessProperties());
+ }
+
+ @Bean
+ ServiceBusJmsConnectionFactoryCustomizer jmsAADAuthenticationCustomizer(AzureServiceBusJmsCredentialSupplier credentialSupplier) {
+ return factory -> {
+ factory.setExtension(JmsConnectionExtensions.USERNAME_OVERRIDE.toString(), (connection, uri) -> "$jwt");
+ factory.setExtension(JmsConnectionExtensions.PASSWORD_OVERRIDE.toString(), (connection, uri) ->
+ credentialSupplier.get()
+ );
+ };
+ }
+
+
+}
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/jms/properties/AzureServiceBusJmsProperties.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/jms/properties/AzureServiceBusJmsProperties.java
index a2abbd9f23de4..881b309b4c19b 100644
--- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/jms/properties/AzureServiceBusJmsProperties.java
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/jms/properties/AzureServiceBusJmsProperties.java
@@ -3,6 +3,9 @@
package com.azure.spring.cloud.autoconfigure.jms.properties;
+import com.azure.spring.cloud.autoconfigure.properties.core.authentication.TokenCredentialConfigurationProperties;
+import com.azure.spring.cloud.autoconfigure.properties.core.profile.AzureProfileConfigurationProperties;
+import com.azure.spring.cloud.core.properties.PasswordlessProperties;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.autoconfigure.jms.JmsPoolConnectionFactoryProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
@@ -11,23 +14,61 @@
import org.springframework.util.StringUtils;
import java.time.Duration;
+import java.util.HashMap;
+import java.util.Map;
/**
* {@link ConfigurationProperties} for configuring Azure Service Bus JMS.
*/
@ConfigurationProperties(prefix = AzureServiceBusJmsProperties.PREFIX)
-public class AzureServiceBusJmsProperties implements InitializingBean {
+public class AzureServiceBusJmsProperties implements InitializingBean, PasswordlessProperties {
/**
* Service Bus JMS properties prefix.
*/
public static final String PREFIX = "spring.jms.servicebus";
+ private static final String SERVICE_BUS_SCOPE_AZURE = "https://servicebus.azure.net/.default";
+ private static final String SERVICE_BUS_SCOPE_AZURE_CHINA = SERVICE_BUS_SCOPE_AZURE;
+ private static final String SERVICE_BUS_SCOPE_AZURE_GERMANY = SERVICE_BUS_SCOPE_AZURE;
+ private static final String SERVICE_BUS_SCOPE_AZURE_US_GOVERNMENT = SERVICE_BUS_SCOPE_AZURE;
+
+ private static final Map SERVICEBUS_SCOPE_MAP = new HashMap() {
+ {
+ put(CloudType.AZURE, SERVICE_BUS_SCOPE_AZURE);
+ put(CloudType.AZURE_CHINA, SERVICE_BUS_SCOPE_AZURE_CHINA);
+ put(CloudType.AZURE_GERMANY, SERVICE_BUS_SCOPE_AZURE_GERMANY);
+ put(CloudType.AZURE_US_GOVERNMENT, SERVICE_BUS_SCOPE_AZURE_US_GOVERNMENT);
+ }
+ };
+
+ private AzureProfileConfigurationProperties profile = new AzureProfileConfigurationProperties();
+
+ /**
+ * The scopes required for the access token.
+ */
+ private String scopes;
+
+ private TokenCredentialConfigurationProperties credential = new TokenCredentialConfigurationProperties();
+
+ /**
+ * Whether to enable supporting azure identity token credentials.
+ *
+ * If the value is true, then 'spring.jms.servicebus.namespace' must be set.
+ * If the passwordlessEnabled is true, it will try to authenticate connections with Azure AD.
+ */
+ private boolean passwordlessEnabled = false;
+
/**
* Whether to enable Servive Bus JMS autoconfiguration.
*/
private boolean enabled = true;
+ /**
+ * The Service Bus namespace.
+ */
+ private String namespace;
+
/**
* Connection string to connect to a Service Bus namespace.
*/
@@ -79,16 +120,16 @@ public JmsPoolConnectionFactoryProperties getPool() {
}
/**
- * Get the connection string to connect to a Service Bus namesapce.
- * @return the connection string to connect to a Service Bus namesapce.
+ * Get the connection string to connect to a Service Bus namespace.
+ * @return the connection string to connect to a Service Bus namespace.
*/
public String getConnectionString() {
return connectionString;
}
/**
- * Set the connection string to connect to a Service Bus namesapce.
- * @param connectionString the connection string to connect to a Service Bus namesapce.
+ * Set the connection string to connect to a Service Bus namespace.
+ * @param connectionString the connection string to connect to a Service Bus namespace.
*/
public void setConnectionString(String connectionString) {
this.connectionString = connectionString;
@@ -158,6 +199,97 @@ public PrefetchPolicy getPrefetchPolicy() {
return prefetchPolicy;
}
+ /**
+ * Get the Service Bus namespace.
+ * @return the Service Bus namespace.
+ */
+ public String getNamespace() {
+ return namespace;
+ }
+
+ /**
+ * Set the Service Bus namespace.
+ * @param namespace the Service Bus namespace.
+ */
+ public void setNamespace(String namespace) {
+ this.namespace = namespace;
+ }
+
+ /**
+ * Get the scopes required for the access token.
+ *
+ * @return scopes required for the access token
+ */
+ @Override
+ public String getScopes() {
+ return this.scopes == null ? getDefaultScopes() : this.scopes;
+ }
+
+ /**
+ * Set the scopes required for the access token.
+ *
+ * @param scopes the scopes required for the access token
+ */
+ public void setScopes(String scopes) {
+ this.scopes = scopes;
+ }
+
+ /**
+ * Whether to enable connections authenticating with Azure AD, default is false.
+ *
+ * @return enable connections authenticating with Azure AD if true, otherwise false.
+ */
+ @Override
+ public boolean isPasswordlessEnabled() {
+ return passwordlessEnabled;
+ }
+
+ /**
+ * Set the value to enable/disable connections authenticating with Azure AD.
+ * If not set, by default the value is false.
+ *
+ * @param passwordlessEnabled the passwordlessEnabled
+ */
+ public void setPasswordlessEnabled(boolean passwordlessEnabled) {
+ this.passwordlessEnabled = passwordlessEnabled;
+ }
+
+ /**
+ * Get the profile
+ * @return the profile
+ */
+ @Override
+ public AzureProfileConfigurationProperties getProfile() {
+ return profile;
+ }
+
+ /**
+ * Set the profile
+ * @param profile the profile properties related to an Azure subscription
+ */
+ public void setProfile(AzureProfileConfigurationProperties profile) {
+ this.profile = profile;
+ }
+
+ /**
+ * Get the credential properties.
+ *
+ * @return the credential properties.
+ */
+ @Override
+ public TokenCredentialConfigurationProperties getCredential() {
+ return credential;
+ }
+
+ /**
+ * Set the credential properties.
+ *
+ * @param credential the credential properties
+ */
+ public void setCredential(TokenCredentialConfigurationProperties credential) {
+ this.credential = credential;
+ }
+
/**
* Validate spring.jms.servicebus related properties.
*
@@ -165,8 +297,14 @@ public PrefetchPolicy getPrefetchPolicy() {
*/
@Override
public void afterPropertiesSet() throws Exception {
- if (!StringUtils.hasText(connectionString)) {
- throw new IllegalArgumentException("'spring.jms.servicebus.connection-string' should be provided");
+ if (isPasswordlessEnabled()) {
+ if (!StringUtils.hasText(namespace)) {
+ throw new IllegalArgumentException("Passwordless connections enabled, 'spring.jms.servicebus.namespace' should be provided.");
+ }
+ } else {
+ if (!StringUtils.hasText(connectionString)) {
+ throw new IllegalArgumentException("'spring.jms.servicebus.connection-string' should be provided.");
+ }
}
if (null == pricingTier || !pricingTier.matches("(?i)premium|standard|basic")) {
@@ -413,4 +551,8 @@ public void setPhase(Integer phase) {
this.phase = phase;
}
}
+
+ private String getDefaultScopes() {
+ return SERVICEBUS_SCOPE_MAP.getOrDefault(getProfile().getCloudType(), SERVICE_BUS_SCOPE_AZURE);
+ }
}
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessAutoConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessAutoConfiguration.java
index c3d1f2f6c36e4..20138c1c79856 100644
--- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessAutoConfiguration.java
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessAutoConfiguration.java
@@ -6,6 +6,7 @@
import com.azure.spring.cloud.autoconfigure.context.AzureGlobalProperties;
import com.azure.spring.cloud.autoconfigure.implementation.redis.passwordless.data.jedis.AzureJedisConnectionFactory;
import com.azure.spring.cloud.autoconfigure.implementation.redis.passwordless.data.jedis.AzureRedisCredentialSupplier;
+import com.azure.spring.cloud.core.implementation.util.AzurePasswordlessPropertiesUtils;
import com.azure.spring.cloud.service.implementation.passwordless.AzureRedisPasswordlessProperties;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
@@ -24,11 +25,8 @@
import org.springframework.data.redis.connection.jedis.JedisConnection;
import redis.clients.jedis.Jedis;
-import java.util.Properties;
-
import static com.azure.spring.cloud.autoconfigure.redis.AzureJedisPasswordlessUtil.getJedisClientConfiguration;
import static com.azure.spring.cloud.autoconfigure.redis.AzureJedisPasswordlessUtil.getStandaloneConfig;
-import static com.azure.spring.cloud.autoconfigure.redis.AzureJedisPasswordlessUtil.mergeAzureProperties;
/**
* Azure Redis passwordless connection configuration using Jedis.
@@ -51,9 +49,8 @@ AzureRedisPasswordlessProperties redisPasswordlessProperties() {
@Bean
@ConditionalOnMissingBean
- AzureRedisCredentialSupplier azureRedisCredentialSupplier(AzureGlobalProperties azureGlobalProperties, AzureRedisPasswordlessProperties azureRedisPasswordlessProperties) {
- Properties properties = mergeAzureProperties(azureGlobalProperties, azureRedisPasswordlessProperties).toProperties();
- return new AzureRedisCredentialSupplier(properties);
+ AzureRedisCredentialSupplier azureRedisCredentialSupplier(AzureRedisPasswordlessProperties azureRedisPasswordlessProperties, AzureGlobalProperties azureGlobalProperties) {
+ return new AzureRedisCredentialSupplier(mergeAzureProperties(azureGlobalProperties, azureRedisPasswordlessProperties).toPasswordlessProperties());
}
@Bean
@@ -61,8 +58,13 @@ AzureRedisCredentialSupplier azureRedisCredentialSupplier(AzureGlobalProperties
AzureJedisConnectionFactory azureRedisConnectionFactory(RedisProperties redisProperties, AzureRedisCredentialSupplier azureRedisCredentialSupplier) {
RedisStandaloneConfiguration standaloneConfig = getStandaloneConfig(redisProperties);
JedisClientConfiguration clientConfiguration = getJedisClientConfiguration(redisProperties);
-
return new AzureJedisConnectionFactory(standaloneConfig, clientConfiguration, azureRedisCredentialSupplier);
}
+ private AzureRedisPasswordlessProperties mergeAzureProperties(AzureGlobalProperties azureGlobalProperties, AzureRedisPasswordlessProperties azurePasswordlessProperties) {
+ AzureRedisPasswordlessProperties mergedProperties = new AzureRedisPasswordlessProperties();
+ AzurePasswordlessPropertiesUtils.mergeAzureCommonProperties(azureGlobalProperties, azurePasswordlessProperties, mergedProperties);
+ return mergedProperties;
+ }
+
}
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessUtil.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessUtil.java
index e9164c0510797..fc101cc2aa50b 100644
--- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessUtil.java
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessUtil.java
@@ -3,9 +3,6 @@
package com.azure.spring.cloud.autoconfigure.redis;
-import com.azure.spring.cloud.autoconfigure.context.AzureGlobalProperties;
-import com.azure.spring.cloud.core.implementation.util.AzurePropertiesUtils;
-import com.azure.spring.cloud.service.implementation.passwordless.AzureRedisPasswordlessProperties;
import org.springframework.boot.autoconfigure.data.redis.RedisProperties;
import org.springframework.boot.context.properties.PropertyMapper;
import org.springframework.data.redis.connection.RedisPassword;
@@ -30,13 +27,6 @@ final class AzureJedisPasswordlessUtil {
private AzureJedisPasswordlessUtil() {
}
- static AzureRedisPasswordlessProperties mergeAzureProperties(AzureGlobalProperties azureGlobalProperties, AzureRedisPasswordlessProperties redisPasswordlessProperties) {
- AzureRedisPasswordlessProperties mergedProperties = new AzureRedisPasswordlessProperties();
- AzurePropertiesUtils.mergeAzureCommonProperties(azureGlobalProperties, redisPasswordlessProperties, mergedProperties);
- mergedProperties.setScopes(redisPasswordlessProperties.getScopes());
- return mergedProperties;
- }
-
static JedisClientConfiguration getJedisClientConfiguration(RedisProperties redisProperties) {
JedisClientConfiguration.JedisClientConfigurationBuilder builder = applyProperties(redisProperties, JedisClientConfiguration.builder());
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json
index ffb8615299244..8147e9658a53d 100644
--- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json
@@ -1536,136 +1536,136 @@
"name": "spring.datasource.azure.credential.client-id",
"type": "java.lang.String",
"description": "Client ID to use when performing service principal authentication with Azure.",
- "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties"
+ "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzureJdbcPasswordlessProperties"
},
{
"name": "spring.datasource.azure.credential.client-secret",
"type": "java.lang.String",
"description": "Client secret to use when performing service principal authentication with Azure.",
- "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties"
+ "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzureJdbcPasswordlessProperties"
},
{
"name": "spring.datasource.azure.credential.client-certificate-password",
"type": "java.lang.String",
"description": "Password of the certificate file.",
- "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties"
+ "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzureJdbcPasswordlessProperties"
},
{
"name": "spring.datasource.azure.credential.client-certificate-path",
"type": "java.lang.String",
"description": "Path of a PEM certificate file to use when performing service principal authentication with Azure.",
- "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties"
+ "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzureJdbcPasswordlessProperties"
},
{
"name": "spring.datasource.azure.credential.username",
"type": "java.lang.String",
"description": "Username to use when performing username\/password authentication with Azure.",
- "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties"
+ "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzureJdbcPasswordlessProperties"
},
{
"name": "spring.datasource.azure.credential.password",
"type": "java.lang.String",
"description": "Password to use when performing username\/password authentication with Azure.",
- "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties"
+ "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzureJdbcPasswordlessProperties"
},
{
"name": "spring.datasource.azure.credential.managed-identity-enabled",
"type": "java.lang.Boolean",
"description": "Whether to enable managed identity to authenticate with Azure. If true and the client-id is set, will use the client ID as user assigned managed identity client ID.",
- "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties",
+ "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzureJdbcPasswordlessProperties",
"defaultValue": false
},
{
"name": "spring.datasource.azure.profile.environment.active-directory-endpoint",
"type": "java.lang.String",
"description": "The Azure Active Directory endpoint to connect to.",
- "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties"
+ "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzureJdbcPasswordlessProperties"
},
{
"name": "spring.datasource.azure.profile.tenant-id",
"type": "java.lang.String",
"description": "Tenant ID for Azure resources.",
- "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties"
+ "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzureJdbcPasswordlessProperties"
},
{
"name": "spring.datasource.azure.profile.cloud-type",
"type": "java.lang.String",
"description": "Name of the Azure cloud to connect to. Supported types are: AZURE, AZURE_CHINA, AZURE_GERMANY, AZURE_US_GOVERNMENT, OTHER. The default value is `AZURE`.",
- "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties"
+ "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzureJdbcPasswordlessProperties"
},
{
"name": "spring.datasource.azure.passwordless-enabled",
"type": "java.lang.Boolean",
"description": "Whether to enable passwordless connections to Azure databases by using OAuth2 Azure Active Directory token credentials.",
- "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties",
+ "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzureJdbcPasswordlessProperties",
"defaultValue": false
},
{
"name": "spring.redis.azure.credential.client-id",
"type": "java.lang.String",
"description": "Client ID to use when performing service principal authentication with Azure.",
- "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties"
+ "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzureRedisPasswordlessProperties"
},
{
"name": "spring.redis.azure.credential.client-secret",
"type": "java.lang.String",
"description": "Client secret to use when performing service principal authentication with Azure.",
- "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties"
+ "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzureRedisPasswordlessProperties"
},
{
"name": "spring.redis.azure.credential.client-certificate-password",
"type": "java.lang.String",
"description": "Password of the certificate file.",
- "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties"
+ "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzureRedisPasswordlessProperties"
},
{
"name": "spring.redis.azure.credential.client-certificate-path",
"type": "java.lang.String",
"description": "Path of a PEM certificate file to use when performing service principal authentication with Azure.",
- "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties"
+ "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzureRedisPasswordlessProperties"
},
{
"name": "spring.redis.azure.credential.username",
"type": "java.lang.String",
"description": "Username to use when performing username\/password authentication with Azure.",
- "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties"
+ "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzureRedisPasswordlessProperties"
},
{
"name": "spring.redis.azure.credential.password",
"type": "java.lang.String",
"description": "Password to use when performing username\/password authentication with Azure.",
- "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties"
+ "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzureRedisPasswordlessProperties"
},
{
"name": "spring.redis.azure.credential.managed-identity-enabled",
"type": "java.lang.Boolean",
"description": "Whether to enable managed identity to authenticate with Azure. If true and the client-id is set, will use the client ID as user assigned managed identity client ID.",
- "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties",
+ "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzureRedisPasswordlessProperties",
"defaultValue": false
},
{
"name": "spring.redis.azure.profile.environment.active-directory-endpoint",
"type": "java.lang.String",
"description": "The Azure Active Directory endpoint to connect to.",
- "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties"
+ "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzureRedisPasswordlessProperties"
},
{
"name": "spring.redis.azure.profile.tenant-id",
"type": "java.lang.String",
"description": "Tenant ID for Azure resources.",
- "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties"
+ "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzureRedisPasswordlessProperties"
},
{
"name": "spring.redis.azure.profile.cloud-type",
"type": "java.lang.String",
"description": "Name of the Azure cloud to connect to. Supported types are: AZURE, AZURE_CHINA, AZURE_GERMANY, AZURE_US_GOVERNMENT, OTHER. The default value is `AZURE`.",
- "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties"
+ "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzureRedisPasswordlessProperties"
},
{
"name": "spring.redis.azure.passwordless-enabled",
"type": "java.lang.Boolean",
"description": "Whether to enable passwordless connections to Azure Redis Cache by using OAuth2 Azure Active Directory token credentials.",
- "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties",
+ "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzureRedisPasswordlessProperties",
"defaultValue": false
},
{
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/resources/META-INF/spring.factories b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/resources/META-INF/spring.factories
index cd77a65a88225..1e9972501de1f 100644
--- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/resources/META-INF/spring.factories
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/resources/META-INF/spring.factories
@@ -1,7 +1,8 @@
org.springframework.boot.env.EnvironmentPostProcessor=com.azure.spring.cloud.autoconfigure.cloudfoundry.environment.VcapProcessor,\
com.azure.spring.cloud.autoconfigure.keyvault.environment.KeyVaultEnvironmentPostProcessor,\
com.azure.spring.cloud.autoconfigure.cloudfoundry.AzureCloudFoundryEnvironmentPostProcessor,\
-com.azure.spring.cloud.autoconfigure.context.AzureGlobalConfigurationEnvironmentPostProcessor
+com.azure.spring.cloud.autoconfigure.context.AzureGlobalConfigurationEnvironmentPostProcessor,\
+com.azure.spring.cloud.autoconfigure.implementation.passwordless.AzurePasswordlessEnvironmentPostProcessor
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.azure.spring.cloud.autoconfigure.aad.AadAutoConfiguration,\
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/jdbc/AbstractAzureJdbcAutoConfigurationTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/jdbc/AbstractAzureJdbcAutoConfigurationTest.java
index 7af077e0d265c..3326ddd16ec86 100644
--- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/jdbc/AbstractAzureJdbcAutoConfigurationTest.java
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/jdbc/AbstractAzureJdbcAutoConfigurationTest.java
@@ -21,7 +21,7 @@
abstract class AbstractAzureJdbcAutoConfigurationTest {
public static final String PUBLIC_AUTHORITY_HOST_STRING = AuthProperty.AUTHORITY_HOST.getPropertyKey() + "=" + "https://login.microsoftonline.com/";
-
+ public static final String PUBLIC_TOKEN_CREDENTIAL_BEAN_NAME_STRING = AuthProperty.TOKEN_CREDENTIAL_BEAN_NAME.getPropertyKey() + "=" + "passwordlessTokenCredential";
abstract void pluginNotOnClassPath();
abstract void wrongJdbcUrl();
abstract void enhanceUrlWithDefaultCredential();
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/jdbc/JdbcPropertiesBeanPostProcessorTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/jdbc/JdbcPropertiesBeanPostProcessorTest.java
index b88603d293f1f..d4e1078c038e3 100644
--- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/jdbc/JdbcPropertiesBeanPostProcessorTest.java
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/jdbc/JdbcPropertiesBeanPostProcessorTest.java
@@ -35,6 +35,7 @@ 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";
private static final String POSTGRESQL_ASSUME_MIN_SERVER_VERSION = POSTGRESQL_PROPERTY_NAME_ASSUME_MIN_SERVER_VERSION + "="
+ POSTGRESQL_PROPERTY_VALUE_ASSUME_MIN_SERVER_VERSION;
@@ -98,6 +99,7 @@ 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()
);
@@ -122,7 +124,8 @@ void shouldGetCloudTypeFromAzureUsGov() {
MYSQL_CONNECTION_STRING,
MYSQL_USER_AGENT,
AuthProperty.TOKEN_CREDENTIAL_PROVIDER_CLASS_NAME.getPropertyKey() + "=" + SpringTokenCredentialProvider.class.getName(),
- US_AUTHORITY_HOST_STRING
+ US_AUTHORITY_HOST_STRING,
+ PUBLIC_TOKEN_CREDENTIAL_BEAN_NAME_STRING
);
assertEquals(expectedJdbcUrl, dataSourceProperties.getUrl());
@@ -139,6 +142,7 @@ 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()
);
@@ -160,7 +164,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()
+ AuthProperty.TOKEN_CREDENTIAL_PROVIDER_CLASS_NAME.getPropertyKey() + "=" + SpringTokenCredentialProvider.class.getName(),
+ PUBLIC_TOKEN_CREDENTIAL_BEAN_NAME_STRING
);
assertEquals(expectedJdbcUrl, dataSourceProperties.getUrl());
@@ -180,7 +185,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()
+ AuthProperty.TOKEN_CREDENTIAL_PROVIDER_CLASS_NAME.getPropertyKey() + "=" + SpringTokenCredentialProvider.class.getName(),
+ PUBLIC_TOKEN_CREDENTIAL_BEAN_NAME_STRING
);
assertEquals(expectedJdbcUrl, dataSourceProperties.getUrl());
}
@@ -199,6 +205,7 @@ void postgreSqlUserAgentShouldConfigureIfNonApplicationNameProvided() {
baseUrl,
AuthProperty.TOKEN_CREDENTIAL_PROVIDER_CLASS_NAME.getPropertyKey() + "=" + SpringTokenCredentialProvider.class.getName(),
APPLICATION_NAME.getName() + "=" + AzureSpringIdentifier.AZURE_SPRING_POSTGRESQL_OAUTH,
+ PUBLIC_TOKEN_CREDENTIAL_BEAN_NAME_STRING,
POSTGRESQL_ASSUME_MIN_SERVER_VERSION
);
@@ -220,6 +227,7 @@ void postgreSqlUserAgentShouldNotConfigureIfApplicationNameExists() {
DatabaseType.POSTGRESQL,
baseUrl,
AuthProperty.TOKEN_CREDENTIAL_PROVIDER_CLASS_NAME.getPropertyKey() + "=" + SpringTokenCredentialProvider.class.getName(),
+ PUBLIC_TOKEN_CREDENTIAL_BEAN_NAME_STRING,
POSTGRESQL_ASSUME_MIN_SERVER_VERSION
);
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/jdbc/JdbcPropertiesBeanPostProcessorWithApplicationContextRunnerTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/jdbc/JdbcPropertiesBeanPostProcessorWithApplicationContextRunnerTest.java
index 0a112b792a9b3..3065375e4f677 100644
--- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/jdbc/JdbcPropertiesBeanPostProcessorWithApplicationContextRunnerTest.java
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/jdbc/JdbcPropertiesBeanPostProcessorWithApplicationContextRunnerTest.java
@@ -11,7 +11,7 @@
import com.azure.spring.cloud.autoconfigure.implementation.jdbc.DatabaseType;
import com.azure.spring.cloud.autoconfigure.implementation.jdbc.SpringTokenCredentialProviderContextProvider;
import com.azure.spring.cloud.service.implementation.identity.credential.provider.SpringTokenCredentialProvider;
-import com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties;
+import com.azure.spring.cloud.service.implementation.passwordless.AzureJdbcPasswordlessProperties;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
@@ -30,12 +30,12 @@ class JdbcPropertiesBeanPostProcessorWithApplicationContextRunnerTest {
private static final String MYSQL_CONNECTION_STRING = "jdbc:mysql://host/database?enableSwitch1&property1=value1";
private static final String PUBLIC_AUTHORITY_HOST_STRING = AuthProperty.AUTHORITY_HOST.getPropertyKey() + "=" + "https://login.microsoftonline.com/";
-
+ public static final String PUBLIC_TOKEN_CREDENTIAL_BEAN_NAME_STRING = AuthProperty.TOKEN_CREDENTIAL_BEAN_NAME.getPropertyKey() + "=" + "passwordlessTokenCredential";
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(AzureJdbcAutoConfiguration.class,
DataSourceProperties.class,
- AzurePasswordlessProperties.class,
+ AzureJdbcPasswordlessProperties.class,
AzureGlobalPropertiesAutoConfiguration.class,
AzureTokenCredentialAutoConfiguration.class));
@@ -75,6 +75,7 @@ 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()
@@ -114,7 +115,7 @@ void testBindSpringBootProperties() {
assertThat(context).hasSingleBean(SpringTokenCredentialProviderContextProvider.class);
ConfigurableEnvironment environment = context.getEnvironment();
- AzurePasswordlessProperties properties = Binder.get(environment).bindOrCreate("spring.datasource.azure", AzurePasswordlessProperties.class);
+ AzureJdbcPasswordlessProperties properties = Binder.get(environment).bindOrCreate("spring.datasource.azure", AzureJdbcPasswordlessProperties.class);
assertNotEquals("azure-client-id", properties.getCredential().getClientId());
assertEquals("fake-jdbc-client-id", properties.getCredential().getClientId());
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/jdbc/MySqlAzureJdbcAutoConfigurationTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/jdbc/MySqlAzureJdbcAutoConfigurationTest.java
index 8040af547a8db..d4470b51112c8 100644
--- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/jdbc/MySqlAzureJdbcAutoConfigurationTest.java
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/jdbc/MySqlAzureJdbcAutoConfigurationTest.java
@@ -84,6 +84,7 @@ void enhanceUrlWithDefaultCredential() {
DatabaseType.MYSQL,
false,
connectionString,
+ PUBLIC_TOKEN_CREDENTIAL_BEAN_NAME_STRING,
PUBLIC_AUTHORITY_HOST_STRING,
AUTHPROPERTY_TOKENCREDENTIALPROVIDERCLASSNAME_PROPERTY,
MYSQL_USER_AGENT
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/jdbc/PostgreSqlAzureJdbcAutoConfigurationTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/jdbc/PostgreSqlAzureJdbcAutoConfigurationTest.java
index 2395ee9d501f4..722ebc7f6997b 100644
--- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/jdbc/PostgreSqlAzureJdbcAutoConfigurationTest.java
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/jdbc/PostgreSqlAzureJdbcAutoConfigurationTest.java
@@ -72,6 +72,7 @@ void enhanceUrlWithDefaultCredential() {
DatabaseType.POSTGRESQL,
false,
connectionString,
+ PUBLIC_TOKEN_CREDENTIAL_BEAN_NAME_STRING,
PUBLIC_AUTHORITY_HOST_STRING,
POSTGRESQL_USER_AGENT,
AUTHPROPERTY_TOKENCREDENTIALPROVIDERCLASSNAME_PROPERTY,
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/jms/AzureServiceBusJmsPropertiesTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/jms/AzureServiceBusJmsPropertiesTests.java
index 2001b29260590..e198123d55a96 100644
--- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/jms/AzureServiceBusJmsPropertiesTests.java
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/jms/AzureServiceBusJmsPropertiesTests.java
@@ -8,6 +8,10 @@
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.NullAndEmptySource;
import org.junit.jupiter.params.provider.ValueSource;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+import org.springframework.context.annotation.Bean;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -16,13 +20,16 @@ class AzureServiceBusJmsPropertiesTests {
static final String CONNECTION_STRING = "Endpoint=sb://host/;SharedAccessKeyName=sasKeyName;"
+ "SharedAccessKey=sasKey";
+ private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
+ .withUserConfiguration(AzureServiceBusJmsPropertiesTestConfig.class);
+
@Test
void connectionStringNotValid() {
AzureServiceBusJmsProperties prop = new AzureServiceBusJmsProperties();
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.";
String actualMessage = ex.getMessage();
System.out.println("message:" + actualMessage);
assertTrue(actualMessage.contains(expectedMessage));
@@ -30,7 +37,7 @@ void connectionStringNotValid() {
@ParameterizedTest
@NullAndEmptySource
- @ValueSource(strings = { "xx" })
+ @ValueSource(strings = {"xx"})
void pricingTierNotValid(String pricingTier) {
AzureServiceBusJmsProperties prop = new AzureServiceBusJmsProperties();
prop.setConnectionString(CONNECTION_STRING);
@@ -44,4 +51,26 @@ void pricingTierNotValid(String pricingTier) {
assertTrue(actualMessage.contains(expectedMessage));
}
+ @Test
+ void testPasswordlessEnabled() {
+ contextRunner
+ .withPropertyValues("spring.jms.servicebus.passwordless-enabled=true")
+ .run(context -> {
+ Exception ex = assertThrows(IllegalStateException.class, () -> context.getBean(AzureServiceBusJmsProperties.class));
+ String actualMessage = ex.getCause().getMessage();
+ String expectedMessage = "Passwordless connections enabled, 'spring.jms.servicebus.namespace' should be provided.";
+ assertTrue(actualMessage.contains(expectedMessage));
+ });
+ }
+
+ @EnableConfigurationProperties
+ static class AzureServiceBusJmsPropertiesTestConfig {
+
+ @Bean
+ @ConfigurationProperties(AzureServiceBusJmsProperties.PREFIX)
+ AzureServiceBusJmsProperties jmsProperties() {
+ return new AzureServiceBusJmsProperties();
+ }
+ }
+
}
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/jms/ServiceBusJmsAutoConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/jms/ServiceBusJmsAutoConfigurationTests.java
index 53857f90d8510..7115e8e0773c8 100644
--- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/jms/ServiceBusJmsAutoConfigurationTests.java
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/jms/ServiceBusJmsAutoConfigurationTests.java
@@ -3,6 +3,7 @@
package com.azure.spring.cloud.autoconfigure.jms;
+import com.azure.spring.cloud.autoconfigure.context.AzureGlobalProperties;
import com.azure.spring.cloud.autoconfigure.jms.properties.AzureServiceBusJmsProperties;
import com.azure.spring.cloud.core.provider.connectionstring.StaticConnectionStringProvider;
import com.azure.spring.cloud.core.service.AzureServiceType;
@@ -42,6 +43,7 @@ class ServiceBusJmsAutoConfigurationTests {
+ "SharedAccessKey=sasKey";
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
+ .withBean(AzureGlobalProperties.class, () -> new AzureGlobalProperties())
.withConfiguration(AutoConfigurations.of(JmsAutoConfiguration.class, ServiceBusJmsAutoConfiguration.class));
private void testQueueJmsListenerContainerFactoryWithCustomSettings(AssertableApplicationContext loaded) {
@@ -127,7 +129,6 @@ void autoconfigurationEnabledAndContextSuccessWithNonpremiumTier(String pricingT
assertThat(context).hasSingleBean(ConnectionFactory.class);
assertThat(context).hasSingleBean(JmsTemplate.class);
assertThat(context).hasSingleBean(DefaultJmsListenerContainerFactoryConfigurer.class);
- assertThat(context).hasBean("jmsListenerContainerFactory");
assertThat(context).hasBean("topicJmsListenerContainerFactory");
assertThat(context).doesNotHaveBean("amqpOpenPropertiesCustomizer");
});
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/jms/ServiceBusJmsPasswordlessConfigurationTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/jms/ServiceBusJmsPasswordlessConfigurationTest.java
new file mode 100644
index 0000000000000..bcbba7d264be1
--- /dev/null
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/jms/ServiceBusJmsPasswordlessConfigurationTest.java
@@ -0,0 +1,147 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.azure.spring.cloud.autoconfigure.jms;
+
+import com.azure.spring.cloud.autoconfigure.context.AzureGlobalProperties;
+import com.azure.spring.cloud.autoconfigure.jms.properties.AzureServiceBusJmsProperties;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.mockito.MockedConstruction;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Import;
+
+import static com.azure.spring.cloud.core.provider.AzureProfileOptionsProvider.CloudType.AZURE;
+import static com.azure.spring.cloud.core.provider.AzureProfileOptionsProvider.CloudType.AZURE_CHINA;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mockConstruction;
+import static org.mockito.Mockito.when;
+
+class ServiceBusJmsPasswordlessConfigurationTest {
+ private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
+ .withUserConfiguration(ServiceBusJmsPasswordlessTestConfig.class);
+
+ @Test
+ void testPropertyEnabledIsFalse() {
+ AzureGlobalProperties azureProperties = new AzureGlobalProperties();
+ azureProperties.getProfile().setCloudType(AZURE);
+ azureProperties.getProfile().getEnvironment().setActiveDirectoryEndpoint("abc");
+ azureProperties.getProfile().getEnvironment().setActiveDirectoryGraphApiVersion("v2");
+
+ this.contextRunner
+ .withPropertyValues("spring.jms.servicebus.passwordless-enabled=false")
+ .withPropertyValues("spring.jms.servicebus.connection-string=fake-connection-string")
+ .withPropertyValues("spring.jms.servicebus.pricing-tier=standard")
+ .withBean(AzureGlobalProperties.class, () -> azureProperties)
+ .run(context -> {
+ assertThat(context).doesNotHaveBean(AzureServiceBusJmsCredentialSupplier.class);
+ assertThat(context).doesNotHaveBean(ServiceBusJmsConnectionFactoryCustomizer.class);
+ });
+ }
+
+ @Test
+ void testPropertyEnabledIsTrue() {
+ AzureGlobalProperties azureProperties = new AzureGlobalProperties();
+ azureProperties.getProfile().setCloudType(AZURE);
+ azureProperties.getProfile().getEnvironment().setActiveDirectoryEndpoint("abc");
+ azureProperties.getProfile().getEnvironment().setActiveDirectoryGraphApiVersion("v2");
+
+ this.contextRunner
+ .withPropertyValues("spring.jms.servicebus.passwordless-enabled=true")
+ .withPropertyValues("spring.jms.servicebus.namespace=testnamespace")
+ .withPropertyValues("spring.jms.servicebus.pricing-tier=standard")
+ .withBean(AzureGlobalProperties.class, () -> azureProperties)
+ .run(context -> {
+ assertThat(context).hasSingleBean(AzureServiceBusJmsCredentialSupplier.class);
+ assertThat(context).hasSingleBean(ServiceBusJmsConnectionFactoryCustomizer.class);
+ });
+ }
+
+ @Test
+ void testServiceBusPasswordlessProperties() {
+ AzureGlobalProperties azureProperties = new AzureGlobalProperties();
+ azureProperties.getProfile().setCloudType(AZURE);
+ azureProperties.getProfile().getEnvironment().setActiveDirectoryEndpoint("abc");
+ azureProperties.getProfile().getEnvironment().setActiveDirectoryGraphApiVersion("v2");
+ this.contextRunner
+ .withPropertyValues("spring.jms.servicebus.passwordless-enabled=true")
+ .withPropertyValues("spring.jms.servicebus.namespace=testnamespace")
+ .withPropertyValues("spring.jms.servicebus.pricing-tier=standard")
+ .withPropertyValues("spring.jms.servicebus.scopes=scopes",
+ "spring.jms.servicebus.profile.tenant-id=tenant-id",
+ "spring.jms.servicebus.profile.subscription-id=subscription-id",
+ "spring.jms.servicebus.profile.CloudType=AZURE_CHINA",
+ "spring.jms.servicebus.credential.client-id=client-id",
+ "spring.jms.servicebus.credential.client-secret=secret",
+ "spring.jms.servicebus.credential.client-certificatePath=client-certificatePath",
+ "spring.jms.servicebus.credential.client-certificatePassword=client-certificatePassword",
+ "spring.jms.servicebus.credential.username=username",
+ "spring.jms.servicebus.credential.password=password",
+ "spring.jms.servicebus.credential.managed-identity-enabled=true",
+ "spring.jms.servicebus.client.application-id=application-id",
+ "spring.jms.servicebus.proxy.hostname=hostname",
+ "spring.jms.servicebus.proxy.username=username",
+ "spring.jms.servicebus.proxy.port=1111",
+ "spring.jms.servicebus.proxy.password=password",
+ "spring.jms.servicebus.proxy.type=type")
+ .withBean(AzureGlobalProperties.class, () -> azureProperties)
+ .run(context -> {
+ assertThat(context).hasSingleBean(AzureServiceBusJmsCredentialSupplier.class);
+ assertThat(context).hasSingleBean(ServiceBusJmsConnectionFactoryCustomizer.class);
+ AzureServiceBusJmsProperties properties = context.getBean(AzureServiceBusJmsProperties.class);
+ assertThat(properties.getScopes()).isEqualTo("scopes");
+ assertThat(properties.getProfile().getTenantId()).isEqualTo("tenant-id");
+ assertThat(properties.getProfile().getSubscriptionId()).isEqualTo("subscription-id");
+ assertThat(properties.getProfile().getCloudType()).isEqualTo(AZURE_CHINA);
+ assertThat(properties.getCredential().getClientId()).isEqualTo("client-id");
+ assertThat(properties.getCredential().getClientSecret()).isEqualTo("secret");
+ assertThat(properties.getCredential().getClientCertificatePath()).isEqualTo("client-certificatePath");
+ assertThat(properties.getCredential().getClientCertificatePassword()).isEqualTo("client-certificatePassword");
+ assertThat(properties.getCredential().getUsername()).isEqualTo("username");
+ assertThat(properties.getCredential().getPassword()).isEqualTo("password");
+ assertThat(properties.getCredential().isManagedIdentityEnabled()).isTrue();
+
+ });
+ }
+
+ @Test
+ void testAzureServiceBusJmsCredentialSupplier() {
+ AzureGlobalProperties azureProperties = new AzureGlobalProperties();
+ azureProperties.getProfile().setCloudType(AZURE);
+ azureProperties.getProfile().getEnvironment().setActiveDirectoryEndpoint("abc");
+ azureProperties.getProfile().getEnvironment().setActiveDirectoryGraphApiVersion("v2");
+
+ this.contextRunner
+ .withPropertyValues("spring.jms.servicebus.passwordless-enabled=true")
+ .withBean(AzureGlobalProperties.class, () -> azureProperties);
+
+ try (MockedConstruction supplierMockedConstruction = mockConstruction(AzureServiceBusJmsCredentialSupplier.class,
+ (azureServiceBusJmsCredentialSupplierMocker, mockerContext) -> {
+ when(azureServiceBusJmsCredentialSupplierMocker.get()).thenReturn("fake-token");
+
+ contextRunner.run(runnerContext -> {
+ assertThat(runnerContext).hasSingleBean(AzureServiceBusJmsCredentialSupplier.class);
+ assertThat(runnerContext).hasSingleBean(ServiceBusJmsConnectionFactoryCustomizer.class);
+ runnerContext.getBean(AzureServiceBusJmsCredentialSupplier.class).get().equals("fake-token");
+ });
+
+ })) {
+ Assertions.assertNotNull(supplierMockedConstruction);
+ }
+
+ }
+
+ @EnableConfigurationProperties
+ @Import({ServiceBusJmsPasswordlessConfiguration.class})
+ static class ServiceBusJmsPasswordlessTestConfig {
+
+ @Bean
+ @ConfigurationProperties(AzureServiceBusJmsProperties.PREFIX)
+ AzureServiceBusJmsProperties jmsProperties() {
+ return new AzureServiceBusJmsProperties();
+ }
+ }
+}
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessUtilTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/passwordless/MergeAzureCommonPropertiesTest.java
similarity index 66%
rename from sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessUtilTest.java
rename to sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/passwordless/MergeAzureCommonPropertiesTest.java
index f90da9f7beb0e..dcfacec34df46 100644
--- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessUtilTest.java
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/passwordless/MergeAzureCommonPropertiesTest.java
@@ -1,19 +1,17 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
-package com.azure.spring.cloud.autoconfigure.redis;
+package com.azure.spring.cloud.autoconfigure.passwordless;
import com.azure.spring.cloud.autoconfigure.context.AzureGlobalProperties;
-import com.azure.spring.cloud.core.properties.client.ClientProperties;
-import com.azure.spring.cloud.core.properties.proxy.ProxyProperties;
+import com.azure.spring.cloud.core.implementation.util.AzurePasswordlessPropertiesUtils;
import com.azure.spring.cloud.core.provider.AzureProfileOptionsProvider;
-import com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties;
import com.azure.spring.cloud.service.implementation.passwordless.AzureRedisPasswordlessProperties;
import org.junit.jupiter.api.Test;
-import static org.junit.jupiter.api.Assertions.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
-class AzureJedisPasswordlessUtilTest {
+class MergeAzureCommonPropertiesTest {
@Test
void testGetPropertiesFromGlobalProperties() {
@@ -26,15 +24,11 @@ void testGetPropertiesFromGlobalProperties() {
globalProperties.getProfile().setCloudType(AzureProfileOptionsProvider.CloudType.AZURE_CHINA);
globalProperties.getProfile().setSubscriptionId("global-sub");
globalProperties.getProfile().setTenantId("global-tenant-id");
- globalProperties.getClient().setApplicationId("global-application-id");
- globalProperties.getProxy().setUsername("global-proxy-username");
- globalProperties.getProxy().setPassword("global-proxy-password");
- globalProperties.getProxy().setHostname("global-proxy-hostname");
- globalProperties.getProxy().setPort(1111);
AzureRedisPasswordlessProperties passwordlessProperties = new AzureRedisPasswordlessProperties();
- AzurePasswordlessProperties result = AzureJedisPasswordlessUtil.mergeAzureProperties(globalProperties, passwordlessProperties);
+ AzureRedisPasswordlessProperties result = new AzureRedisPasswordlessProperties();
+ AzurePasswordlessPropertiesUtils.mergeAzureCommonProperties(globalProperties, passwordlessProperties, result);
assertEquals("https://*.cacheinfra.windows.net:10225/appid/.default", result.getScopes());
assertEquals("global-client-id", result.getCredential().getClientId());
@@ -45,11 +39,6 @@ void testGetPropertiesFromGlobalProperties() {
assertEquals(AzureProfileOptionsProvider.CloudType.AZURE_CHINA, result.getProfile().getCloudType());
assertEquals("global-sub", result.getProfile().getSubscriptionId());
assertEquals("global-tenant-id", result.getProfile().getTenantId());
- assertEquals("global-application-id", result.getClient().getApplicationId());
- assertEquals("global-proxy-username", result.getProxy().getUsername());
- assertEquals("global-proxy-password", result.getProxy().getPassword());
- assertEquals("global-proxy-hostname", result.getProxy().getHostname());
- assertEquals(1111, result.getProxy().getPort());
}
@Test
@@ -66,13 +55,9 @@ void testGetPropertiesFromAzurePasswordlessProperties() {
passwordlessProperties.getProfile().setCloudType(AzureProfileOptionsProvider.CloudType.AZURE_US_GOVERNMENT);
passwordlessProperties.getProfile().setSubscriptionId("sub");
passwordlessProperties.getProfile().setTenantId("tenant-id");
- ((ClientProperties) passwordlessProperties.getClient()).setApplicationId("passwordless-application-id");
- ((ProxyProperties) passwordlessProperties.getProxy()).setUsername("proxy-username");
- ((ProxyProperties) passwordlessProperties.getProxy()).setPassword("proxy-password");
- ((ProxyProperties) passwordlessProperties.getProxy()).setHostname("proxy-hostname");
- ((ProxyProperties) passwordlessProperties.getProxy()).setPort(2222);
- AzurePasswordlessProperties result = AzureJedisPasswordlessUtil.mergeAzureProperties(globalProperties, passwordlessProperties);
+ AzureRedisPasswordlessProperties result = new AzureRedisPasswordlessProperties();
+ AzurePasswordlessPropertiesUtils.mergeAzureCommonProperties(globalProperties, passwordlessProperties, result);
assertEquals("scopes-us-gov", result.getScopes());
assertEquals("client-id", result.getCredential().getClientId());
@@ -83,11 +68,6 @@ void testGetPropertiesFromAzurePasswordlessProperties() {
assertEquals(AzureProfileOptionsProvider.CloudType.AZURE_US_GOVERNMENT, result.getProfile().getCloudType());
assertEquals("sub", result.getProfile().getSubscriptionId());
assertEquals("tenant-id", result.getProfile().getTenantId());
- assertEquals("passwordless-application-id", result.getClient().getApplicationId());
- assertEquals("proxy-username", result.getProxy().getUsername());
- assertEquals("proxy-password", result.getProxy().getPassword());
- assertEquals("proxy-hostname", result.getProxy().getHostname());
- assertEquals(2222, result.getProxy().getPort());
}
@@ -116,12 +96,9 @@ void testGetPropertiesFromGlobalAndPasswordlessProperties() {
passwordlessProperties.getCredential().setManagedIdentityEnabled(true);
passwordlessProperties.getProfile().setCloudType(AzureProfileOptionsProvider.CloudType.AZURE_US_GOVERNMENT);
passwordlessProperties.getProfile().setSubscriptionId("sub");
- ((ClientProperties) passwordlessProperties.getClient()).setApplicationId("passwordless-application-id");
- ((ProxyProperties) passwordlessProperties.getProxy()).setUsername("proxy-username");
- ((ProxyProperties) passwordlessProperties.getProxy()).setHostname("proxy-hostname");
- ((ProxyProperties) passwordlessProperties.getProxy()).setPort(2222);
- AzurePasswordlessProperties result = AzureJedisPasswordlessUtil.mergeAzureProperties(globalProperties, passwordlessProperties);
+ AzureRedisPasswordlessProperties result = new AzureRedisPasswordlessProperties();
+ AzurePasswordlessPropertiesUtils.mergeAzureCommonProperties(globalProperties, passwordlessProperties, result);
assertEquals("scope", result.getScopes());
assertEquals("global-client-id", result.getCredential().getClientId());
@@ -132,10 +109,5 @@ void testGetPropertiesFromGlobalAndPasswordlessProperties() {
assertEquals(AzureProfileOptionsProvider.CloudType.AZURE_US_GOVERNMENT, result.getProfile().getCloudType());
assertEquals("sub", result.getProfile().getSubscriptionId());
assertEquals("global-tenant-id", result.getProfile().getTenantId());
- assertEquals("passwordless-application-id", result.getClient().getApplicationId());
- assertEquals("proxy-username", result.getProxy().getUsername());
- assertEquals("global-proxy-password", result.getProxy().getPassword());
- assertEquals("proxy-hostname", result.getProxy().getHostname());
- assertEquals(2222, result.getProxy().getPort());
}
}
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessAutoConfigurationTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessAutoConfigurationTest.java
index 355056b7544f7..8d19c2b7d1f0b 100644
--- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessAutoConfigurationTest.java
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/redis/AzureJedisPasswordlessAutoConfigurationTest.java
@@ -11,6 +11,7 @@
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.AutoConfigurations;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -236,6 +237,7 @@ void testRedisConfigurationWithClientName() {
}
@Configuration(proxyBeanMethods = false)
+ @EnableConfigurationProperties
static class CustomConfiguration {
@Bean
diff --git a/sdk/spring/spring-cloud-azure-core/pom.xml b/sdk/spring/spring-cloud-azure-core/pom.xml
index db344875526dc..cf405d9f0460f 100644
--- a/sdk/spring/spring-cloud-azure-core/pom.xml
+++ b/sdk/spring/spring-cloud-azure-core/pom.xml
@@ -59,6 +59,13 @@
1.10.1
+
+ com.azure
+ azure-identity-extensions
+ 1.1.1
+ true
+
+
com.azure
azure-storage-blob
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
new file mode 100644
index 0000000000000..54df0ceecbe78
--- /dev/null
+++ b/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/implementation/util/AzurePasswordlessPropertiesUtils.java
@@ -0,0 +1,107 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT License.
+
+package com.azure.spring.cloud.core.implementation.util;
+
+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;
+
+/**
+ * Util class for AzurePasswordlessProperties.
+ */
+public final class AzurePasswordlessPropertiesUtils {
+
+ private AzurePasswordlessPropertiesUtils() {
+
+ }
+
+ /**
+ * Copy common properties from source {@link AzureProperties} object to target {@link T extends PasswordlessProperties} object.
+ * If a field x.y.z exists in both source and target object, the source value will override the target value.
+ *
+ * @param source The source {@link AzureProperties} object.
+ * @param target The target object.
+ * @param The type of the target that extends PasswordlessProperties.
+ */
+ public static void copyAzureCommonProperties(AzureProperties source, T target) {
+ // call explicitly for these fields could be defined as final
+
+ BeanUtils.copyProperties(source.getProfile(), target.getProfile());
+ BeanUtils.copyProperties(source.getProfile().getEnvironment(), target.getProfile().getEnvironment());
+ BeanUtils.copyProperties(source.getCredential(), target.getCredential());
+
+ }
+
+ /**
+ * Copy common properties from source {@link PasswordlessProperties} object to target {@link T extends PasswordlessProperties} object. Ignore the source
+ * value if it is null.
+ *
+ * @param source The source {@link PasswordlessProperties} object.
+ * @param target The target object.
+ * @param The type of the target that extends PasswordlessProperties.
+ */
+ public static void copyAzureCommonPropertiesIgnoreNull(PasswordlessProperties source, T target) {
+
+ copyPropertiesIgnoreNull(source.getProfile(), target.getProfile());
+ copyPropertiesIgnoreNull(source.getProfile().getEnvironment(), target.getProfile().getEnvironment());
+ copyPropertiesIgnoreNull(source.getCredential(), target.getCredential());
+
+ target.setScopes(source.getScopes());
+ target.setPasswordlessEnabled(source.isPasswordlessEnabled());
+ }
+
+ /**
+ * Merge properties from a {@link AzureProperties} object and a {@link PasswordlessProperties} object. If a same property appears in both two objects, the
+ * value from the latter will take precedence.
+ * @param defaultProperties The default properties, the merge result will take value from this property as default.
+ * @param properties The overridden properties, the merge result will take value from this if a same property
+ * appears in two property objects.
+ * @param target The merge result.
+ * @param The type of the merge result.
+ */
+ public static void mergeAzureCommonProperties(AzureProperties defaultProperties,
+ PasswordlessProperties properties,
+ T target) {
+ 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
+
+ com.azure
+ azure-identity-extensions
+ 1.1.1
+