Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -138,12 +138,6 @@
"old": "method void com.azure.spring.cloud.autoconfigure.jms.properties.AzureServiceBusJmsProperties::setUsername(java.lang.String)",
"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.numberOfParametersChanged",
Expand Down
1 change: 1 addition & 0 deletions sdk/spring/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
This section includes changes in `spring-cloud-azure-autoconfigure` module.

#### Features Added
- Support passwordless connections for JMS ServiceBus in Spring Boot 3. [#35608](https://github.com/Azure/azure-sdk-for-java/pull/35608).
- Added autoconfiguration for the Event Grid client [#35613](https://github.com/Azure/azure-sdk-for-java/pull/35613).

## 5.2.0 (2023-06-02)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
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.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;
Expand All @@ -32,7 +34,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;


Expand All @@ -52,7 +53,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.");
Expand Down Expand Up @@ -125,17 +126,15 @@ private void enhanceUserAgent(DatabaseType databaseType, JdbcConnectionStringEnh
}
}

private Map<String, String> buildEnhancedProperties(DatabaseType databaseType, AzurePasswordlessProperties properties) {
private Map<String, String> buildEnhancedProperties(DatabaseType databaseType, AzureJdbcPasswordlessProperties properties) {
Map<String, String> 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());

Expand All @@ -154,12 +153,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;

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.spring.cloud.autoconfigure.implementation.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<String> {

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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
package com.azure.spring.cloud.autoconfigure.implementation.jms;

import com.azure.spring.cloud.autoconfigure.implementation.condition.ConditionalOnMissingProperty;
import com.azure.spring.cloud.autoconfigure.implementation.context.properties.AzureGlobalProperties;
import com.azure.spring.cloud.autoconfigure.implementation.jms.properties.AzureServiceBusJmsProperties;
import com.azure.spring.cloud.autoconfigure.implementation.resourcemanager.AzureServiceBusResourceManagerAutoConfiguration;
import com.azure.spring.cloud.autoconfigure.jms.ServiceBusJmsConnectionFactoryCustomizer;
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 com.azure.spring.cloud.autoconfigure.jms.ServiceBusJmsConnectionFactoryCustomizer;
import jakarta.jms.ConnectionFactory;
import org.apache.qpid.jms.JmsConnectionExtensions;
import org.apache.qpid.jms.JmsConnectionFactory;
Expand All @@ -32,6 +34,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;

/**
Expand All @@ -46,17 +49,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> azureServiceBusJmsCredentialSupplier) {
return factory -> {
final Map<String, Object> 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);
Expand All @@ -70,4 +83,10 @@ static AzureServiceBusJmsPropertiesBeanPostProcessor azureServiceBusJmsPropertie
ObjectProvider<ServiceConnectionStringProvider<AzureServiceType.ServiceBus>> connectionStringProviders) {
return new AzureServiceBusJmsPropertiesBeanPostProcessor(connectionStringProviders);
}

private AzureServiceBusJmsProperties mergeAzureProperties(AzureGlobalProperties azureGlobalProperties, AzureServiceBusJmsProperties azurePasswordlessProperties) {
AzureServiceBusJmsProperties mergedProperties = new AzureServiceBusJmsProperties();
AzurePasswordlessPropertiesUtils.mergeAzureCommonProperties(azureGlobalProperties, azurePasswordlessProperties, mergedProperties);
return mergedProperties;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@

import com.azure.spring.cloud.autoconfigure.implementation.jms.properties.AzureServiceBusJmsProperties;
import com.azure.spring.jms.ServiceBusJmsConnectionFactory;
import com.azure.spring.cloud.autoconfigure.jms.ServiceBusJmsConnectionFactoryCustomizer;
import com.azure.spring.cloud.core.implementation.connectionstring.ServiceBusConnectionString;
import com.azure.spring.cloud.autoconfigure.jms.ServiceBusJmsConnectionFactoryCustomizer;
import org.apache.qpid.jms.policy.JmsDefaultPrefetchPolicy;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
Expand Down Expand Up @@ -56,20 +56,26 @@ private <T extends ServiceBusJmsConnectionFactory> void setPrefetchPolicy(T fact
private <T extends ServiceBusJmsConnectionFactory> T createConnectionFactoryInstance(Class<T> 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);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.spring.cloud.autoconfigure.implementation.jms;

import com.azure.spring.cloud.autoconfigure.implementation.jms.properties.AzureServiceBusJmsProperties;
import com.azure.spring.cloud.autoconfigure.jms.ServiceBusJmsConnectionFactoryCustomizer;
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()
);
};
}


}
Loading