From d95782855697400fafeeea26428af813c2c60871 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Fri, 10 Feb 2023 17:00:17 +0800 Subject: [PATCH 01/29] jms passwordless --- .../AzureServiceBusCredentialSupplier.java | 37 +++++ .../jms/ServiceBusJmsAutoConfiguration.java | 13 +- ...ServiceBusJmsConnectionFactoryFactory.java | 17 ++- ...erviceBusJmsPasswordlessConfiguration.java | 58 +++++++ .../AzureServiceBusJmsProperties.java | 33 +++- ...itional-spring-configuration-metadata.json | 68 +++++++++ .../ServiceBusJmsAutoConfigurationTests.java | 1 - ...ceBusJmsPasswordlessConfigurationTest.java | 143 ++++++++++++++++++ .../util/AzureSpringIdentifier.java | 1 + .../jms/ServiceBusJmsPasswordlessIT.java | 45 ++++++ ...pplication-servicebus-jms-passwordless.yml | 6 + .../servicebus/test-resources.json | 4 + ...AzureServiceBusPasswordlessProperties.java | 37 +++++ .../pom.xml | 5 + 14 files changed, 452 insertions(+), 16 deletions(-) create mode 100644 sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/jms/AzureServiceBusCredentialSupplier.java create mode 100644 sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/jms/ServiceBusJmsPasswordlessConfiguration.java create mode 100644 sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/jms/ServiceBusJmsPasswordlessConfigurationTest.java create mode 100644 sdk/spring/spring-cloud-azure-integration-tests/src/test/java/com/azure/spring/cloud/integration/tests/servicebus/jms/ServiceBusJmsPasswordlessIT.java create mode 100644 sdk/spring/spring-cloud-azure-integration-tests/src/test/resources/application-servicebus-jms-passwordless.yml create mode 100644 sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureServiceBusPasswordlessProperties.java diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/jms/AzureServiceBusCredentialSupplier.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/jms/AzureServiceBusCredentialSupplier.java new file mode 100644 index 0000000000000..dc9f182577741 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/jms/AzureServiceBusCredentialSupplier.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; + +/** + * AzureServiceBusCredentialSupplier that provide a String as the password to connect Azure ServiceBus. + * + * @since 4.7.0 + */ +public class AzureServiceBusCredentialSupplier implements Supplier { + + private final AzureAuthenticationTemplate azureAuthenticationTemplate; + + /** + * Create {@link AzureServiceBusCredentialSupplier} instance. + * @param properties properties to initialize AzureServiceBusCredentialSupplier. + */ + public AzureServiceBusCredentialSupplier(Properties properties) { + azureAuthenticationTemplate = new AzureAuthenticationTemplate(); + azureAuthenticationTemplate.init(properties); + } + + @Override + public String get() { + return azureAuthenticationTemplate.getTokenAsPassword(); + } + + AzureServiceBusCredentialSupplier(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..c822b1163dbaf 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 @@ -31,6 +31,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 +46,21 @@ 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({AzureServiceBusJmsProperties.class, JmsProperties.class}) +@Import({ServiceBusJmsConnectionFactoryConfiguration.class, ServiceBusJmsContainerConfiguration.class, ServiceBusJmsPasswordlessConfiguration.class}) public class ServiceBusJmsAutoConfiguration { @Bean @ConditionalOnExpression("'premium'.equalsIgnoreCase('${spring.jms.servicebus.pricing-tier}')") - ServiceBusJmsConnectionFactoryCustomizer amqpOpenPropertiesCustomizer() { + ServiceBusJmsConnectionFactoryCustomizer amqpOpenPropertiesCustomizer(ObjectProvider azureServiceBusCredentialSupplier) { return factory -> { final Map properties = new HashMap<>(); properties.put("com.microsoft:is-client-provider", true); - properties.put("user-agent", AZURE_SPRING_SERVICE_BUS); + if (azureServiceBusCredentialSupplier.getIfAvailable() == null) { + properties.put("user-agent", AZURE_SPRING_SERVICE_BUS); + } else { + properties.put("user-agent", AZURE_SPRING_PASSWORDLESS_SERVICE_BUS); + } //set user agent factory.setExtension(JmsConnectionExtensions.AMQP_OPEN_PROPERTIES.toString(), (connection, uri) -> properties); 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..d8a1ce089f8bd 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,12 +57,19 @@ private void setPrefetchPolicy(T fact private T createConnectionFactoryInstance(Class factoryClass) { try { T factory; - ServiceBusConnectionString serviceBusConnectionString = new ServiceBusConnectionString(properties.getConnectionString()); - String host = serviceBusConnectionString.getEndpointUri().getHost(); + String remoteUrl = null; + String username = null; + String password = null; + if (properties.getConnectionString() != null) { + 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(); + remoteUrl = String.format(AMQP_URI_FORMAT, host, properties.getIdleTimeout().toMillis()); + username = serviceBusConnectionString.getSharedAccessKeyName(); + password = serviceBusConnectionString.getSharedAccessKey(); + } else if (properties.getEndpoint() != null) { + remoteUrl = String.format(AMQP_URI_FORMAT, properties.getEndpoint(), properties.getIdleTimeout().toMillis()); + } if (StringUtils.hasLength(username) && StringUtils.hasLength(password)) { factory = factoryClass.getConstructor(String.class, String.class, String.class) 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..9d4733d13e630 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/jms/ServiceBusJmsPasswordlessConfiguration.java @@ -0,0 +1,58 @@ +// 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.core.implementation.util.AzurePropertiesUtils; +import com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties; +import com.azure.spring.cloud.service.implementation.passwordless.AzureServiceBusPasswordlessProperties; +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.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.Properties; + +/** + * {@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") +public class ServiceBusJmsPasswordlessConfiguration { + + @Bean + @ConfigurationProperties(prefix = "spring.jms.servicebus") + AzureServiceBusPasswordlessProperties serviceBusPasswordlessProperties() { + return new AzureServiceBusPasswordlessProperties(); + } + + @Bean + @ConditionalOnMissingBean + AzureServiceBusCredentialSupplier azureServiceBusCredentialSupplier(AzureGlobalProperties azureGlobalProperties, AzureServiceBusPasswordlessProperties serviceBusPasswordlessProperties) { + Properties properties = mergeAzureProperties(azureGlobalProperties, serviceBusPasswordlessProperties).toProperties(); + return new AzureServiceBusCredentialSupplier(properties); + } + + @Bean + ServiceBusJmsConnectionFactoryCustomizer jmsAADAuthenticationCustomizer(AzureServiceBusCredentialSupplier credentialSupplier) { + return factory -> { + factory.setExtension(JmsConnectionExtensions.USERNAME_OVERRIDE.toString(), (connection, uri) -> "$jwt"); + factory.setExtension(JmsConnectionExtensions.PASSWORD_OVERRIDE.toString(), (connection, uri) -> + credentialSupplier.get() + ); + }; + } + + private AzurePasswordlessProperties mergeAzureProperties(AzureGlobalProperties azureGlobalProperties, AzurePasswordlessProperties azurePasswordlessProperties) { + AzurePasswordlessProperties mergedProperties = new AzurePasswordlessProperties(); + AzurePropertiesUtils.mergeAzureCommonProperties(azureGlobalProperties, azurePasswordlessProperties, mergedProperties); + mergedProperties.setScopes(azurePasswordlessProperties.getScopes()); + return mergedProperties; + } +} 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..d2f0067caaad9 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 @@ -28,6 +28,11 @@ public class AzureServiceBusJmsProperties implements InitializingBean { */ private boolean enabled = true; + /** + * Endpoint to a Service Bus namespace. + */ + private String endpoint; + /** * Connection string to connect to a Service Bus namespace. */ @@ -79,16 +84,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 +163,22 @@ public PrefetchPolicy getPrefetchPolicy() { return prefetchPolicy; } + /** + * Get the endpoint to connect to a Service Bus namespace. + * @return the endpoint to connect to a Service Bus namespace. + */ + public String getEndpoint() { + return endpoint; + } + + /** + * Set the endpoint to connect to a Service Bus namespace. + * @param endpoint the endpoint to connect to a Service Bus namespace. + */ + public void setEndpoint(String endpoint) { + this.endpoint = endpoint; + } + /** * Validate spring.jms.servicebus related properties. * @@ -165,8 +186,8 @@ 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 (!StringUtils.hasText(connectionString) && !StringUtils.hasText(endpoint)) { + throw new IllegalArgumentException("'spring.jms.servicebus.connection-string' or 'spring.jms.servicebus.endpoint' should be provided"); } if (null == pricingTier || !pricingTier.matches("(?i)premium|standard|basic")) { 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 0e1122b9a59a0..e23b2498da585 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 @@ -1668,6 +1668,74 @@ "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties", "defaultValue": false }, + { + "name": "spring.jms.servicebus.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" + }, + { + "name": "spring.jms.servicebus.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" + }, + { + "name": "spring.jms.servicebus.credential.client-certificate-password", + "type": "java.lang.String", + "description": "Password of the certificate file.", + "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties" + }, + { + "name": "spring.jms.servicebus.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" + }, + { + "name": "spring.jms.servicebus.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" + }, + { + "name": "spring.jms.servicebus.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" + }, + { + "name": "spring.jms.servicebus.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", + "defaultValue": false + }, + { + "name": "spring.jms.servicebus.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" + }, + { + "name": "spring.jms.servicebus.profile.tenant-id", + "type": "java.lang.String", + "description": "Tenant ID for Azure resources.", + "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties" + }, + { + "name": "spring.jms.servicebus.profile.cloud-type", + "type": "java.lang.String", + "description": "Name of the Azure cloud to connect to.", + "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties" + }, + { + "name": "spring.jms.servicebus.passwordless-enabled", + "type": "java.lang.Boolean", + "description": "Whether to enable passwordless connections to Azure ServiceBus by using OAuth2 Azure Active Directory token credentials.", + "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties", + "defaultValue": false + }, { "name": "spring.cloud.azure.message-converter.isolated-object-mapper", "type": "java.lang.Boolean", 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..b0c7362b82c6f 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 @@ -127,7 +127,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..773a37134310f --- /dev/null +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/jms/ServiceBusJmsPasswordlessConfigurationTest.java @@ -0,0 +1,143 @@ +// 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.service.implementation.passwordless.AzureServiceBusPasswordlessProperties; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mockito.MockedConstruction; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +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") + .withBean(AzureGlobalProperties.class, () -> azureProperties) + .run(context -> { + assertThat(context).doesNotHaveBean(AzureServiceBusPasswordlessProperties.class); + assertThat(context).doesNotHaveBean(AzureServiceBusCredentialSupplier.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") + .withBean(AzureGlobalProperties.class, () -> azureProperties) + .run(context -> { + assertThat(context).hasSingleBean(AzureServiceBusPasswordlessProperties.class); + assertThat(context).hasSingleBean(AzureServiceBusCredentialSupplier.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.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(AzureServiceBusPasswordlessProperties.class); + assertThat(context).hasSingleBean(AzureServiceBusCredentialSupplier.class); + assertThat(context).hasSingleBean(ServiceBusJmsConnectionFactoryCustomizer.class); + AzureServiceBusPasswordlessProperties properties = context.getBean(AzureServiceBusPasswordlessProperties.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(); + assertThat(properties.getClient().getApplicationId()).isEqualTo("application-id"); + assertThat(properties.getProxy().getHostname()).isEqualTo("hostname"); + assertThat(properties.getProxy().getUsername()).isEqualTo("username"); + assertThat(properties.getProxy().getPort()).isEqualTo(1111); + assertThat(properties.getProxy().getPassword()).isEqualTo("password"); + assertThat(properties.getProxy().getType()).isEqualTo("type"); + }); + } + + @Test + void testAzureServiceBusCredentialSupplier() { + 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(AzureServiceBusCredentialSupplier.class, + (azureServiceBusCredentialSupplierMocker, mockerContext) -> { + when(azureServiceBusCredentialSupplierMocker.get()).thenReturn("fake-token"); + + contextRunner.run(runnerContext -> { + assertThat(runnerContext).hasSingleBean(AzureServiceBusPasswordlessProperties.class); + assertThat(runnerContext).hasSingleBean(AzureServiceBusCredentialSupplier.class); + assertThat(runnerContext).hasSingleBean(ServiceBusJmsConnectionFactoryCustomizer.class); + runnerContext.getBean(AzureServiceBusCredentialSupplier.class).get().equals("fake-token"); + }); + + })) { + Assertions.assertNotNull(supplierMockedConstruction); + } + + } + + @EnableConfigurationProperties + @Import(ServiceBusJmsPasswordlessConfiguration.class) + static class ServiceBusJmsPasswordlessTestConfig { + } +} diff --git a/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/implementation/util/AzureSpringIdentifier.java b/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/implementation/util/AzureSpringIdentifier.java index a4a8333f10c8b..42e2c1712101a 100644 --- a/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/implementation/util/AzureSpringIdentifier.java +++ b/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/implementation/util/AzureSpringIdentifier.java @@ -55,6 +55,7 @@ private AzureSpringIdentifier() { * Azure Spring ServiceBus */ public static final String AZURE_SPRING_SERVICE_BUS = "az-sp-bus/" + VERSION; + public static final String AZURE_SPRING_PASSWORDLESS_SERVICE_BUS = "az-sp-pl-sb/" + VERSION; /** * Azure Spring Storage Blob diff --git a/sdk/spring/spring-cloud-azure-integration-tests/src/test/java/com/azure/spring/cloud/integration/tests/servicebus/jms/ServiceBusJmsPasswordlessIT.java b/sdk/spring/spring-cloud-azure-integration-tests/src/test/java/com/azure/spring/cloud/integration/tests/servicebus/jms/ServiceBusJmsPasswordlessIT.java new file mode 100644 index 0000000000000..9ba55a00d0276 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-integration-tests/src/test/java/com/azure/spring/cloud/integration/tests/servicebus/jms/ServiceBusJmsPasswordlessIT.java @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.spring.cloud.integration.tests.servicebus.jms; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.jms.annotation.JmsListener; +import org.springframework.jms.core.JmsTemplate; +import org.springframework.test.context.ActiveProfiles; + +import java.util.concurrent.Exchanger; + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE) +@ActiveProfiles("servicebus-jms-passwordless") +public class ServiceBusJmsPasswordlessIT { + private static final Logger LOGGER = LoggerFactory.getLogger(ServiceBusJmsPasswordlessIT.class); + private static final String DATA = "service bus jms passwordless test"; + private static final String QUEUE_NAME = "que001"; + private final Exchanger EXCHANGER = new Exchanger<>(); + + @Autowired + private JmsTemplate jmsTemplate; + + @Test + @Timeout(70) + public void testServiceBusJmsOperation() throws InterruptedException { + LOGGER.info("ServiceBusJmsPasswordlessIT begin."); + jmsTemplate.convertAndSend(QUEUE_NAME, DATA); + LOGGER.info("Send message: {}", DATA); + String msg = EXCHANGER.exchange(null); + Assertions.assertEquals(DATA, msg); + LOGGER.info("ServiceBusJmsPasswordlessIT end."); + } + + @JmsListener(destination = QUEUE_NAME, containerFactory = "jmsListenerContainerFactory") + public void receiveQueueMessage(String message) throws InterruptedException { + LOGGER.info("Received message from queue: {}", message); + EXCHANGER.exchange(message); + } +} diff --git a/sdk/spring/spring-cloud-azure-integration-tests/src/test/resources/application-servicebus-jms-passwordless.yml b/sdk/spring/spring-cloud-azure-integration-tests/src/test/resources/application-servicebus-jms-passwordless.yml new file mode 100644 index 0000000000000..a13192bcdaeb1 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-integration-tests/src/test/resources/application-servicebus-jms-passwordless.yml @@ -0,0 +1,6 @@ +spring: + jms: + servicebus: + pricing-tier: standard + passwordless-enabled: true + endpoint: ${SERVICEBUS_PASSWORDLESS_ENDPOINT} diff --git a/sdk/spring/spring-cloud-azure-integration-tests/test-resources/servicebus/test-resources.json b/sdk/spring/spring-cloud-azure-integration-tests/test-resources/servicebus/test-resources.json index 636ef04e1bbf2..9e76573145d30 100644 --- a/sdk/spring/spring-cloud-azure-integration-tests/test-resources/servicebus/test-resources.json +++ b/sdk/spring/spring-cloud-azure-integration-tests/test-resources/servicebus/test-resources.json @@ -409,6 +409,10 @@ "AZURE_SERVICE_BUS_DOMAIN_NAME": { "type": "string", "value": "[substring(parameters('serviceBusEndpointSuffix'), 1)]" + }, + "SERVICEBUS_PASSWORDLESS_ENDPOINT": { + "type": "string", + "value": "[concat(variables('azureServiceBusNamespaceName'), substring(parameters('serviceBusEndpointSuffix'), 1))]" } } } diff --git a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureServiceBusPasswordlessProperties.java b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureServiceBusPasswordlessProperties.java new file mode 100644 index 0000000000000..e36dcf90a4586 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureServiceBusPasswordlessProperties.java @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.cloud.service.implementation.passwordless; + +import java.util.HashMap; +import java.util.Map; + +/** + * Configuration properties for passwordless connections with Azure ServiceBus. + */ +public class AzureServiceBusPasswordlessProperties extends AzurePasswordlessProperties { + + private static final String SERVICEBUS_SCOPE_AZURE = "https://servicebus.azure.net/.default"; + private static final String SERVICEBUS_SCOPE_AZURE_CHINA = "https://servicebus.azure.net/.default"; + private static final String SERVICEBUS_SCOPE_AZURE_GERMANY = "https://servicebus.azure.net/.default"; + private static final String SERVICEBUS_SCOPE_AZURE_US_GOVERNMENT = "https://servicebus.azure.net/.default"; + + private static final Map SERVICEBUS_SCOPE_MAP = new HashMap() { + { + put(CloudType.AZURE, SERVICEBUS_SCOPE_AZURE); + put(CloudType.AZURE_CHINA, SERVICEBUS_SCOPE_AZURE_CHINA); + put(CloudType.AZURE_GERMANY, SERVICEBUS_SCOPE_AZURE_GERMANY); + put(CloudType.AZURE_US_GOVERNMENT, SERVICEBUS_SCOPE_AZURE_US_GOVERNMENT); + } + }; + + @Override + public String getScopes() { + return super.getScopes() == null ? getRedisScopes() : super.getScopes(); + } + + private String getRedisScopes() { + return SERVICEBUS_SCOPE_MAP.getOrDefault(getProfile().getCloudType(), SERVICEBUS_SCOPE_AZURE); + } + +} diff --git a/sdk/spring/spring-cloud-azure-starter-servicebus-jms/pom.xml b/sdk/spring/spring-cloud-azure-starter-servicebus-jms/pom.xml index 0d62fe5eb37ac..303f92620ed9b 100644 --- a/sdk/spring/spring-cloud-azure-starter-servicebus-jms/pom.xml +++ b/sdk/spring/spring-cloud-azure-starter-servicebus-jms/pom.xml @@ -143,6 +143,11 @@ netty-codec-http 4.1.87.Final + + com.azure + azure-identity-extensions + 1.1.0 + From 3a4737e3fe197c9dfd12f84674b47ca7d139ff94 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Tue, 14 Feb 2023 16:00:32 +0800 Subject: [PATCH 02/29] use namespace instead of endpoint --- ...eBusJmsConnectionFactoryConfiguration.java | 22 ++++++++++------- ...ServiceBusJmsConnectionFactoryFactory.java | 16 ++++++++++--- .../AzureServiceBusJmsProperties.java | 24 +++++++++---------- ...pplication-servicebus-jms-passwordless.yml | 4 +++- .../servicebus/test-resources.json | 4 ---- 5 files changed, 42 insertions(+), 28 deletions(-) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/jms/ServiceBusJmsConnectionFactoryConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/jms/ServiceBusJmsConnectionFactoryConfiguration.java index 68209ee38bc46..3e589b69ad450 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/jms/ServiceBusJmsConnectionFactoryConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/jms/ServiceBusJmsConnectionFactoryConfiguration.java @@ -4,6 +4,7 @@ package com.azure.spring.cloud.autoconfigure.jms; import com.azure.spring.cloud.autoconfigure.jms.properties.AzureServiceBusJmsProperties; +import com.azure.spring.cloud.service.implementation.passwordless.AzureServiceBusPasswordlessProperties; import org.apache.commons.pool2.PooledObject; import org.messaginghub.pooled.jms.JmsPoolConnectionFactory; import org.springframework.beans.factory.ObjectProvider; @@ -27,9 +28,11 @@ public class ServiceBusJmsConnectionFactoryConfiguration { private static ServiceBusJmsConnectionFactory createJmsConnectionFactory(AzureServiceBusJmsProperties properties, - ObjectProvider factoryCustomizers) { + ObjectProvider factoryCustomizers, + ObjectProvider serviceBusPasswordlessProperties) { return new ServiceBusJmsConnectionFactoryFactory(properties, - factoryCustomizers.orderedStream().collect(Collectors.toList())) + factoryCustomizers.orderedStream().collect(Collectors.toList()), + serviceBusPasswordlessProperties.getIfAvailable()) .createConnectionFactory(ServiceBusJmsConnectionFactory.class); } @@ -41,8 +44,9 @@ static class SimpleConnectionFactoryConfiguration { @Bean @ConditionalOnProperty(prefix = "spring.jms.cache", name = "enabled", havingValue = "false") ServiceBusJmsConnectionFactory jmsConnectionFactory(AzureServiceBusJmsProperties properties, - ObjectProvider factoryCustomizers) { - return createJmsConnectionFactory(properties, factoryCustomizers); + ObjectProvider factoryCustomizers, + ObjectProvider serviceBusPasswordlessProperties) { + return createJmsConnectionFactory(properties, factoryCustomizers, serviceBusPasswordlessProperties); } @Configuration(proxyBeanMethods = false) @@ -54,8 +58,9 @@ static class CachingConnectionFactoryConfiguration { @Bean CachingConnectionFactory jmsConnectionFactory(JmsProperties jmsProperties, AzureServiceBusJmsProperties properties, - ObjectProvider factoryCustomizers) { - ServiceBusJmsConnectionFactory factory = createJmsConnectionFactory(properties, factoryCustomizers); + ObjectProvider factoryCustomizers, + ObjectProvider serviceBusPasswordlessProperties) { + ServiceBusJmsConnectionFactory factory = createJmsConnectionFactory(properties, factoryCustomizers, serviceBusPasswordlessProperties); CachingConnectionFactory connectionFactory = new CachingConnectionFactory(factory); JmsProperties.Cache cacheProperties = jmsProperties.getCache(); connectionFactory.setCacheConsumers(cacheProperties.isConsumers()); @@ -74,8 +79,9 @@ static class PooledConnectionFactoryConfiguration { @Bean(destroyMethod = "stop") @ConditionalOnProperty(prefix = "spring.jms.servicebus.pool", name = "enabled", havingValue = "true") JmsPoolConnectionFactory jmsPoolConnectionFactory(AzureServiceBusJmsProperties properties, - ObjectProvider factoryCustomizers) { - ServiceBusJmsConnectionFactory factory = createJmsConnectionFactory(properties, factoryCustomizers); + ObjectProvider factoryCustomizers, + ObjectProvider serviceBusPasswordlessProperties) { + ServiceBusJmsConnectionFactory factory = createJmsConnectionFactory(properties, factoryCustomizers, serviceBusPasswordlessProperties); return new JmsPoolConnectionFactoryFactory(properties.getPool()) .createPooledConnectionFactory(factory); } 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 d8a1ce089f8bd..a8b3e7ff27b04 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 @@ -5,6 +5,7 @@ import com.azure.spring.cloud.autoconfigure.jms.properties.AzureServiceBusJmsProperties; import com.azure.spring.cloud.core.implementation.connectionstring.ServiceBusConnectionString; +import com.azure.spring.cloud.service.implementation.passwordless.AzureServiceBusPasswordlessProperties; import org.apache.qpid.jms.policy.JmsDefaultPrefetchPolicy; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -19,15 +20,22 @@ public class ServiceBusJmsConnectionFactoryFactory { private final AzureServiceBusJmsProperties properties; private final List factoryCustomizers; + private final AzureServiceBusPasswordlessProperties serviceBusPasswordlessProperties; private static final String AMQP_URI_FORMAT = "amqps://%s?amqp.idleTimeout=%d"; ServiceBusJmsConnectionFactoryFactory(AzureServiceBusJmsProperties properties, List factoryCustomizers) { + this(properties, factoryCustomizers, null); + } + + ServiceBusJmsConnectionFactoryFactory(AzureServiceBusJmsProperties properties, + List factoryCustomizers, + AzureServiceBusPasswordlessProperties serviceBusPasswordlessProperties) { Assert.notNull(properties, "Properties must not be null"); this.properties = properties; + this.serviceBusPasswordlessProperties = serviceBusPasswordlessProperties; this.factoryCustomizers = (factoryCustomizers != null) ? factoryCustomizers : Collections.emptyList(); - } T createConnectionFactory(Class factoryClass) { @@ -67,8 +75,10 @@ private T createConnectionFactoryInst remoteUrl = String.format(AMQP_URI_FORMAT, host, properties.getIdleTimeout().toMillis()); username = serviceBusConnectionString.getSharedAccessKeyName(); password = serviceBusConnectionString.getSharedAccessKey(); - } else if (properties.getEndpoint() != null) { - remoteUrl = String.format(AMQP_URI_FORMAT, properties.getEndpoint(), properties.getIdleTimeout().toMillis()); + } else if (serviceBusPasswordlessProperties != null & properties.getNameSpace() != null) { + remoteUrl = String.format(AMQP_URI_FORMAT, + properties.getNameSpace() + serviceBusPasswordlessProperties.getProfile().getEnvironment().getServiceBusDomainName(), + properties.getIdleTimeout().toMillis()); } if (StringUtils.hasLength(username) && StringUtils.hasLength(password)) { 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 d2f0067caaad9..cbe6659eddaeb 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 @@ -29,9 +29,9 @@ public class AzureServiceBusJmsProperties implements InitializingBean { private boolean enabled = true; /** - * Endpoint to a Service Bus namespace. + * The Service Bus namespace. */ - private String endpoint; + private String nameSpace; /** * Connection string to connect to a Service Bus namespace. @@ -164,19 +164,19 @@ public PrefetchPolicy getPrefetchPolicy() { } /** - * Get the endpoint to connect to a Service Bus namespace. - * @return the endpoint to connect to a Service Bus namespace. + * Get the Service Bus namespace. + * @return the Service Bus namespace. */ - public String getEndpoint() { - return endpoint; + public String getNameSpace() { + return nameSpace; } /** - * Set the endpoint to connect to a Service Bus namespace. - * @param endpoint the endpoint to connect to a Service Bus namespace. + * Set the Service Bus namespace. + * @param nameSpace the Service Bus namespace. */ - public void setEndpoint(String endpoint) { - this.endpoint = endpoint; + public void setNameSpace(String nameSpace) { + this.nameSpace = nameSpace; } /** @@ -186,8 +186,8 @@ public void setEndpoint(String endpoint) { */ @Override public void afterPropertiesSet() throws Exception { - if (!StringUtils.hasText(connectionString) && !StringUtils.hasText(endpoint)) { - throw new IllegalArgumentException("'spring.jms.servicebus.connection-string' or 'spring.jms.servicebus.endpoint' should be provided"); + if (!StringUtils.hasText(connectionString) && !StringUtils.hasText(nameSpace)) { + throw new IllegalArgumentException("'spring.jms.servicebus.connection-string' or 'spring.jms.servicebus.namespace' should be provided"); } if (null == pricingTier || !pricingTier.matches("(?i)premium|standard|basic")) { diff --git a/sdk/spring/spring-cloud-azure-integration-tests/src/test/resources/application-servicebus-jms-passwordless.yml b/sdk/spring/spring-cloud-azure-integration-tests/src/test/resources/application-servicebus-jms-passwordless.yml index a13192bcdaeb1..14a77cc58923c 100644 --- a/sdk/spring/spring-cloud-azure-integration-tests/src/test/resources/application-servicebus-jms-passwordless.yml +++ b/sdk/spring/spring-cloud-azure-integration-tests/src/test/resources/application-servicebus-jms-passwordless.yml @@ -1,6 +1,8 @@ spring: jms: servicebus: + enabled: true pricing-tier: standard passwordless-enabled: true - endpoint: ${SERVICEBUS_PASSWORDLESS_ENDPOINT} + namespace: ${AZURE_SERVICE_BUS_NAMESPACE} + diff --git a/sdk/spring/spring-cloud-azure-integration-tests/test-resources/servicebus/test-resources.json b/sdk/spring/spring-cloud-azure-integration-tests/test-resources/servicebus/test-resources.json index 9e76573145d30..636ef04e1bbf2 100644 --- a/sdk/spring/spring-cloud-azure-integration-tests/test-resources/servicebus/test-resources.json +++ b/sdk/spring/spring-cloud-azure-integration-tests/test-resources/servicebus/test-resources.json @@ -409,10 +409,6 @@ "AZURE_SERVICE_BUS_DOMAIN_NAME": { "type": "string", "value": "[substring(parameters('serviceBusEndpointSuffix'), 1)]" - }, - "SERVICEBUS_PASSWORDLESS_ENDPOINT": { - "type": "string", - "value": "[concat(variables('azureServiceBusNamespaceName'), substring(parameters('serviceBusEndpointSuffix'), 1))]" } } } From 9888266d210183cdadc3a57734a26412883d77d5 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Tue, 14 Feb 2023 16:12:03 +0800 Subject: [PATCH 03/29] address some pipeline errors --- .../src/main/resources/revapi/revapi.json | 6 ++++++ .../autoconfigure/jms/ServiceBusJmsAutoConfiguration.java | 2 +- .../jms/AzureServiceBusJmsPropertiesTests.java | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) 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 d9b70a86659fa..423b60a366112 100644 --- a/eng/code-quality-reports/src/main/resources/revapi/revapi.json +++ b/eng/code-quality-reports/src/main/resources/revapi/revapi.json @@ -303,6 +303,12 @@ "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", 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 c822b1163dbaf..f89a957bab96b 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 @@ -47,7 +47,7 @@ @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, ServiceBusJmsPasswordlessConfiguration.class}) +@Import({ServiceBusJmsPasswordlessConfiguration.class, ServiceBusJmsConnectionFactoryConfiguration.class, ServiceBusJmsContainerConfiguration.class}) public class ServiceBusJmsAutoConfiguration { @Bean 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..244dddab056f9 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 @@ -22,7 +22,7 @@ void connectionStringNotValid() { Exception ex = assertThrows(IllegalArgumentException.class, prop::afterPropertiesSet); - String expectedMessage = "'spring.jms.servicebus.connection-string' should be provided"; + String expectedMessage = "'spring.jms.servicebus.connection-string' or 'spring.jms.servicebus.namespace' should be provided"; String actualMessage = ex.getMessage(); System.out.println("message:" + actualMessage); assertTrue(actualMessage.contains(expectedMessage)); From be9940c3fa1d3d0eae7ba09c54bba609d1471a81 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Tue, 14 Feb 2023 17:29:07 +0800 Subject: [PATCH 04/29] code refactor --- .../jms/ServiceBusJmsAutoConfiguration.java | 6 ++-- ...ServiceBusJmsConnectionFactoryFactory.java | 16 ++++------- .../AzureServiceBusJmsProperties.java | 24 ++++++++++++++-- .../AzureServiceBusJmsPropertiesTests.java | 28 +++++++++++++++++-- 4 files changed, 56 insertions(+), 18 deletions(-) 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 f89a957bab96b..a9c0a29b15246 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 @@ -56,10 +56,10 @@ ServiceBusJmsConnectionFactoryCustomizer amqpOpenPropertiesCustomizer(ObjectProv return factory -> { final Map properties = new HashMap<>(); properties.put("com.microsoft:is-client-provider", true); - if (azureServiceBusCredentialSupplier.getIfAvailable() == null) { - properties.put("user-agent", AZURE_SPRING_SERVICE_BUS); - } else { + if (azureServiceBusCredentialSupplier.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(), 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 a8b3e7ff27b04..5393417ef9aa4 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 @@ -24,11 +24,6 @@ public class ServiceBusJmsConnectionFactoryFactory { private static final String AMQP_URI_FORMAT = "amqps://%s?amqp.idleTimeout=%d"; - ServiceBusJmsConnectionFactoryFactory(AzureServiceBusJmsProperties properties, - List factoryCustomizers) { - this(properties, factoryCustomizers, null); - } - ServiceBusJmsConnectionFactoryFactory(AzureServiceBusJmsProperties properties, List factoryCustomizers, AzureServiceBusPasswordlessProperties serviceBusPasswordlessProperties) { @@ -68,17 +63,16 @@ private T createConnectionFactoryInst String remoteUrl = null; String username = null; String password = null; - if (properties.getConnectionString() != null) { + if (serviceBusPasswordlessProperties != null & properties.getNameSpace() != null) { + remoteUrl = String.format(AMQP_URI_FORMAT, + properties.getNameSpace() + serviceBusPasswordlessProperties.getProfile().getEnvironment().getServiceBusDomainName(), + properties.getIdleTimeout().toMillis()); + } else if (properties.getConnectionString() != null) { ServiceBusConnectionString serviceBusConnectionString = new ServiceBusConnectionString(properties.getConnectionString()); String host = serviceBusConnectionString.getEndpointUri().getHost(); - remoteUrl = String.format(AMQP_URI_FORMAT, host, properties.getIdleTimeout().toMillis()); username = serviceBusConnectionString.getSharedAccessKeyName(); password = serviceBusConnectionString.getSharedAccessKey(); - } else if (serviceBusPasswordlessProperties != null & properties.getNameSpace() != null) { - remoteUrl = String.format(AMQP_URI_FORMAT, - properties.getNameSpace() + serviceBusPasswordlessProperties.getProfile().getEnvironment().getServiceBusDomainName(), - properties.getIdleTimeout().toMillis()); } if (StringUtils.hasLength(username) && StringUtils.hasLength(password)) { 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 cbe6659eddaeb..6fe88147dc936 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 @@ -52,6 +52,11 @@ public class AzureServiceBusJmsProperties implements InitializingBean { */ private String pricingTier; + /** + * Whether to enable supporting authentication with Azure AD. + */ + private boolean passwordlessEnabled = false; + private final Listener listener = new Listener(); private final PrefetchPolicy prefetchPolicy = new PrefetchPolicy(); @@ -179,6 +184,14 @@ public void setNameSpace(String nameSpace) { this.nameSpace = nameSpace; } + private boolean isPasswordlessEnabled() { + return passwordlessEnabled; + } + + void setPasswordlessEnabled(boolean passwordlessEnabled) { + this.passwordlessEnabled = passwordlessEnabled; + } + /** * Validate spring.jms.servicebus related properties. * @@ -186,13 +199,20 @@ public void setNameSpace(String nameSpace) { */ @Override public void afterPropertiesSet() throws Exception { - if (!StringUtils.hasText(connectionString) && !StringUtils.hasText(nameSpace)) { - throw new IllegalArgumentException("'spring.jms.servicebus.connection-string' or 'spring.jms.servicebus.namespace' 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")) { throw new IllegalArgumentException("'spring.jms.servicebus.pricing-tier' is not valid"); } + } /** 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 244dddab056f9..e76292c87539b 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,9 @@ 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.EnableConfigurationProperties; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.context.annotation.Import; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -16,13 +19,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' or 'spring.jms.servicebus.namespace' 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 +36,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 +50,22 @@ 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 + @Import(AzureServiceBusJmsProperties.class) + static class AzureServiceBusJmsPropertiesTestConfig { + + } + } From df12f2e543c25a4067f34f97e3f4546b06c129b9 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Tue, 14 Feb 2023 17:30:58 +0800 Subject: [PATCH 05/29] update identity --- sdk/spring/spring-cloud-azure-starter-redis/pom.xml | 2 +- sdk/spring/spring-cloud-azure-starter-servicebus-jms/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/spring/spring-cloud-azure-starter-redis/pom.xml b/sdk/spring/spring-cloud-azure-starter-redis/pom.xml index 890c561c169dc..5d57d10c1e57a 100644 --- a/sdk/spring/spring-cloud-azure-starter-redis/pom.xml +++ b/sdk/spring/spring-cloud-azure-starter-redis/pom.xml @@ -106,7 +106,7 @@ com.azure azure-identity-extensions - 1.1.0 + 1.1.1 diff --git a/sdk/spring/spring-cloud-azure-starter-servicebus-jms/pom.xml b/sdk/spring/spring-cloud-azure-starter-servicebus-jms/pom.xml index 303f92620ed9b..7522ac383d8f8 100644 --- a/sdk/spring/spring-cloud-azure-starter-servicebus-jms/pom.xml +++ b/sdk/spring/spring-cloud-azure-starter-servicebus-jms/pom.xml @@ -146,7 +146,7 @@ com.azure azure-identity-extensions - 1.1.0 + 1.1.1 From 374d95f4ba2377c29c921921f89ea87192a45521 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Wed, 15 Feb 2023 09:58:10 +0800 Subject: [PATCH 06/29] use && not & --- .../jms/ServiceBusJmsConnectionFactoryFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 5393417ef9aa4..88f0c65e18498 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 @@ -63,7 +63,7 @@ private T createConnectionFactoryInst String remoteUrl = null; String username = null; String password = null; - if (serviceBusPasswordlessProperties != null & properties.getNameSpace() != null) { + if (serviceBusPasswordlessProperties != null && properties.getNameSpace() != null) { remoteUrl = String.format(AMQP_URI_FORMAT, properties.getNameSpace() + serviceBusPasswordlessProperties.getProfile().getEnvironment().getServiceBusDomainName(), properties.getIdleTimeout().toMillis()); From 68069c662994c20a656f203121827abb951cb72a Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Wed, 15 Feb 2023 11:30:08 +0800 Subject: [PATCH 07/29] address comments --- ...AzureServiceBusJmsCredentialSupplier.java} | 12 ++++----- .../jms/ServiceBusJmsAutoConfiguration.java | 4 +-- ...eBusJmsConnectionFactoryConfiguration.java | 22 ++++++--------- ...ServiceBusJmsConnectionFactoryFactory.java | 27 ++++++------------- ...erviceBusJmsPasswordlessConfiguration.java | 18 ++++++++++--- ...ceBusJmsPasswordlessConfigurationTest.java | 18 ++++++------- .../jms/ServiceBusJmsPasswordlessIT.java | 4 +-- 7 files changed, 49 insertions(+), 56 deletions(-) rename sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/jms/{AzureServiceBusCredentialSupplier.java => AzureServiceBusJmsCredentialSupplier.java} (64%) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/jms/AzureServiceBusCredentialSupplier.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/jms/AzureServiceBusJmsCredentialSupplier.java similarity index 64% rename from sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/jms/AzureServiceBusCredentialSupplier.java rename to sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/jms/AzureServiceBusJmsCredentialSupplier.java index dc9f182577741..caa19c0645e7e 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/jms/AzureServiceBusCredentialSupplier.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/jms/AzureServiceBusJmsCredentialSupplier.java @@ -9,19 +9,19 @@ import java.util.function.Supplier; /** - * AzureServiceBusCredentialSupplier that provide a String as the password to connect Azure ServiceBus. + * AzureServiceBusJmsCredentialSupplier that provide a String as the password to connect Azure ServiceBus. * * @since 4.7.0 */ -public class AzureServiceBusCredentialSupplier implements Supplier { +public class AzureServiceBusJmsCredentialSupplier implements Supplier { private final AzureAuthenticationTemplate azureAuthenticationTemplate; /** - * Create {@link AzureServiceBusCredentialSupplier} instance. - * @param properties properties to initialize AzureServiceBusCredentialSupplier. + * Create {@link AzureServiceBusJmsCredentialSupplier} instance. + * @param properties properties to initialize AzureServiceBusJmsCredentialSupplier. */ - public AzureServiceBusCredentialSupplier(Properties properties) { + public AzureServiceBusJmsCredentialSupplier(Properties properties) { azureAuthenticationTemplate = new AzureAuthenticationTemplate(); azureAuthenticationTemplate.init(properties); } @@ -31,7 +31,7 @@ public String get() { return azureAuthenticationTemplate.getTokenAsPassword(); } - AzureServiceBusCredentialSupplier(AzureAuthenticationTemplate azureAuthenticationTemplate) { + 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 a9c0a29b15246..9bd87e982bc13 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 @@ -52,11 +52,11 @@ public class ServiceBusJmsAutoConfiguration { @Bean @ConditionalOnExpression("'premium'.equalsIgnoreCase('${spring.jms.servicebus.pricing-tier}')") - ServiceBusJmsConnectionFactoryCustomizer amqpOpenPropertiesCustomizer(ObjectProvider azureServiceBusCredentialSupplier) { + ServiceBusJmsConnectionFactoryCustomizer amqpOpenPropertiesCustomizer(ObjectProvider azureServiceBusJmsCredentialSupplier) { return factory -> { final Map properties = new HashMap<>(); properties.put("com.microsoft:is-client-provider", true); - if (azureServiceBusCredentialSupplier.getIfAvailable() != null) { + if (azureServiceBusJmsCredentialSupplier.getIfAvailable() != null) { properties.put("user-agent", AZURE_SPRING_PASSWORDLESS_SERVICE_BUS); } else { properties.put("user-agent", AZURE_SPRING_SERVICE_BUS); diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/jms/ServiceBusJmsConnectionFactoryConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/jms/ServiceBusJmsConnectionFactoryConfiguration.java index 3e589b69ad450..68209ee38bc46 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/jms/ServiceBusJmsConnectionFactoryConfiguration.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/jms/ServiceBusJmsConnectionFactoryConfiguration.java @@ -4,7 +4,6 @@ package com.azure.spring.cloud.autoconfigure.jms; import com.azure.spring.cloud.autoconfigure.jms.properties.AzureServiceBusJmsProperties; -import com.azure.spring.cloud.service.implementation.passwordless.AzureServiceBusPasswordlessProperties; import org.apache.commons.pool2.PooledObject; import org.messaginghub.pooled.jms.JmsPoolConnectionFactory; import org.springframework.beans.factory.ObjectProvider; @@ -28,11 +27,9 @@ public class ServiceBusJmsConnectionFactoryConfiguration { private static ServiceBusJmsConnectionFactory createJmsConnectionFactory(AzureServiceBusJmsProperties properties, - ObjectProvider factoryCustomizers, - ObjectProvider serviceBusPasswordlessProperties) { + ObjectProvider factoryCustomizers) { return new ServiceBusJmsConnectionFactoryFactory(properties, - factoryCustomizers.orderedStream().collect(Collectors.toList()), - serviceBusPasswordlessProperties.getIfAvailable()) + factoryCustomizers.orderedStream().collect(Collectors.toList())) .createConnectionFactory(ServiceBusJmsConnectionFactory.class); } @@ -44,9 +41,8 @@ static class SimpleConnectionFactoryConfiguration { @Bean @ConditionalOnProperty(prefix = "spring.jms.cache", name = "enabled", havingValue = "false") ServiceBusJmsConnectionFactory jmsConnectionFactory(AzureServiceBusJmsProperties properties, - ObjectProvider factoryCustomizers, - ObjectProvider serviceBusPasswordlessProperties) { - return createJmsConnectionFactory(properties, factoryCustomizers, serviceBusPasswordlessProperties); + ObjectProvider factoryCustomizers) { + return createJmsConnectionFactory(properties, factoryCustomizers); } @Configuration(proxyBeanMethods = false) @@ -58,9 +54,8 @@ static class CachingConnectionFactoryConfiguration { @Bean CachingConnectionFactory jmsConnectionFactory(JmsProperties jmsProperties, AzureServiceBusJmsProperties properties, - ObjectProvider factoryCustomizers, - ObjectProvider serviceBusPasswordlessProperties) { - ServiceBusJmsConnectionFactory factory = createJmsConnectionFactory(properties, factoryCustomizers, serviceBusPasswordlessProperties); + ObjectProvider factoryCustomizers) { + ServiceBusJmsConnectionFactory factory = createJmsConnectionFactory(properties, factoryCustomizers); CachingConnectionFactory connectionFactory = new CachingConnectionFactory(factory); JmsProperties.Cache cacheProperties = jmsProperties.getCache(); connectionFactory.setCacheConsumers(cacheProperties.isConsumers()); @@ -79,9 +74,8 @@ static class PooledConnectionFactoryConfiguration { @Bean(destroyMethod = "stop") @ConditionalOnProperty(prefix = "spring.jms.servicebus.pool", name = "enabled", havingValue = "true") JmsPoolConnectionFactory jmsPoolConnectionFactory(AzureServiceBusJmsProperties properties, - ObjectProvider factoryCustomizers, - ObjectProvider serviceBusPasswordlessProperties) { - ServiceBusJmsConnectionFactory factory = createJmsConnectionFactory(properties, factoryCustomizers, serviceBusPasswordlessProperties); + ObjectProvider factoryCustomizers) { + ServiceBusJmsConnectionFactory factory = createJmsConnectionFactory(properties, factoryCustomizers); return new JmsPoolConnectionFactoryFactory(properties.getPool()) .createPooledConnectionFactory(factory); } 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 88f0c65e18498..fd0f5905af41c 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 @@ -5,7 +5,6 @@ import com.azure.spring.cloud.autoconfigure.jms.properties.AzureServiceBusJmsProperties; import com.azure.spring.cloud.core.implementation.connectionstring.ServiceBusConnectionString; -import com.azure.spring.cloud.service.implementation.passwordless.AzureServiceBusPasswordlessProperties; import org.apache.qpid.jms.policy.JmsDefaultPrefetchPolicy; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -20,17 +19,15 @@ public class ServiceBusJmsConnectionFactoryFactory { private final AzureServiceBusJmsProperties properties; private final List factoryCustomizers; - private final AzureServiceBusPasswordlessProperties serviceBusPasswordlessProperties; private static final String AMQP_URI_FORMAT = "amqps://%s?amqp.idleTimeout=%d"; ServiceBusJmsConnectionFactoryFactory(AzureServiceBusJmsProperties properties, - List factoryCustomizers, - AzureServiceBusPasswordlessProperties serviceBusPasswordlessProperties) { + List factoryCustomizers) { Assert.notNull(properties, "Properties must not be null"); this.properties = properties; - this.serviceBusPasswordlessProperties = serviceBusPasswordlessProperties; this.factoryCustomizers = (factoryCustomizers != null) ? factoryCustomizers : Collections.emptyList(); + } T createConnectionFactory(Class factoryClass) { @@ -60,20 +57,12 @@ private void setPrefetchPolicy(T fact private T createConnectionFactoryInstance(Class factoryClass) { try { T factory; - String remoteUrl = null; - String username = null; - String password = null; - if (serviceBusPasswordlessProperties != null && properties.getNameSpace() != null) { - remoteUrl = String.format(AMQP_URI_FORMAT, - properties.getNameSpace() + serviceBusPasswordlessProperties.getProfile().getEnvironment().getServiceBusDomainName(), - properties.getIdleTimeout().toMillis()); - } else if (properties.getConnectionString() != null) { - ServiceBusConnectionString serviceBusConnectionString = new ServiceBusConnectionString(properties.getConnectionString()); - String host = serviceBusConnectionString.getEndpointUri().getHost(); - remoteUrl = String.format(AMQP_URI_FORMAT, host, properties.getIdleTimeout().toMillis()); - username = serviceBusConnectionString.getSharedAccessKeyName(); - password = serviceBusConnectionString.getSharedAccessKey(); - } + 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(); if (StringUtils.hasLength(username) && StringUtils.hasLength(password)) { factory = factoryClass.getConstructor(String.class, String.class, String.class) 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 index 9d4733d13e630..4e53d4194f3db 100644 --- 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 @@ -4,6 +4,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.implementation.util.AzurePropertiesUtils; import com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties; import com.azure.spring.cloud.service.implementation.passwordless.AzureServiceBusPasswordlessProperties; @@ -26,6 +27,8 @@ @ConditionalOnProperty(value = "spring.jms.servicebus.passwordless-enabled", havingValue = "true") public class ServiceBusJmsPasswordlessConfiguration { + private static final String AMQP_URI_FORMAT = "amqps://%s?amqp.idleTimeout=%d"; + @Bean @ConfigurationProperties(prefix = "spring.jms.servicebus") AzureServiceBusPasswordlessProperties serviceBusPasswordlessProperties() { @@ -34,18 +37,25 @@ AzureServiceBusPasswordlessProperties serviceBusPasswordlessProperties() { @Bean @ConditionalOnMissingBean - AzureServiceBusCredentialSupplier azureServiceBusCredentialSupplier(AzureGlobalProperties azureGlobalProperties, AzureServiceBusPasswordlessProperties serviceBusPasswordlessProperties) { + AzureServiceBusJmsCredentialSupplier azureServiceBusJmsCredentialSupplier(AzureGlobalProperties azureGlobalProperties, AzureServiceBusPasswordlessProperties serviceBusPasswordlessProperties) { Properties properties = mergeAzureProperties(azureGlobalProperties, serviceBusPasswordlessProperties).toProperties(); - return new AzureServiceBusCredentialSupplier(properties); + return new AzureServiceBusJmsCredentialSupplier(properties); } @Bean - ServiceBusJmsConnectionFactoryCustomizer jmsAADAuthenticationCustomizer(AzureServiceBusCredentialSupplier credentialSupplier) { + ServiceBusJmsConnectionFactoryCustomizer jmsAADAuthenticationCustomizer(AzureServiceBusJmsCredentialSupplier credentialSupplier, + AzureServiceBusJmsProperties properties, + AzureServiceBusPasswordlessProperties serviceBusPasswordlessProperties) { return factory -> { factory.setExtension(JmsConnectionExtensions.USERNAME_OVERRIDE.toString(), (connection, uri) -> "$jwt"); factory.setExtension(JmsConnectionExtensions.PASSWORD_OVERRIDE.toString(), (connection, uri) -> - credentialSupplier.get() + credentialSupplier.get() ); + + String remoteUrl = String.format(AMQP_URI_FORMAT, + properties.getNameSpace() + serviceBusPasswordlessProperties.getProfile().getEnvironment().getServiceBusDomainName(), + properties.getIdleTimeout().toMillis()); + factory.setRemoteURI(remoteUrl); }; } 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 index 773a37134310f..a975e3109eee5 100644 --- 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 @@ -34,7 +34,7 @@ void testPropertyEnabledIsFalse() { .withBean(AzureGlobalProperties.class, () -> azureProperties) .run(context -> { assertThat(context).doesNotHaveBean(AzureServiceBusPasswordlessProperties.class); - assertThat(context).doesNotHaveBean(AzureServiceBusCredentialSupplier.class); + assertThat(context).doesNotHaveBean(AzureServiceBusJmsCredentialSupplier.class); assertThat(context).doesNotHaveBean(ServiceBusJmsConnectionFactoryCustomizer.class); }); } @@ -51,7 +51,7 @@ void testPropertyEnabledIsTrue() { .withBean(AzureGlobalProperties.class, () -> azureProperties) .run(context -> { assertThat(context).hasSingleBean(AzureServiceBusPasswordlessProperties.class); - assertThat(context).hasSingleBean(AzureServiceBusCredentialSupplier.class); + assertThat(context).hasSingleBean(AzureServiceBusJmsCredentialSupplier.class); assertThat(context).hasSingleBean(ServiceBusJmsConnectionFactoryCustomizer.class); }); } @@ -85,7 +85,7 @@ void testServiceBusPasswordlessProperties() { .withBean(AzureGlobalProperties.class, () -> azureProperties) .run(context -> { assertThat(context).hasSingleBean(AzureServiceBusPasswordlessProperties.class); - assertThat(context).hasSingleBean(AzureServiceBusCredentialSupplier.class); + assertThat(context).hasSingleBean(AzureServiceBusJmsCredentialSupplier.class); assertThat(context).hasSingleBean(ServiceBusJmsConnectionFactoryCustomizer.class); AzureServiceBusPasswordlessProperties properties = context.getBean(AzureServiceBusPasswordlessProperties.class); assertThat(properties.getScopes()).isEqualTo("scopes"); @@ -109,7 +109,7 @@ void testServiceBusPasswordlessProperties() { } @Test - void testAzureServiceBusCredentialSupplier() { + void testAzureServiceBusJmsCredentialSupplier() { AzureGlobalProperties azureProperties = new AzureGlobalProperties(); azureProperties.getProfile().setCloudType(AZURE); azureProperties.getProfile().getEnvironment().setActiveDirectoryEndpoint("abc"); @@ -119,15 +119,15 @@ void testAzureServiceBusCredentialSupplier() { .withPropertyValues("spring.jms.servicebus.passwordless-enabled=true") .withBean(AzureGlobalProperties.class, () -> azureProperties); - try (MockedConstruction supplierMockedConstruction = mockConstruction(AzureServiceBusCredentialSupplier.class, - (azureServiceBusCredentialSupplierMocker, mockerContext) -> { - when(azureServiceBusCredentialSupplierMocker.get()).thenReturn("fake-token"); + try (MockedConstruction supplierMockedConstruction = mockConstruction(AzureServiceBusJmsCredentialSupplier.class, + (azureServiceBusJmsCredentialSupplierMocker, mockerContext) -> { + when(azureServiceBusJmsCredentialSupplierMocker.get()).thenReturn("fake-token"); contextRunner.run(runnerContext -> { assertThat(runnerContext).hasSingleBean(AzureServiceBusPasswordlessProperties.class); - assertThat(runnerContext).hasSingleBean(AzureServiceBusCredentialSupplier.class); + assertThat(runnerContext).hasSingleBean(AzureServiceBusJmsCredentialSupplier.class); assertThat(runnerContext).hasSingleBean(ServiceBusJmsConnectionFactoryCustomizer.class); - runnerContext.getBean(AzureServiceBusCredentialSupplier.class).get().equals("fake-token"); + runnerContext.getBean(AzureServiceBusJmsCredentialSupplier.class).get().equals("fake-token"); }); })) { diff --git a/sdk/spring/spring-cloud-azure-integration-tests/src/test/java/com/azure/spring/cloud/integration/tests/servicebus/jms/ServiceBusJmsPasswordlessIT.java b/sdk/spring/spring-cloud-azure-integration-tests/src/test/java/com/azure/spring/cloud/integration/tests/servicebus/jms/ServiceBusJmsPasswordlessIT.java index 9ba55a00d0276..51d5fc1aca61f 100644 --- a/sdk/spring/spring-cloud-azure-integration-tests/src/test/java/com/azure/spring/cloud/integration/tests/servicebus/jms/ServiceBusJmsPasswordlessIT.java +++ b/sdk/spring/spring-cloud-azure-integration-tests/src/test/java/com/azure/spring/cloud/integration/tests/servicebus/jms/ServiceBusJmsPasswordlessIT.java @@ -28,7 +28,7 @@ public class ServiceBusJmsPasswordlessIT { @Test @Timeout(70) - public void testServiceBusJmsOperation() throws InterruptedException { + void testServiceBusJmsOperation() throws InterruptedException { LOGGER.info("ServiceBusJmsPasswordlessIT begin."); jmsTemplate.convertAndSend(QUEUE_NAME, DATA); LOGGER.info("Send message: {}", DATA); @@ -38,7 +38,7 @@ public void testServiceBusJmsOperation() throws InterruptedException { } @JmsListener(destination = QUEUE_NAME, containerFactory = "jmsListenerContainerFactory") - public void receiveQueueMessage(String message) throws InterruptedException { + void receiveQueueMessage(String message) throws InterruptedException { LOGGER.info("Received message from queue: {}", message); EXCHANGER.exchange(message); } From 3776c5656f82163a8b30cf17283271da150aab7c Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Wed, 15 Feb 2023 11:46:45 +0800 Subject: [PATCH 08/29] fix ut errors --- .../ServiceBusJmsPasswordlessConfigurationTest.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) 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 index a975e3109eee5..9b3ce02385666 100644 --- 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 @@ -4,6 +4,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.service.implementation.passwordless.AzureServiceBusPasswordlessProperties; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -46,9 +47,13 @@ void testPropertyEnabledIsTrue() { azureProperties.getProfile().getEnvironment().setActiveDirectoryEndpoint("abc"); azureProperties.getProfile().getEnvironment().setActiveDirectoryGraphApiVersion("v2"); + AzureServiceBusJmsProperties jmsProperties = new AzureServiceBusJmsProperties(); 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) + .withBean(AzureServiceBusJmsProperties.class, () -> jmsProperties) .run(context -> { assertThat(context).hasSingleBean(AzureServiceBusPasswordlessProperties.class); assertThat(context).hasSingleBean(AzureServiceBusJmsCredentialSupplier.class); @@ -62,9 +67,11 @@ void testServiceBusPasswordlessProperties() { azureProperties.getProfile().setCloudType(AZURE); azureProperties.getProfile().getEnvironment().setActiveDirectoryEndpoint("abc"); azureProperties.getProfile().getEnvironment().setActiveDirectoryGraphApiVersion("v2"); - + AzureServiceBusJmsProperties jmsProperties = new AzureServiceBusJmsProperties(); 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", @@ -83,6 +90,7 @@ void testServiceBusPasswordlessProperties() { "spring.jms.servicebus.proxy.password=password", "spring.jms.servicebus.proxy.type=type") .withBean(AzureGlobalProperties.class, () -> azureProperties) + .withBean(AzureServiceBusJmsProperties.class, () -> jmsProperties) .run(context -> { assertThat(context).hasSingleBean(AzureServiceBusPasswordlessProperties.class); assertThat(context).hasSingleBean(AzureServiceBusJmsCredentialSupplier.class); From 7545cb8c40c2a41540fa019fcf5ffeee49e07450 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Wed, 15 Feb 2023 14:14:34 +0800 Subject: [PATCH 09/29] fix it errors --- ...erviceBusJmsPasswordlessConfiguration.java | 38 ++++++++++++++----- 1 file changed, 28 insertions(+), 10 deletions(-) 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 index 4e53d4194f3db..73c35d4119b71 100644 --- 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 @@ -3,9 +3,12 @@ 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.core.implementation.util.AzurePropertiesUtils; +import com.azure.spring.cloud.core.provider.connectionstring.ServiceConnectionStringProvider; +import com.azure.spring.cloud.core.service.AzureServiceType; import com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties; import com.azure.spring.cloud.service.implementation.passwordless.AzureServiceBusPasswordlessProperties; import org.apache.qpid.jms.JmsConnectionExtensions; @@ -16,8 +19,6 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import java.util.Properties; - /** * {@link EnableAutoConfiguration Auto-configuration} for Azure Service Bus JMS passwordless support. * @@ -31,15 +32,15 @@ public class ServiceBusJmsPasswordlessConfiguration { @Bean @ConfigurationProperties(prefix = "spring.jms.servicebus") - AzureServiceBusPasswordlessProperties serviceBusPasswordlessProperties() { - return new AzureServiceBusPasswordlessProperties(); + AzureServiceBusPasswordlessProperties serviceBusPasswordlessProperties(AzureGlobalProperties azureGlobalProperties) { + AzureServiceBusPasswordlessProperties properties = new AzureServiceBusPasswordlessProperties(); + return mergeAzureProperties(azureGlobalProperties, properties); } @Bean @ConditionalOnMissingBean - AzureServiceBusJmsCredentialSupplier azureServiceBusJmsCredentialSupplier(AzureGlobalProperties azureGlobalProperties, AzureServiceBusPasswordlessProperties serviceBusPasswordlessProperties) { - Properties properties = mergeAzureProperties(azureGlobalProperties, serviceBusPasswordlessProperties).toProperties(); - return new AzureServiceBusJmsCredentialSupplier(properties); + AzureServiceBusJmsCredentialSupplier azureServiceBusJmsCredentialSupplier(AzureServiceBusPasswordlessProperties serviceBusPasswordlessProperties) { + return new AzureServiceBusJmsCredentialSupplier(serviceBusPasswordlessProperties.toProperties()); } @Bean @@ -53,16 +54,33 @@ ServiceBusJmsConnectionFactoryCustomizer jmsAADAuthenticationCustomizer(AzureSer ); String remoteUrl = String.format(AMQP_URI_FORMAT, - properties.getNameSpace() + serviceBusPasswordlessProperties.getProfile().getEnvironment().getServiceBusDomainName(), + properties.getNameSpace() + "." + serviceBusPasswordlessProperties.getProfile().getEnvironment().getServiceBusDomainName(), properties.getIdleTimeout().toMillis()); factory.setRemoteURI(remoteUrl); }; } - private AzurePasswordlessProperties mergeAzureProperties(AzureGlobalProperties azureGlobalProperties, AzurePasswordlessProperties azurePasswordlessProperties) { - AzurePasswordlessProperties mergedProperties = new AzurePasswordlessProperties(); + @Bean + @ConditionalOnMissingProperty(prefix = "spring.jms.servicebus", name = "connection-string") + ServiceConnectionStringProvider ServiceBusJMSConnectionStringProvider() { + return new ServiceConnectionStringProvider() { + @Override + public AzureServiceType.ServiceBus getServiceType() { + return AzureServiceType.SERVICE_BUS; + } + + @Override + public String getConnectionString() { + return "Endpoint=sb://passwordless-fake.servicebus.windows.net/;SharedAccessKeyName=passwordless-fake-accesskeyname;SharedAccessKey=passwordless-fake-key="; + } + }; + } + + private AzureServiceBusPasswordlessProperties mergeAzureProperties(AzureGlobalProperties azureGlobalProperties, AzurePasswordlessProperties azurePasswordlessProperties) { + AzureServiceBusPasswordlessProperties mergedProperties = new AzureServiceBusPasswordlessProperties(); AzurePropertiesUtils.mergeAzureCommonProperties(azureGlobalProperties, azurePasswordlessProperties, mergedProperties); mergedProperties.setScopes(azurePasswordlessProperties.getScopes()); return mergedProperties; } + } From 8085fe4e0446c17d68be47f4f8c2f28e86f94542 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Wed, 15 Feb 2023 14:48:21 +0800 Subject: [PATCH 10/29] fix checkstyle errors --- ...erviceBusJmsPasswordlessConfiguration.java | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) 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 index 73c35d4119b71..e9ed85e9e45a7 100644 --- 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 @@ -62,18 +62,8 @@ ServiceBusJmsConnectionFactoryCustomizer jmsAADAuthenticationCustomizer(AzureSer @Bean @ConditionalOnMissingProperty(prefix = "spring.jms.servicebus", name = "connection-string") - ServiceConnectionStringProvider ServiceBusJMSConnectionStringProvider() { - return new ServiceConnectionStringProvider() { - @Override - public AzureServiceType.ServiceBus getServiceType() { - return AzureServiceType.SERVICE_BUS; - } - - @Override - public String getConnectionString() { - return "Endpoint=sb://passwordless-fake.servicebus.windows.net/;SharedAccessKeyName=passwordless-fake-accesskeyname;SharedAccessKey=passwordless-fake-key="; - } - }; + ServiceConnectionStringProvider serviceBusJmsConnectionStringProvider() { + return new ServiceBusJmsConnectionStringProvider(); } private AzureServiceBusPasswordlessProperties mergeAzureProperties(AzureGlobalProperties azureGlobalProperties, AzurePasswordlessProperties azurePasswordlessProperties) { @@ -83,4 +73,17 @@ private AzureServiceBusPasswordlessProperties mergeAzureProperties(AzureGlobalPr return mergedProperties; } + class ServiceBusJmsConnectionStringProvider implements ServiceConnectionStringProvider { + + @Override + public AzureServiceType.ServiceBus getServiceType() { + return AzureServiceType.SERVICE_BUS; + } + + @Override + public String getConnectionString() { + return "Endpoint=sb://passwordless-fake.servicebus.windows.net/;SharedAccessKeyName=passwordless-fake-accesskeyname;SharedAccessKey=passwordless-fake-key="; + } + } + } From ba71484aab4d984a1d759fb362bc8ab2a8f84261 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Wed, 15 Feb 2023 16:41:00 +0800 Subject: [PATCH 11/29] fix checkstyle errors --- .../jms/ServiceBusJmsPasswordlessConfiguration.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 index e9ed85e9e45a7..46d70c14cc51f 100644 --- 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 @@ -73,7 +73,7 @@ private AzureServiceBusPasswordlessProperties mergeAzureProperties(AzureGlobalPr return mergedProperties; } - class ServiceBusJmsConnectionStringProvider implements ServiceConnectionStringProvider { + static class ServiceBusJmsConnectionStringProvider implements ServiceConnectionStringProvider { @Override public AzureServiceType.ServiceBus getServiceType() { From 81023d6c7edaed81b41290274c6e79ba1ae22f71 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Tue, 21 Feb 2023 18:49:38 +0800 Subject: [PATCH 12/29] 1. code refactor 2. add AzurePasswordlessEnvironmentPostProcessor to filter ineligible functions --- .../jdbc/JdbcPropertiesBeanPostProcessor.java | 57 +++---- .../jms/ServiceBusJmsAutoConfiguration.java | 9 +- ...ServiceBusJmsConnectionFactoryFactory.java | 28 ++-- ...erviceBusJmsPasswordlessConfiguration.java | 59 +------ .../AzureServiceBusJmsProperties.java | 73 +++++++-- ...ePasswordlessEnvironmentPostProcessor.java | 43 +++++ .../passwordless/package-info.java | 7 + ...ureJedisPasswordlessAutoConfiguration.java | 16 +- .../redis/AzureJedisPasswordlessUtil.java | 21 --- ...itional-spring-configuration-metadata.json | 66 ++++---- .../main/resources/META-INF/spring.factories | 3 +- ...bstractAzureJdbcAutoConfigurationTest.java | 2 +- .../JdbcPropertiesBeanPostProcessorTest.java | 14 +- ...essorWithApplicationContextRunnerTest.java | 9 +- .../MySqlAzureJdbcAutoConfigurationTest.java | 1 + ...tgreSqlAzureJdbcAutoConfigurationTest.java | 1 + .../AzureServiceBusJmsPropertiesTests.java | 9 +- .../ServiceBusJmsAutoConfigurationTests.java | 2 + ...ceBusJmsPasswordlessConfigurationTest.java | 30 ++-- ...reKafkaOAuth2BinderConfigurationTests.java | 19 +-- ...zureKafkaOAuth2BootConfigurationTests.java | 34 ++-- ...edisPasswordlessAutoConfigurationTest.java | 2 + sdk/spring/spring-cloud-azure-core/pom.xml | 7 + .../AzurePasswordlessPropertiesUtils.java | 125 ++++++++++++++ .../properties/PasswordlessProperties.java | 111 +++++++++++++ .../kafka/AzureKafkaPropertiesUtils.java | 10 +- ...afkaOAuth2AuthenticateCallbackHandler.java | 40 ++--- .../AzureJdbcPasswordlessProperties.java | 80 +++++++++ .../AzureKafkaPasswordlessProperties.java | 58 +++++++ .../AzurePasswordlessProperties.java | 152 ------------------ .../AzureRedisPasswordlessProperties.java | 50 +++++- ...AzureServiceBusPasswordlessProperties.java | 37 ----- .../kafka/AzureKafkaPropertiesUtilsTest.java | 8 +- ...OAuth2AuthenticateCallbackHandlerTest.java | 38 ++--- .../AzureRedisPasswordlessPropertiesTest.java | 2 +- 35 files changed, 755 insertions(+), 468 deletions(-) create mode 100644 sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/passwordless/AzurePasswordlessEnvironmentPostProcessor.java create mode 100644 sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/passwordless/package-info.java create mode 100644 sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/implementation/util/AzurePasswordlessPropertiesUtils.java create mode 100644 sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/properties/PasswordlessProperties.java create mode 100644 sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureJdbcPasswordlessProperties.java create mode 100644 sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureKafkaPasswordlessProperties.java delete mode 100644 sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzurePasswordlessProperties.java delete mode 100644 sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureServiceBusPasswordlessProperties.java 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..6949361d08e40 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."); @@ -77,9 +78,9 @@ public Object postProcessBeforeInitialization(Object bean, String beanName) thro boolean isPasswordProvided = StringUtils.hasText(dataSourceProperties.getPassword()); if (isPasswordProvided) { LOGGER.debug( - "If you are using Azure hosted services," - + "it is encouraged to use the passwordless feature. " - + "Please refer to https://aka.ms/passwordless-connections."); + "If you are using Azure hosted services," + + "it is encouraged to use the passwordless feature. " + + "Please refer to https://aka.ms/passwordless-connections."); return bean; } @@ -105,17 +106,17 @@ private void enhanceUserAgent(DatabaseType databaseType, JdbcConnectionStringEnh if (DatabaseType.MYSQL == databaseType) { Map enhancedAttributes = new HashMap<>(); enhancedAttributes.put(MYSQL_PROPERTY_CONNECTION_ATTRIBUTES_ATTRIBUTE_EXTENSION_VERSION, - AzureSpringIdentifier.AZURE_SPRING_MYSQL_OAUTH); + AzureSpringIdentifier.AZURE_SPRING_MYSQL_OAUTH); enhancer.enhancePropertyAttributes( - MYSQL_PROPERTY_NAME_CONNECTION_ATTRIBUTES, - enhancedAttributes, - MYSQL_PROPERTY_CONNECTION_ATTRIBUTES_DELIMITER, - MYSQL_PROPERTY_CONNECTION_ATTRIBUTES_KV_DELIMITER + MYSQL_PROPERTY_NAME_CONNECTION_ATTRIBUTES, + enhancedAttributes, + MYSQL_PROPERTY_CONNECTION_ATTRIBUTES_DELIMITER, + MYSQL_PROPERTY_CONNECTION_ATTRIBUTES_KV_DELIMITER ); } else if (DatabaseType.POSTGRESQL == databaseType) { Map enhancedProperties = new HashMap<>(); enhancedProperties.put(POSTGRESQL_PROPERTY_NAME_APPLICATION_NAME, - AzureSpringIdentifier.AZURE_SPRING_POSTGRESQL_OAUTH); + AzureSpringIdentifier.AZURE_SPRING_POSTGRESQL_OAUTH); // Set property assumeMinServerVersion with value "9.0.0" here for the following reasons: // 1. We need to set application_name in paramList to build connections with postgresql server, in order to do that, the number of assumeVersion must >= 9.0.0. // https://github.com/pgjdbc/pgjdbc/blob/98c04a0c903e90f2d5d10a09baf1f753747b2556/pgjdbc/src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java#L360 @@ -123,21 +124,19 @@ private void enhanceUserAgent(DatabaseType databaseType, JdbcConnectionStringEnh // https://learn.microsoft.com/azure/postgresql/single-server/concepts-supported-versions // https://learn.microsoft.com/azure/postgresql/flexible-server/concepts-supported-versions enhancedProperties.put(POSTGRESQL_PROPERTY_NAME_ASSUME_MIN_SERVER_VERSION, - POSTGRESQL_PROPERTY_VALUE_ASSUME_MIN_SERVER_VERSION); + POSTGRESQL_PROPERTY_VALUE_ASSUME_MIN_SERVER_VERSION); enhancer.enhanceProperties(enhancedProperties, true); } } - 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); - } + 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_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/ServiceBusJmsAutoConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/jms/ServiceBusJmsAutoConfiguration.java index 9bd87e982bc13..6c49a46c9f5aa 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 @@ -21,6 +21,7 @@ import org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration; import org.springframework.boot.autoconfigure.jms.JmsProperties; import org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration; +import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -46,10 +47,16 @@ AzureServiceBusResourceManagerAutoConfiguration.class }) @ConditionalOnProperty(value = "spring.jms.servicebus.enabled", matchIfMissing = true) @ConditionalOnClass({ ConnectionFactory.class, JmsConnectionFactory.class, JmsTemplate.class }) -@EnableConfigurationProperties({AzureServiceBusJmsProperties.class, JmsProperties.class}) +@EnableConfigurationProperties({JmsProperties.class}) @Import({ServiceBusJmsPasswordlessConfiguration.class, ServiceBusJmsConnectionFactoryConfiguration.class, ServiceBusJmsContainerConfiguration.class}) public class ServiceBusJmsAutoConfiguration { + @Bean + @ConfigurationProperties(prefix = AzureServiceBusJmsProperties.PREFIX) + AzureServiceBusJmsProperties serviceBusJmsProperties() { + return new AzureServiceBusJmsProperties(); + } + @Bean @ConditionalOnExpression("'premium'.equalsIgnoreCase('${spring.jms.servicebus.pricing-tier}')") ServiceBusJmsConnectionFactoryCustomizer amqpOpenPropertiesCustomizer(ObjectProvider azureServiceBusJmsCredentialSupplier) { 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..065e04fca2ec7 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 index 46d70c14cc51f..82457c368e186 100644 --- 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 @@ -3,19 +3,13 @@ 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.core.implementation.util.AzurePropertiesUtils; -import com.azure.spring.cloud.core.provider.connectionstring.ServiceConnectionStringProvider; -import com.azure.spring.cloud.core.service.AzureServiceType; -import com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties; -import com.azure.spring.cloud.service.implementation.passwordless.AzureServiceBusPasswordlessProperties; +import com.azure.spring.cloud.core.implementation.util.AzurePasswordlessPropertiesUtils; 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.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -28,62 +22,25 @@ @ConditionalOnProperty(value = "spring.jms.servicebus.passwordless-enabled", havingValue = "true") public class ServiceBusJmsPasswordlessConfiguration { - private static final String AMQP_URI_FORMAT = "amqps://%s?amqp.idleTimeout=%d"; - - @Bean - @ConfigurationProperties(prefix = "spring.jms.servicebus") - AzureServiceBusPasswordlessProperties serviceBusPasswordlessProperties(AzureGlobalProperties azureGlobalProperties) { - AzureServiceBusPasswordlessProperties properties = new AzureServiceBusPasswordlessProperties(); - return mergeAzureProperties(azureGlobalProperties, properties); - } - @Bean @ConditionalOnMissingBean - AzureServiceBusJmsCredentialSupplier azureServiceBusJmsCredentialSupplier(AzureServiceBusPasswordlessProperties serviceBusPasswordlessProperties) { - return new AzureServiceBusJmsCredentialSupplier(serviceBusPasswordlessProperties.toProperties()); + AzureServiceBusJmsCredentialSupplier azureServiceBusJmsCredentialSupplier(AzureGlobalProperties azureGlobalProperties, AzureServiceBusJmsProperties azureServiceBusJmsProperties) { + return new AzureServiceBusJmsCredentialSupplier(mergeAzureProperties(azureGlobalProperties, azureServiceBusJmsProperties).toPasswordlessProperties()); } @Bean - ServiceBusJmsConnectionFactoryCustomizer jmsAADAuthenticationCustomizer(AzureServiceBusJmsCredentialSupplier credentialSupplier, - AzureServiceBusJmsProperties properties, - AzureServiceBusPasswordlessProperties serviceBusPasswordlessProperties) { + 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() + credentialSupplier.get() ); - - String remoteUrl = String.format(AMQP_URI_FORMAT, - properties.getNameSpace() + "." + serviceBusPasswordlessProperties.getProfile().getEnvironment().getServiceBusDomainName(), - properties.getIdleTimeout().toMillis()); - factory.setRemoteURI(remoteUrl); }; } - @Bean - @ConditionalOnMissingProperty(prefix = "spring.jms.servicebus", name = "connection-string") - ServiceConnectionStringProvider serviceBusJmsConnectionStringProvider() { - return new ServiceBusJmsConnectionStringProvider(); - } - - private AzureServiceBusPasswordlessProperties mergeAzureProperties(AzureGlobalProperties azureGlobalProperties, AzurePasswordlessProperties azurePasswordlessProperties) { - AzureServiceBusPasswordlessProperties mergedProperties = new AzureServiceBusPasswordlessProperties(); - AzurePropertiesUtils.mergeAzureCommonProperties(azureGlobalProperties, azurePasswordlessProperties, mergedProperties); - mergedProperties.setScopes(azurePasswordlessProperties.getScopes()); + private AzureServiceBusJmsProperties mergeAzureProperties(AzureGlobalProperties azureGlobalProperties, AzureServiceBusJmsProperties azurePasswordlessProperties) { + AzureServiceBusJmsProperties mergedProperties = new AzureServiceBusJmsProperties(); + AzurePasswordlessPropertiesUtils.mergeAzureCommonProperties(azureGlobalProperties, azurePasswordlessProperties, mergedProperties); return mergedProperties; } - - static class ServiceBusJmsConnectionStringProvider implements ServiceConnectionStringProvider { - - @Override - public AzureServiceType.ServiceBus getServiceType() { - return AzureServiceType.SERVICE_BUS; - } - - @Override - public String getConnectionString() { - return "Endpoint=sb://passwordless-fake.servicebus.windows.net/;SharedAccessKeyName=passwordless-fake-accesskeyname;SharedAccessKey=passwordless-fake-key="; - } - } - } 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 6fe88147dc936..79ab37f2f49c6 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.core.properties.PasswordlessProperties; +import com.azure.spring.cloud.core.properties.authentication.TokenCredentialProperties; +import com.azure.spring.cloud.core.properties.profile.AzureProfileProperties; import org.springframework.beans.factory.InitializingBean; import org.springframework.boot.autoconfigure.jms.JmsPoolConnectionFactoryProperties; import org.springframework.boot.context.properties.ConfigurationProperties; @@ -11,18 +14,41 @@ 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 { + 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; + + // Whether to enable supporting azure identity token credentials + private boolean passwordlessEnabled = false; + + 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); + } + }; /** * Service Bus JMS properties prefix. */ public static final String PREFIX = "spring.jms.servicebus"; + private AzureProfileProperties profile = new AzureProfileProperties(); + + private String scopes; + + private TokenCredentialProperties credential = new TokenCredentialProperties(); + /** * Whether to enable Servive Bus JMS autoconfiguration. */ @@ -52,11 +78,6 @@ public class AzureServiceBusJmsProperties implements InitializingBean { */ private String pricingTier; - /** - * Whether to enable supporting authentication with Azure AD. - */ - private boolean passwordlessEnabled = false; - private final Listener listener = new Listener(); private final PrefetchPolicy prefetchPolicy = new PrefetchPolicy(); @@ -184,11 +205,11 @@ public void setNameSpace(String nameSpace) { this.nameSpace = nameSpace; } - private boolean isPasswordlessEnabled() { - return passwordlessEnabled; + public void setScopes(String scopes) { + this.scopes = scopes; } - void setPasswordlessEnabled(boolean passwordlessEnabled) { + public void setPasswordlessEnabled(boolean passwordlessEnabled) { this.passwordlessEnabled = passwordlessEnabled; } @@ -215,6 +236,34 @@ public void afterPropertiesSet() throws Exception { } + @Override + public String getScopes() { + return this.scopes == null ? getScopesFromMap() : this.scopes; + } + + @Override + public boolean isPasswordlessEnabled() { + return passwordlessEnabled; + } + + @Override + public AzureProfileProperties getProfile() { + return profile; + } + + public void setProfile(AzureProfileProperties profile) { + this.profile = profile; + } + + @Override + public TokenCredentialProperties getCredential() { + return credential; + } + + public void setCredential(TokenCredentialProperties credential) { + this.credential = credential; + } + /** * Properties to configure {@link org.apache.qpid.jms.policy.JmsDefaultPrefetchPolicy} for {@link * org.apache.qpid.jms.JmsConnectionFactory} . @@ -454,4 +503,8 @@ public void setPhase(Integer phase) { this.phase = phase; } } + + private String getScopesFromMap() { + 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/passwordless/AzurePasswordlessEnvironmentPostProcessor.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/passwordless/AzurePasswordlessEnvironmentPostProcessor.java new file mode 100644 index 0000000000000..c962be40eaa3a --- /dev/null +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/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.passwordless; + +import com.azure.cosmos.implementation.guava25.collect.Lists; +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.List; +import java.util.Properties; + +/** + * Add properties to 'spring.cloud.function.ineligible-definitions' to filter ineligible functions that used by passwordless autoconfiguration. + * + * @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 = Lists.newArrayList(); + 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/passwordless/package-info.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/passwordless/package-info.java new file mode 100644 index 0000000000000..a5407daf14788 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/passwordless/package-info.java @@ -0,0 +1,7 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +/** + * Spring Cloud Azure's auto-configuration for Spring Message. + */ +package com.azure.spring.cloud.autoconfigure.passwordless; 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 ff480c13f0a9a..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,8 +3,6 @@ package com.azure.spring.cloud.autoconfigure.redis; -import com.azure.spring.cloud.autoconfigure.context.AzureGlobalProperties; -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; @@ -20,9 +18,6 @@ import java.net.URI; import java.net.URISyntaxException; -import static com.azure.spring.cloud.core.implementation.util.AzurePropertiesUtils.copyPropertiesIgnoreNull; -import static com.azure.spring.cloud.core.implementation.util.AzurePropertiesUtils.copyPropertiesIgnoreTargetNonNull; - final class AzureJedisPasswordlessUtil { private static final int AZURE_REDIS_PORT = 6380; @@ -32,22 +27,6 @@ final class AzureJedisPasswordlessUtil { private AzureJedisPasswordlessUtil() { } - static AzureRedisPasswordlessProperties mergeAzureProperties(AzureGlobalProperties azureGlobalProperties, AzureRedisPasswordlessProperties redisPasswordlessProperties) { - AzureRedisPasswordlessProperties target = new AzureRedisPasswordlessProperties(); - copyPropertiesIgnoreNull(redisPasswordlessProperties.getScopes(), target.getScopes()); - copyPropertiesIgnoreNull(redisPasswordlessProperties.getCredential(), target.getCredential()); - copyPropertiesIgnoreNull(redisPasswordlessProperties.getProfile(), target.getProfile()); - copyPropertiesIgnoreNull(redisPasswordlessProperties.getClient(), target.getClient()); - copyPropertiesIgnoreNull(redisPasswordlessProperties.getProxy(), target.getProxy()); - - if (azureGlobalProperties != null) { - copyPropertiesIgnoreTargetNonNull(azureGlobalProperties.getProfile(), target.getProfile()); - copyPropertiesIgnoreTargetNonNull(azureGlobalProperties.getCredential(), target.getCredential()); - } - - return redisPasswordlessProperties; - } - 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 e23b2498da585..f7e530cc7a44d 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,204 +1536,204 @@ "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.AzurePasswordlessProperties2" }, { "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.AzurePasswordlessProperties2" }, { "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.AzurePasswordlessProperties2" }, { "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.AzurePasswordlessProperties2" }, { "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.AzurePasswordlessProperties2" }, { "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.AzurePasswordlessProperties2" }, { "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.AzurePasswordlessProperties2", "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.AzurePasswordlessProperties2" }, { "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.AzurePasswordlessProperties2" }, { "name": "spring.datasource.azure.profile.cloud-type", "type": "java.lang.String", "description": "Name of the Azure cloud to connect to.", - "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties" + "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties2" }, { "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.AzurePasswordlessProperties2", "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.AzurePasswordlessProperties2" }, { "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.AzurePasswordlessProperties2" }, { "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.AzurePasswordlessProperties2" }, { "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.AzurePasswordlessProperties2" }, { "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.AzurePasswordlessProperties2" }, { "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.AzurePasswordlessProperties2" }, { "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.AzurePasswordlessProperties2", "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.AzurePasswordlessProperties2" }, { "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.AzurePasswordlessProperties2" }, { "name": "spring.redis.azure.profile.cloud-type", "type": "java.lang.String", "description": "Name of the Azure cloud to connect to.", - "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties" + "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties2" }, { "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.AzurePasswordlessProperties2", "defaultValue": false }, { "name": "spring.jms.servicebus.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.AzurePasswordlessProperties2" }, { "name": "spring.jms.servicebus.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.AzurePasswordlessProperties2" }, { "name": "spring.jms.servicebus.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.AzurePasswordlessProperties2" }, { "name": "spring.jms.servicebus.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.AzurePasswordlessProperties2" }, { "name": "spring.jms.servicebus.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.AzurePasswordlessProperties2" }, { "name": "spring.jms.servicebus.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.AzurePasswordlessProperties2" }, { "name": "spring.jms.servicebus.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.AzurePasswordlessProperties2", "defaultValue": false }, { "name": "spring.jms.servicebus.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.AzurePasswordlessProperties2" }, { "name": "spring.jms.servicebus.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.AzurePasswordlessProperties2" }, { "name": "spring.jms.servicebus.profile.cloud-type", "type": "java.lang.String", "description": "Name of the Azure cloud to connect to.", - "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties" + "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties2" }, { "name": "spring.jms.servicebus.passwordless-enabled", "type": "java.lang.Boolean", "description": "Whether to enable passwordless connections to Azure ServiceBus 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.AzurePasswordlessProperties2", "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..1adb7c079c093 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.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 e76292c87539b..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,9 +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.Import; +import org.springframework.context.annotation.Bean; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -63,9 +64,13 @@ void testPasswordlessEnabled() { } @EnableConfigurationProperties - @Import(AzureServiceBusJmsProperties.class) 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 b0c7362b82c6f..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) { 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 index 9b3ce02385666..bcbba7d264be1 100644 --- 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 @@ -5,12 +5,13 @@ import com.azure.spring.cloud.autoconfigure.context.AzureGlobalProperties; import com.azure.spring.cloud.autoconfigure.jms.properties.AzureServiceBusJmsProperties; -import com.azure.spring.cloud.service.implementation.passwordless.AzureServiceBusPasswordlessProperties; 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; @@ -32,9 +33,10 @@ void testPropertyEnabledIsFalse() { 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(AzureServiceBusPasswordlessProperties.class); assertThat(context).doesNotHaveBean(AzureServiceBusJmsCredentialSupplier.class); assertThat(context).doesNotHaveBean(ServiceBusJmsConnectionFactoryCustomizer.class); }); @@ -47,15 +49,12 @@ void testPropertyEnabledIsTrue() { azureProperties.getProfile().getEnvironment().setActiveDirectoryEndpoint("abc"); azureProperties.getProfile().getEnvironment().setActiveDirectoryGraphApiVersion("v2"); - AzureServiceBusJmsProperties jmsProperties = new AzureServiceBusJmsProperties(); 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) - .withBean(AzureServiceBusJmsProperties.class, () -> jmsProperties) .run(context -> { - assertThat(context).hasSingleBean(AzureServiceBusPasswordlessProperties.class); assertThat(context).hasSingleBean(AzureServiceBusJmsCredentialSupplier.class); assertThat(context).hasSingleBean(ServiceBusJmsConnectionFactoryCustomizer.class); }); @@ -67,7 +66,6 @@ void testServiceBusPasswordlessProperties() { azureProperties.getProfile().setCloudType(AZURE); azureProperties.getProfile().getEnvironment().setActiveDirectoryEndpoint("abc"); azureProperties.getProfile().getEnvironment().setActiveDirectoryGraphApiVersion("v2"); - AzureServiceBusJmsProperties jmsProperties = new AzureServiceBusJmsProperties(); this.contextRunner .withPropertyValues("spring.jms.servicebus.passwordless-enabled=true") .withPropertyValues("spring.jms.servicebus.namespace=testnamespace") @@ -90,12 +88,10 @@ void testServiceBusPasswordlessProperties() { "spring.jms.servicebus.proxy.password=password", "spring.jms.servicebus.proxy.type=type") .withBean(AzureGlobalProperties.class, () -> azureProperties) - .withBean(AzureServiceBusJmsProperties.class, () -> jmsProperties) .run(context -> { - assertThat(context).hasSingleBean(AzureServiceBusPasswordlessProperties.class); assertThat(context).hasSingleBean(AzureServiceBusJmsCredentialSupplier.class); assertThat(context).hasSingleBean(ServiceBusJmsConnectionFactoryCustomizer.class); - AzureServiceBusPasswordlessProperties properties = context.getBean(AzureServiceBusPasswordlessProperties.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"); @@ -107,12 +103,7 @@ void testServiceBusPasswordlessProperties() { assertThat(properties.getCredential().getUsername()).isEqualTo("username"); assertThat(properties.getCredential().getPassword()).isEqualTo("password"); assertThat(properties.getCredential().isManagedIdentityEnabled()).isTrue(); - assertThat(properties.getClient().getApplicationId()).isEqualTo("application-id"); - assertThat(properties.getProxy().getHostname()).isEqualTo("hostname"); - assertThat(properties.getProxy().getUsername()).isEqualTo("username"); - assertThat(properties.getProxy().getPort()).isEqualTo(1111); - assertThat(properties.getProxy().getPassword()).isEqualTo("password"); - assertThat(properties.getProxy().getType()).isEqualTo("type"); + }); } @@ -132,7 +123,6 @@ void testAzureServiceBusJmsCredentialSupplier() { when(azureServiceBusJmsCredentialSupplierMocker.get()).thenReturn("fake-token"); contextRunner.run(runnerContext -> { - assertThat(runnerContext).hasSingleBean(AzureServiceBusPasswordlessProperties.class); assertThat(runnerContext).hasSingleBean(AzureServiceBusJmsCredentialSupplier.class); assertThat(runnerContext).hasSingleBean(ServiceBusJmsConnectionFactoryCustomizer.class); runnerContext.getBean(AzureServiceBusJmsCredentialSupplier.class).get().equals("fake-token"); @@ -145,7 +135,13 @@ void testAzureServiceBusJmsCredentialSupplier() { } @EnableConfigurationProperties - @Import(ServiceBusJmsPasswordlessConfiguration.class) + @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/kafka/AzureKafkaOAuth2BinderConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/kafka/AzureKafkaOAuth2BinderConfigurationTests.java index ade382a2b1169..91a4d8057a28d 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/kafka/AzureKafkaOAuth2BinderConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/kafka/AzureKafkaOAuth2BinderConfigurationTests.java @@ -2,15 +2,13 @@ // Licensed under the MIT License. package com.azure.spring.cloud.autoconfigure.kafka; -import com.azure.core.credential.TokenCredential; import com.azure.identity.DefaultAzureCredential; import com.azure.identity.ManagedIdentityCredential; +import com.azure.identity.extensions.implementation.credential.provider.TokenCredentialProvider; import com.azure.spring.cloud.autoconfigure.context.AzureGlobalProperties; import com.azure.spring.cloud.autoconfigure.context.AzureGlobalPropertiesAutoConfiguration; import com.azure.spring.cloud.autoconfigure.context.AzureTokenCredentialAutoConfiguration; -import com.azure.spring.cloud.core.credential.AzureCredentialResolver; import com.azure.spring.cloud.service.implementation.kafka.KafkaOAuth2AuthenticateCallbackHandler; -import com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties; import org.apache.kafka.common.config.types.Password; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; @@ -180,21 +178,18 @@ void testOAuth2ConfiguredToCallbackHandlerWithKafkaBinderProperties() { KafkaOAuth2AuthenticateCallbackHandler callbackHandler = new KafkaOAuth2AuthenticateCallbackHandler(); callbackHandler.configure(modifiedConfigs, null, null); - AzurePasswordlessProperties properties = (AzurePasswordlessProperties) ReflectionTestUtils - .getField(callbackHandler, "properties"); - AzureCredentialResolver azureTokenCredentialResolver = - (AzureCredentialResolver) ReflectionTestUtils.getField(callbackHandler, "tokenCredentialResolver"); - assertTrue(azureTokenCredentialResolver.resolve(properties) instanceof ManagedIdentityCredential); + @SuppressWarnings("unchecked") TokenCredentialProvider tokenCredentialProvider = + (TokenCredentialProvider) ReflectionTestUtils.getField(callbackHandler, "tokenCredentialProvider"); + assertTrue(tokenCredentialProvider.get() instanceof ManagedIdentityCredential); modifiedConfigs.clear(); modifiedConfigs.putAll(processor.getMergedProducerProperties(kafkaProperties)); modifiedConfigs.put(BOOTSTRAP_SERVERS_CONFIG, Arrays.asList("myehnamespace.servicebus.windows.net:9093")); modifiedConfigs.put(SASL_JAAS_CONFIG, new Password((String) modifiedConfigs.get(SASL_JAAS_CONFIG))); callbackHandler.configure(modifiedConfigs, null, null); - properties = (AzurePasswordlessProperties) ReflectionTestUtils.getField(callbackHandler, "properties"); - azureTokenCredentialResolver = - (AzureCredentialResolver) ReflectionTestUtils.getField(callbackHandler, "tokenCredentialResolver"); - assertTrue(azureTokenCredentialResolver.resolve(properties) instanceof DefaultAzureCredential); + tokenCredentialProvider = + (TokenCredentialProvider) ReflectionTestUtils.getField(callbackHandler, "tokenCredentialProvider"); + assertTrue(tokenCredentialProvider.get() instanceof DefaultAzureCredential); }); } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/kafka/AzureKafkaOAuth2BootConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/kafka/AzureKafkaOAuth2BootConfigurationTests.java index 18ca365f99dac..9e935f1b5559c 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/kafka/AzureKafkaOAuth2BootConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/kafka/AzureKafkaOAuth2BootConfigurationTests.java @@ -3,15 +3,14 @@ package com.azure.spring.cloud.autoconfigure.kafka; -import com.azure.core.credential.TokenCredential; import com.azure.identity.DefaultAzureCredential; import com.azure.identity.ManagedIdentityCredential; +import com.azure.identity.extensions.implementation.credential.provider.TokenCredentialProvider; import com.azure.spring.cloud.autoconfigure.context.AzureGlobalProperties; import com.azure.spring.cloud.autoconfigure.context.AzureGlobalPropertiesAutoConfiguration; import com.azure.spring.cloud.autoconfigure.context.AzureTokenCredentialAutoConfiguration; -import com.azure.spring.cloud.core.credential.AzureCredentialResolver; import com.azure.spring.cloud.service.implementation.kafka.KafkaOAuth2AuthenticateCallbackHandler; -import com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties; +import com.azure.spring.cloud.service.implementation.passwordless.AzureKafkaPasswordlessProperties; import org.apache.kafka.common.config.types.Password; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; @@ -179,22 +178,23 @@ void testOAuthConfiguredToCallbackHandler() { KafkaOAuth2AuthenticateCallbackHandler callbackHandler = new KafkaOAuth2AuthenticateCallbackHandler(); callbackHandler.configure(modifiedConfigs, null, null); - AzurePasswordlessProperties properties = (AzurePasswordlessProperties) ReflectionTestUtils + AzureKafkaPasswordlessProperties properties = (AzureKafkaPasswordlessProperties) ReflectionTestUtils .getField(callbackHandler, "properties"); - AzureCredentialResolver azureTokenCredentialResolver = - (AzureCredentialResolver) ReflectionTestUtils.getField(callbackHandler, "tokenCredentialResolver"); - assertNotNull(azureTokenCredentialResolver); - assertTrue(azureTokenCredentialResolver.resolve(properties) instanceof ManagedIdentityCredential); + @SuppressWarnings("unchecked") TokenCredentialProvider tokenCredentialProvider = + (TokenCredentialProvider) ReflectionTestUtils.getField(callbackHandler, "tokenCredentialProvider"); + assertNotNull(tokenCredentialProvider); + assertTrue(tokenCredentialProvider.get() instanceof ManagedIdentityCredential); Map consumerProperties = processor.getMergedConsumerProperties(getKafkaSpringProperties(context)); modifiedConfigs.clear(); modifiedConfigs.putAll(consumerProperties); modifiedConfigs.put(SASL_JAAS_CONFIG, new Password((String) modifiedConfigs.get(SASL_JAAS_CONFIG))); callbackHandler.configure(modifiedConfigs, null, null); - properties = (AzurePasswordlessProperties) ReflectionTestUtils.getField(callbackHandler, "properties"); - azureTokenCredentialResolver = (AzureCredentialResolver) ReflectionTestUtils.getField(callbackHandler, "tokenCredentialResolver"); - assertNotNull(azureTokenCredentialResolver); - assertTrue(azureTokenCredentialResolver.resolve(properties) instanceof DefaultAzureCredential); + properties = (AzureKafkaPasswordlessProperties) ReflectionTestUtils.getField(callbackHandler, "properties"); + tokenCredentialProvider = + (TokenCredentialProvider) ReflectionTestUtils.getField(callbackHandler, "tokenCredentialProvider"); + assertNotNull(tokenCredentialProvider); + assertTrue(tokenCredentialProvider.get() instanceof DefaultAzureCredential); }); } @@ -211,12 +211,12 @@ void testOAuthConfiguredToCallbackHandlerWithAzureProperties() { KafkaOAuth2AuthenticateCallbackHandler callbackHandler = new KafkaOAuth2AuthenticateCallbackHandler(); callbackHandler.configure(modifiedConfigs, null, null); - AzurePasswordlessProperties properties = (AzurePasswordlessProperties) ReflectionTestUtils + AzureKafkaPasswordlessProperties properties = (AzureKafkaPasswordlessProperties) ReflectionTestUtils .getField(callbackHandler, "properties"); - AzureCredentialResolver azureTokenCredentialResolver = - (AzureCredentialResolver) ReflectionTestUtils.getField(callbackHandler, "tokenCredentialResolver"); - assertNotNull(azureTokenCredentialResolver); - assertTrue(azureTokenCredentialResolver.resolve(properties) instanceof ManagedIdentityCredential); + @SuppressWarnings("unchecked") TokenCredentialProvider tokenCredentialProvider = + (TokenCredentialProvider) ReflectionTestUtils.getField(callbackHandler, "tokenCredentialProvider"); + assertNotNull(tokenCredentialProvider); + assertTrue(tokenCredentialProvider.get() instanceof ManagedIdentityCredential); }); } 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 a2447742ce9e7..a475f22be954f 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..ac3c272387a98 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/implementation/util/AzurePasswordlessPropertiesUtils.java @@ -0,0 +1,125 @@ +// 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.PasswordlessProperties; +import com.azure.spring.cloud.core.properties.AzureProperties; +import com.azure.spring.cloud.core.provider.RetryOptionsProvider; +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; + +/** + * + */ +public final class AzurePasswordlessPropertiesUtils { + + private AzurePasswordlessPropertiesUtils() { + + } + + /** + * Copy common properties from source {@link AzureProperties} object to target {@link T} 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 AzureProperties. + */ + 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()); + + if (source instanceof RetryOptionsProvider && target instanceof RetryOptionsProvider) { + RetryOptionsProvider.RetryOptions sourceRetry = ((RetryOptionsProvider) source).getRetry(); + RetryOptionsProvider.RetryOptions targetRetry = ((RetryOptionsProvider) target).getRetry(); + BeanUtils.copyProperties(sourceRetry, targetRetry); + BeanUtils.copyProperties(sourceRetry.getExponential(), targetRetry.getExponential()); + BeanUtils.copyProperties(sourceRetry.getFixed(), targetRetry.getFixed()); + } + } + + // TODO (xiada): add tests for this + /** + * Copy common properties from source {@link AzureProperties} object to target {@link T} object. Ignore the source + * value if it is null. + * + * @param source The source {@link AzureProperties} object. + * @param target The target object. + * @param The type of the target that extends AzureProperties. + */ + 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()); + + if (source instanceof RetryOptionsProvider && target instanceof RetryOptionsProvider) { + RetryOptionsProvider.RetryOptions sourceRetry = ((RetryOptionsProvider) source).getRetry(); + RetryOptionsProvider.RetryOptions targetRetry = ((RetryOptionsProvider) target).getRetry(); + copyPropertiesIgnoreNull(sourceRetry, targetRetry); + copyPropertiesIgnoreNull(sourceRetry.getExponential(), targetRetry.getExponential()); + copyPropertiesIgnoreNull(sourceRetry.getFixed(), targetRetry.getFixed()); + } + } + + /** + * Merge properties from two {@link AzureProperties} objects. 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); + if (target instanceof PasswordlessProperties) { + target.setScopes(properties.getScopes()); + target.setPasswordlessEnabled(properties.isPasswordlessEnabled()); + } + } + + /** + * Copy common properties from source object to target object. Ignore the source value if it is null. + * + * @param source The source object. + * @param target The target object. + */ + public static void copyPropertiesIgnoreNull(Object source, Object target) { + BeanUtils.copyProperties(source, target, findNullPropertyNames(source)); + } + + private static String[] findPropertyNames(Object source, Predicate predicate) { + final Set emptyNames = new HashSet<>(); + + final BeanWrapper beanWrapper = new BeanWrapperImpl(source); + PropertyDescriptor[] pds = beanWrapper.getPropertyDescriptors(); + + for (PropertyDescriptor pd : pds) { + Object srcValue = beanWrapper.getPropertyValue(pd.getName()); + if (predicate.test(srcValue)) { + emptyNames.add(pd.getName()); + } + } + return emptyNames.toArray(new String[0]); + } + + private static String[] findNullPropertyNames(Object source) { + return findPropertyNames(source, Objects::isNull); + } + +} diff --git a/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/properties/PasswordlessProperties.java b/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/properties/PasswordlessProperties.java new file mode 100644 index 0000000000000..0a353c234473c --- /dev/null +++ b/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/properties/PasswordlessProperties.java @@ -0,0 +1,111 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.cloud.core.properties; + +import com.azure.identity.extensions.implementation.enums.AuthProperty; +import com.azure.spring.cloud.core.provider.AzureProfileOptionsProvider; +import com.azure.spring.cloud.core.provider.authentication.TokenCredentialOptionsProvider; + +import java.util.Properties; +import java.util.function.BiConsumer; +import java.util.function.Function; + +/** + * Unified properties for Azure passwordless clients. + */ +public interface PasswordlessProperties extends TokenCredentialOptionsProvider, AzureProfileOptionsProvider { + + /** + * Gets the scopes required for the access token. + * + * @return scopes required for the access token + */ + String getScopes(); + + /** + * Set the scopes to get the access token. + * + * @param scopes the scopes required for the access token + */ + void setScopes(String scopes); + + /** + * Whether to enable connections authenticating with Azure AD, default is false. + * + * @return Whether to enable connections authenticating with Azure AD. + */ + boolean isPasswordlessEnabled(); + + /** + * Set the passwordlessEnabled value. + * + * @param passwordlessEnabled the passwordlessEnabled + */ + void setPasswordlessEnabled(boolean passwordlessEnabled); + + default Properties toPasswordlessProperties() { + Properties target = new Properties(); + for (AzurePasswordlessPropertiesMapping m : AzurePasswordlessPropertiesMapping.values()) { + if (m.getter.apply(this) != null) { + m.setter.accept(target, m.getter.apply(this)); + } + } + return target; + } + + /** + * A mapping util used to transfer a {@link PasswordlessProperties} instance to a {@link Properties} instance. + */ + enum AzurePasswordlessPropertiesMapping { + + scopes(p -> p.getScopes(), + (p, s) -> p.setProperty(AuthProperty.SCOPES.getPropertyKey(), s)), + + clientCertificatePassword(p -> p.getCredential().getClientCertificatePassword(), + (p, s) -> p.setProperty(AuthProperty.CLIENT_CERTIFICATE_PASSWORD.getPropertyKey(), s)), + + clientCertificatePath(p -> p.getCredential().getClientCertificatePath(), + (p, s) -> p.setProperty(AuthProperty.CLIENT_CERTIFICATE_PATH.getPropertyKey(), s)), + + clientId(p -> p.getCredential().getClientId(), + (p, s) -> p.setProperty(AuthProperty.CLIENT_ID.getPropertyKey(), s)), + + clientSecret(p -> p.getCredential().getClientSecret(), + (p, s) -> p.setProperty(AuthProperty.CLIENT_SECRET.getPropertyKey(), s)), + + managedIdentityEnabled(p -> String.valueOf(p.getCredential().isManagedIdentityEnabled()), + (p, s) -> p.setProperty(AuthProperty.MANAGED_IDENTITY_ENABLED.getPropertyKey(), s)), + + password(p -> p.getCredential().getPassword(), + (p, s) -> p.setProperty(AuthProperty.PASSWORD.getPropertyKey(), s)), + + username(p -> p.getCredential().getUsername(), + (p, s) -> p.setProperty(AuthProperty.USERNAME.getPropertyKey(), s)), + + tenantId(p -> p.getProfile().getTenantId(), + (p, s) -> p.setProperty(AuthProperty.TENANT_ID.getPropertyKey(), s)), + + authorityHost(p -> p.getProfile().getEnvironment().getActiveDirectoryEndpoint(), + (p, s) -> p.setProperty(AuthProperty.AUTHORITY_HOST.getPropertyKey(), s)); + + private Function getter; + private BiConsumer setter; + + AzurePasswordlessPropertiesMapping(Function getter, BiConsumer setter) { + this.getter = getter; + this.setter = setter; + } + + public Function getter() { + return getter; + } + + public BiConsumer setter() { + return setter; + } + + } +} + diff --git a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/kafka/AzureKafkaPropertiesUtils.java b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/kafka/AzureKafkaPropertiesUtils.java index a3ac2e2bd273e..7585ca9f6b91a 100644 --- a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/kafka/AzureKafkaPropertiesUtils.java +++ b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/kafka/AzureKafkaPropertiesUtils.java @@ -7,7 +7,7 @@ import com.azure.spring.cloud.core.provider.AzureProfileOptionsProvider; import com.azure.spring.cloud.service.implementation.jaas.Jaas; import com.azure.spring.cloud.service.implementation.jaas.JaasResolver; -import com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties; +import com.azure.spring.cloud.service.implementation.passwordless.AzureKafkaPasswordlessProperties; import org.apache.kafka.common.security.oauthbearer.OAuthBearerLoginModule; import java.util.Collections; @@ -33,7 +33,7 @@ private AzureKafkaPropertiesUtils() { static final String PROFILE_PREFIX = "azure.profile."; static final String ENVIRONMENT_PREFIX = PROFILE_PREFIX + "environment."; - public static void copyJaasPropertyToAzureProperties(String source, AzurePasswordlessProperties target) { + public static void copyJaasPropertyToAzureProperties(String source, AzureKafkaPasswordlessProperties target) { JaasResolver resolver = new JaasResolver(); Jaas jaas = resolver.resolve(source).orElse(new Jaas(OAuthBearerLoginModule.class.getName())); Map map = jaas.getOptions(); @@ -172,9 +172,9 @@ private static List buildPropertyKeys() { private String propertyKey; private Function getter; - private BiConsumer setter; + private BiConsumer setter; - AzureKafkaPasswordlessPropertiesMapping(String propertyKey, Function getter, BiConsumer getter, BiConsumer setter) { this.propertyKey = propertyKey; this.getter = getter; @@ -189,7 +189,7 @@ public Function getter() { return getter; } - public BiConsumer setter() { + public BiConsumer setter() { return setter; } diff --git a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/kafka/KafkaOAuth2AuthenticateCallbackHandler.java b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/kafka/KafkaOAuth2AuthenticateCallbackHandler.java index 3f325e33a8622..a036af01a4ce9 100644 --- a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/kafka/KafkaOAuth2AuthenticateCallbackHandler.java +++ b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/kafka/KafkaOAuth2AuthenticateCallbackHandler.java @@ -4,11 +4,10 @@ import com.azure.core.credential.TokenCredential; import com.azure.core.credential.TokenRequestContext; +import com.azure.identity.extensions.implementation.credential.TokenCredentialProviderOptions; +import com.azure.identity.extensions.implementation.credential.provider.TokenCredentialProvider; import com.azure.spring.cloud.core.credential.AzureCredentialResolver; -import com.azure.spring.cloud.core.implementation.credential.resolver.AzureTokenCredentialResolver; -import com.azure.spring.cloud.core.implementation.factory.credential.DefaultAzureCredentialBuilderFactory; -import com.azure.spring.cloud.core.properties.AzureProperties; -import com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties; +import com.azure.spring.cloud.service.implementation.passwordless.AzureKafkaPasswordlessProperties; import org.apache.kafka.common.config.types.Password; import org.apache.kafka.common.security.auth.AuthenticateCallbackHandler; import org.apache.kafka.common.security.oauthbearer.OAuthBearerTokenCallback; @@ -35,19 +34,17 @@ public class KafkaOAuth2AuthenticateCallbackHandler implements AuthenticateCallb private static final Duration ACCESS_TOKEN_REQUEST_BLOCK_TIME = Duration.ofSeconds(30); private static final String TOKEN_AUDIENCE_FORMAT = "%s://%s/.default"; - private final AzurePasswordlessProperties properties; - private final AzureCredentialResolver externalTokenCredentialResolver; + private final AzureKafkaPasswordlessProperties properties; - private AzureCredentialResolver tokenCredentialResolver; + private TokenCredentialProvider tokenCredentialProvider; private Function> resolveToken; public KafkaOAuth2AuthenticateCallbackHandler() { this(null, null); } - public KafkaOAuth2AuthenticateCallbackHandler(AzurePasswordlessProperties properties, AzureCredentialResolver externalTokenCredentialResolver) { - this.properties = properties == null ? new AzurePasswordlessProperties() : properties; - this.externalTokenCredentialResolver = externalTokenCredentialResolver == null ? new AzureTokenCredentialResolver() : externalTokenCredentialResolver; + public KafkaOAuth2AuthenticateCallbackHandler(AzureKafkaPasswordlessProperties properties, AzureCredentialResolver externalTokenCredentialResolver) { + this.properties = properties == null ? new AzureKafkaPasswordlessProperties() : properties; } @Override @@ -57,7 +54,7 @@ public void configure(Map configs, String mechanism, List tokenCredential.getToken(request).map(AzureOAuthBearerToken::new); - this.tokenCredentialResolver = new InternalCredentialResolver(externalTokenCredentialResolver, configs); + this.tokenCredentialProvider = new InternalTokenCredentialProvider(TokenCredentialProvider.createDefault(new TokenCredentialProviderOptions(properties.toPasswordlessProperties())), configs); } private TokenRequestContext buildTokenRequestContext(Map configs) { @@ -95,7 +92,7 @@ public void handle(Callback[] callbacks) throws UnsupportedCallbackException { if (callback instanceof OAuthBearerTokenCallback) { OAuthBearerTokenCallback oauthCallback = (OAuthBearerTokenCallback) callback; this.resolveToken - .apply(tokenCredentialResolver.resolve(properties)) + .apply(tokenCredentialProvider.get()) .doOnNext(oauthCallback::token) .doOnError(throwable -> oauthCallback.error("invalid_grant", throwable.getMessage(), null)) .block(ACCESS_TOKEN_REQUEST_BLOCK_TIME); @@ -110,35 +107,26 @@ public void close() { // NOOP } - private static class InternalCredentialResolver implements AzureCredentialResolver { - private final AzureCredentialResolver delegated; + private static class InternalTokenCredentialProvider implements TokenCredentialProvider { + private final TokenCredentialProvider delegated; private final Map configs; private TokenCredential credential; - InternalCredentialResolver(AzureCredentialResolver delegated, Map configs) { + InternalTokenCredentialProvider(TokenCredentialProvider delegated, Map configs) { this.delegated = delegated; this.configs = configs; } @Override - public TokenCredential resolve(AzureProperties properties) { + public TokenCredential get() { if (credential == null) { credential = (TokenCredential) configs.get(AZURE_TOKEN_CREDENTIAL); // Resolve the token credential when there is no credential passed from configs. if (credential == null) { - credential = delegated.resolve(properties); - if (credential == null) { - // Create DefaultAzureCredential when no credential can be resolved from configs. - credential = new DefaultAzureCredentialBuilderFactory(properties).build().build(); - } + credential = delegated.get(); } } return credential; } - - @Override - public boolean isResolvable(AzureProperties properties) { - return true; - } } } diff --git a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureJdbcPasswordlessProperties.java b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureJdbcPasswordlessProperties.java new file mode 100644 index 0000000000000..41bafc0464fd6 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureJdbcPasswordlessProperties.java @@ -0,0 +1,80 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.cloud.service.implementation.passwordless; + +import com.azure.spring.cloud.core.properties.PasswordlessProperties; +import com.azure.spring.cloud.core.properties.authentication.TokenCredentialProperties; +import com.azure.spring.cloud.core.properties.profile.AzureProfileProperties; + +import java.util.HashMap; +import java.util.Map; + +/** + * Configuration properties for passwordless connections with Azure Database. + */ +public class AzureJdbcPasswordlessProperties implements PasswordlessProperties { + + private static final String JDBC_SCOPE_AZURE = "https://ossrdbms-aad.database.windows.net/.default"; + private static final String JDBC_SCOPE_AZURE_CHINA = "https://ossrdbms-aad.database.chinacloudapi.cn/.default"; + private static final String JDBC_SCOPE_AZURE_GERMANY = "https://ossrdbms-aad.database.cloudapi.de/.default"; + private static final String JDBC_SCOPE_AZURE_US_GOVERNMENT = "https://ossrdbms-aad.database.usgovcloudapi.net/.default"; + + private static final Map JDBC_SCOPE_MAP = new HashMap() { + { + put(CloudType.AZURE, JDBC_SCOPE_AZURE); + put(CloudType.AZURE_CHINA, JDBC_SCOPE_AZURE_CHINA); + put(CloudType.AZURE_GERMANY, JDBC_SCOPE_AZURE_GERMANY); + put(CloudType.AZURE_US_GOVERNMENT, JDBC_SCOPE_AZURE_US_GOVERNMENT); + } + }; + + private boolean passwordlessEnabled = false; + + private AzureProfileProperties profile = new AzureProfileProperties(); + + private String scopes; + + private TokenCredentialProperties credential = new TokenCredentialProperties(); + + @Override + public String getScopes() { + return this.scopes == null ? getScopesFromMap() : this.scopes; + } + + public void setScopes(String scopes) { + this.scopes = scopes; + } + + @Override + public boolean isPasswordlessEnabled() { + return this.passwordlessEnabled; + } + + @Override + public void setPasswordlessEnabled(boolean passwordlessEnabled) { + this.passwordlessEnabled = passwordlessEnabled; + } + + private String getScopesFromMap() { + return JDBC_SCOPE_MAP.getOrDefault(getProfile().getCloudType(), JDBC_SCOPE_AZURE); + } + + @Override + public AzureProfileProperties getProfile() { + return profile; + } + + public void setProfile(AzureProfileProperties profile) { + this.profile = profile; + } + + @Override + public TokenCredentialProperties getCredential() { + return credential; + } + + public void setCredential(TokenCredentialProperties credential) { + this.credential = credential; + } +} diff --git a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureKafkaPasswordlessProperties.java b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureKafkaPasswordlessProperties.java new file mode 100644 index 0000000000000..4ba3da9c96be6 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureKafkaPasswordlessProperties.java @@ -0,0 +1,58 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.cloud.service.implementation.passwordless; + +import com.azure.spring.cloud.core.properties.PasswordlessProperties; +import com.azure.spring.cloud.core.properties.authentication.TokenCredentialProperties; +import com.azure.spring.cloud.core.properties.profile.AzureProfileProperties; + +/** + * Configuration properties for passwordless connections with Azure ServiceBus. + */ +public class AzureKafkaPasswordlessProperties implements PasswordlessProperties { + + private boolean passwordlessEnabled = false; + + private AzureProfileProperties profile = new AzureProfileProperties(); + + private TokenCredentialProperties credential = new TokenCredentialProperties(); + + @Override + public String getScopes() { + return null; + } + + @Override + public void setScopes(String scopes) { + throw new RuntimeException("This method is not available in AzureKafkaPasswordlessProperties"); + } + + @Override + public boolean isPasswordlessEnabled() { + return this.passwordlessEnabled; + } + + @Override + public void setPasswordlessEnabled(boolean passwordlessEnabled) { + this.passwordlessEnabled = passwordlessEnabled; + } + + @Override + public AzureProfileProperties getProfile() { + return profile; + } + + public void setProfile(AzureProfileProperties profile) { + this.profile = profile; + } + + @Override + public TokenCredentialProperties getCredential() { + return credential; + } + + public void setCredential(TokenCredentialProperties credential) { + this.credential = credential; + } +} diff --git a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzurePasswordlessProperties.java b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzurePasswordlessProperties.java deleted file mode 100644 index 53b08b91327a5..0000000000000 --- a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzurePasswordlessProperties.java +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.azure.spring.cloud.service.implementation.passwordless; - -import com.azure.identity.extensions.implementation.enums.AuthProperty; -import com.azure.spring.cloud.core.properties.AzureProperties; -import com.azure.spring.cloud.core.properties.authentication.TokenCredentialProperties; -import com.azure.spring.cloud.core.properties.client.ClientProperties; -import com.azure.spring.cloud.core.properties.profile.AzureProfileProperties; -import com.azure.spring.cloud.core.properties.proxy.ProxyProperties; -import com.azure.spring.cloud.core.provider.AzureProfileOptionsProvider; -import com.azure.spring.cloud.core.provider.authentication.TokenCredentialOptionsProvider; - -import java.util.Properties; -import java.util.function.BiConsumer; -import java.util.function.Function; - -/** - * Implement {@link TokenCredentialOptionsProvider} and {@link AzureProfileOptionsProvider} for Spring Cloud Azure - * support for other third party services. - */ -public class AzurePasswordlessProperties implements AzureProperties { - - private AzureProfileProperties profile = new AzureProfileProperties(); - - private String scopes; - - private TokenCredentialProperties credential = new TokenCredentialProperties(); - - // Use client options inside credential for azure identity - private ClientProperties client = new ClientProperties(); - - // Use proxy options inside credential for azure identity - private ProxyProperties proxy = new ProxyProperties(); - - // Whether to enable supporting azure identity token credentials - private boolean passwordlessEnabled = false; - - @Override - public AzureProfileProperties getProfile() { - return profile; - } - - public void setProfile(AzureProfileProperties profile) { - this.profile = profile; - } - - @Override - public TokenCredentialProperties getCredential() { - return credential; - } - - public void setCredential(TokenCredentialProperties credential) { - this.credential = credential; - } - - @Override - public ClientOptions getClient() { - return client; - } - - public void setClient(ClientProperties client) { - this.client = client; - } - - @Override - public ProxyOptions getProxy() { - return proxy; - } - - public void setProxy(ProxyProperties proxy) { - this.proxy = proxy; - } - - public boolean isPasswordlessEnabled() { - return passwordlessEnabled; - } - - public void setPasswordlessEnabled(boolean passwordlessEnabled) { - this.passwordlessEnabled = passwordlessEnabled; - } - - public String getScopes() { - return scopes; - } - - public void setScopes(String scopes) { - this.scopes = scopes; - } - - public Properties toProperties() { - Properties target = new Properties(); - for (AzurePasswordlessPropertiesMapping m : AzurePasswordlessPropertiesMapping.values()) { - if (m.getter.apply(this) != null) { - m.setter.accept(target, m.getter.apply(this)); - } - } - return target; - } - - private enum AzurePasswordlessPropertiesMapping { - - scopes(p -> p.getScopes(), - (p, s) -> p.setProperty(AuthProperty.SCOPES.getPropertyKey(), s)), - - clientCertificatePassword(p -> p.getCredential().getClientCertificatePassword(), - (p, s) -> p.setProperty(AuthProperty.CLIENT_CERTIFICATE_PASSWORD.getPropertyKey(), s)), - - clientCertificatePath(p -> p.getCredential().getClientCertificatePath(), - (p, s) -> p.setProperty(AuthProperty.CLIENT_CERTIFICATE_PATH.getPropertyKey(), s)), - - clientId(p -> p.getCredential().getClientId(), - (p, s) -> p.setProperty(AuthProperty.CLIENT_ID.getPropertyKey(), s)), - - clientSecret(p -> p.getCredential().getClientSecret(), - (p, s) -> p.setProperty(AuthProperty.CLIENT_SECRET.getPropertyKey(), s)), - - managedIdentityEnabled(p -> String.valueOf(p.getCredential().isManagedIdentityEnabled()), - (p, s) -> p.setProperty(AuthProperty.MANAGED_IDENTITY_ENABLED.getPropertyKey(), s)), - - password(p -> p.getCredential().getPassword(), - (p, s) -> p.setProperty(AuthProperty.PASSWORD.getPropertyKey(), s)), - - username(p -> p.getCredential().getUsername(), - (p, s) -> p.setProperty(AuthProperty.USERNAME.getPropertyKey(), s)), - - tenantId(p -> p.getProfile().getTenantId(), - (p, s) -> p.setProperty(AuthProperty.TENANT_ID.getPropertyKey(), s)), - - authorityHost(p -> p.getProfile().getEnvironment().getActiveDirectoryEndpoint(), - (p, s) -> p.setProperty(AuthProperty.AUTHORITY_HOST.getPropertyKey(), s)); - - private Function getter; - private BiConsumer setter; - - AzurePasswordlessPropertiesMapping(Function getter, BiConsumer setter) { - this.getter = getter; - this.setter = setter; - } - - public Function getter() { - return getter; - } - - public BiConsumer setter() { - return setter; - } - - } -} diff --git a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureRedisPasswordlessProperties.java b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureRedisPasswordlessProperties.java index 2658052ff1f1e..44ba84277716f 100644 --- a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureRedisPasswordlessProperties.java +++ b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureRedisPasswordlessProperties.java @@ -3,14 +3,18 @@ package com.azure.spring.cloud.service.implementation.passwordless; +import com.azure.spring.cloud.core.properties.PasswordlessProperties; +import com.azure.spring.cloud.core.properties.authentication.TokenCredentialProperties; +import com.azure.spring.cloud.core.properties.profile.AzureProfileProperties; import com.azure.spring.cloud.core.provider.AzureProfileOptionsProvider; + import java.util.HashMap; import java.util.Map; /** * Configuration properties for passwordless connections with Azure Redis. */ -public class AzureRedisPasswordlessProperties extends AzurePasswordlessProperties { +public class AzureRedisPasswordlessProperties implements PasswordlessProperties { private static final String REDIS_SCOPE_AZURE = "https://*.cacheinfra.windows.net:10225/appid/.default"; private static final String REDIS_SCOPE_AZURE_CHINA = "https://*.cacheinfra.windows.net.china:10225/appid/.default"; @@ -26,13 +30,53 @@ public class AzureRedisPasswordlessProperties extends AzurePasswordlessPropertie } }; + private AzureProfileProperties profile = new AzureProfileProperties(); + + private String scopes; + + private boolean passwordlessEnabled = false; + + private TokenCredentialProperties credential = new TokenCredentialProperties(); + @Override public String getScopes() { - return super.getScopes() == null ? getRedisScopes() : super.getScopes(); + return this.scopes == null ? getScopesFromMap() : this.scopes; + } + + @Override + public void setScopes(String scopes) { + this.scopes = scopes; + } + + @Override + public boolean isPasswordlessEnabled() { + return this.passwordlessEnabled; } - private String getRedisScopes() { + @Override + public void setPasswordlessEnabled(boolean passwordlessEnabled) { + this.passwordlessEnabled = passwordlessEnabled; + } + + private String getScopesFromMap() { return REDIS_SCOPE_MAP.getOrDefault(getProfile().getCloudType(), REDIS_SCOPE_AZURE); } + @Override + public AzureProfileProperties getProfile() { + return profile; + } + + public void setProfile(AzureProfileProperties profile) { + this.profile = profile; + } + + @Override + public TokenCredentialProperties getCredential() { + return credential; + } + + public void setCredential(TokenCredentialProperties credential) { + this.credential = credential; + } } diff --git a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureServiceBusPasswordlessProperties.java b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureServiceBusPasswordlessProperties.java deleted file mode 100644 index e36dcf90a4586..0000000000000 --- a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureServiceBusPasswordlessProperties.java +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -package com.azure.spring.cloud.service.implementation.passwordless; - -import java.util.HashMap; -import java.util.Map; - -/** - * Configuration properties for passwordless connections with Azure ServiceBus. - */ -public class AzureServiceBusPasswordlessProperties extends AzurePasswordlessProperties { - - private static final String SERVICEBUS_SCOPE_AZURE = "https://servicebus.azure.net/.default"; - private static final String SERVICEBUS_SCOPE_AZURE_CHINA = "https://servicebus.azure.net/.default"; - private static final String SERVICEBUS_SCOPE_AZURE_GERMANY = "https://servicebus.azure.net/.default"; - private static final String SERVICEBUS_SCOPE_AZURE_US_GOVERNMENT = "https://servicebus.azure.net/.default"; - - private static final Map SERVICEBUS_SCOPE_MAP = new HashMap() { - { - put(CloudType.AZURE, SERVICEBUS_SCOPE_AZURE); - put(CloudType.AZURE_CHINA, SERVICEBUS_SCOPE_AZURE_CHINA); - put(CloudType.AZURE_GERMANY, SERVICEBUS_SCOPE_AZURE_GERMANY); - put(CloudType.AZURE_US_GOVERNMENT, SERVICEBUS_SCOPE_AZURE_US_GOVERNMENT); - } - }; - - @Override - public String getScopes() { - return super.getScopes() == null ? getRedisScopes() : super.getScopes(); - } - - private String getRedisScopes() { - return SERVICEBUS_SCOPE_MAP.getOrDefault(getProfile().getCloudType(), SERVICEBUS_SCOPE_AZURE); - } - -} diff --git a/sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/implementation/kafka/AzureKafkaPropertiesUtilsTest.java b/sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/implementation/kafka/AzureKafkaPropertiesUtilsTest.java index fe7b7601d7a1e..f8592b2213581 100644 --- a/sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/implementation/kafka/AzureKafkaPropertiesUtilsTest.java +++ b/sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/implementation/kafka/AzureKafkaPropertiesUtilsTest.java @@ -3,7 +3,7 @@ package com.azure.spring.cloud.service.implementation.kafka; 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.AzureKafkaPasswordlessProperties; import org.junit.jupiter.api.Test; import static com.azure.spring.cloud.service.implementation.kafka.AzureKafkaPropertiesUtils.copyJaasPropertyToAzureProperties; @@ -17,7 +17,7 @@ class AzureKafkaPropertiesUtilsTest { @Test void testCopyJaasPropertyToAzureProperties() { String jaasConfig = "org.apache.kafka.common.security.oauthbearer.OAuthBearerLoginModule required azure.configured=\"true\";"; - AzurePasswordlessProperties properties = new AzurePasswordlessProperties(); + AzureKafkaPasswordlessProperties properties = new AzureKafkaPasswordlessProperties(); copyJaasPropertyToAzureProperties(jaasConfig, properties); assertFalse(properties.getCredential().isManagedIdentityEnabled()); @@ -29,7 +29,7 @@ void testCopyJaasPropertyToAzureProperties() { void testCopyJaasPropertyWithCustomizedValuesToAzureProperties() { String jaasConfig = "org.apache.kafka.common.security.oauthbearer.OAuthBearerLoginModule required azure.configured=\"true\" " + "azure.credential.managed-identity-enabled=\"true\" azure.credential.client-id=\"test\" azure.profile.cloud-type=\"azure\";"; - AzurePasswordlessProperties properties = new AzurePasswordlessProperties(); + AzureKafkaPasswordlessProperties properties = new AzureKafkaPasswordlessProperties(); copyJaasPropertyToAzureProperties(jaasConfig, properties); assertTrue(properties.getCredential().isManagedIdentityEnabled()); @@ -40,7 +40,7 @@ void testCopyJaasPropertyWithCustomizedValuesToAzureProperties() { @Test void testClearAzureProperties() { - AzurePasswordlessProperties properties = new AzurePasswordlessProperties(); + AzureKafkaPasswordlessProperties properties = new AzureKafkaPasswordlessProperties(); properties.getProfile().setCloudType(AzureProfileOptionsProvider.CloudType.AZURE); properties.getProfile().setTenantId("fake-tenant-id"); } diff --git a/sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/implementation/kafka/KafkaOAuth2AuthenticateCallbackHandlerTest.java b/sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/implementation/kafka/KafkaOAuth2AuthenticateCallbackHandlerTest.java index 03f3b5cfc03e5..26d28edbb998f 100644 --- a/sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/implementation/kafka/KafkaOAuth2AuthenticateCallbackHandlerTest.java +++ b/sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/implementation/kafka/KafkaOAuth2AuthenticateCallbackHandlerTest.java @@ -7,12 +7,12 @@ import com.azure.core.credential.TokenRequestContext; import com.azure.identity.DefaultAzureCredential; import com.azure.identity.ManagedIdentityCredential; -import com.azure.spring.cloud.core.credential.AzureCredentialResolver; +import com.azure.identity.extensions.implementation.credential.provider.TokenCredentialProvider; import com.azure.spring.cloud.service.implementation.jaas.Jaas; -import com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties; +import com.azure.spring.cloud.service.implementation.passwordless.AzureKafkaPasswordlessProperties; import org.apache.kafka.common.config.types.Password; -import org.apache.kafka.common.security.oauthbearer.OAuthBearerTokenCallback; import org.apache.kafka.common.security.oauthbearer.OAuthBearerLoginModule; +import org.apache.kafka.common.security.oauthbearer.OAuthBearerTokenCallback; import org.junit.jupiter.api.Test; import org.mockito.Mockito; import org.springframework.test.util.ReflectionTestUtils; @@ -43,7 +43,7 @@ class KafkaOAuth2AuthenticateCallbackHandlerTest { private static final List KAFKA_BOOTSTRAP_SERVER = Arrays.asList("namespace.servicebus.windows.net:9093"); private static final String AZURE_THIRD_PARTY_SERVICE_PROPERTIES_FIELD_NAME = "properties"; - private static final String TOKEN_CREDENTIAL_RESOLVER_FIELD_NAME = "tokenCredentialResolver"; + private static final String TOKEN_CREDENTIAL_PROVIDER_FIELD_NAME = "tokenCredentialProvider"; @Test void testTokenCredentialShouldConfig() { TokenCredential tokenCredential = new TokenCredential() { @@ -59,12 +59,10 @@ public Mono getToken(TokenRequestContext tokenRequestContext) { KafkaOAuth2AuthenticateCallbackHandler handler = new KafkaOAuth2AuthenticateCallbackHandler(); handler.configure(configs, null, null); - AzurePasswordlessProperties properties = (AzurePasswordlessProperties) ReflectionTestUtils - .getField(handler, AZURE_THIRD_PARTY_SERVICE_PROPERTIES_FIELD_NAME); - @SuppressWarnings("unchecked") AzureCredentialResolver azureTokenCredentialResolver = - (AzureCredentialResolver) ReflectionTestUtils.getField(handler, TOKEN_CREDENTIAL_RESOLVER_FIELD_NAME); - assertNotNull(azureTokenCredentialResolver); - assertEquals(tokenCredential, azureTokenCredentialResolver.resolve(properties)); + @SuppressWarnings("unchecked") TokenCredentialProvider tokenCredentialProvider = + (TokenCredentialProvider) ReflectionTestUtils.getField(handler, TOKEN_CREDENTIAL_PROVIDER_FIELD_NAME); + assertNotNull(tokenCredentialProvider); + assertEquals(tokenCredential, tokenCredentialProvider.get()); } @Test @@ -74,12 +72,10 @@ void testCreateDefaultTokenCredential() { KafkaOAuth2AuthenticateCallbackHandler handler = new KafkaOAuth2AuthenticateCallbackHandler(); handler.configure(configs, null, null); - AzurePasswordlessProperties properties = (AzurePasswordlessProperties) ReflectionTestUtils - .getField(handler, AZURE_THIRD_PARTY_SERVICE_PROPERTIES_FIELD_NAME); - @SuppressWarnings("unchecked") AzureCredentialResolver azureTokenCredentialResolver = - (AzureCredentialResolver) ReflectionTestUtils.getField(handler, TOKEN_CREDENTIAL_RESOLVER_FIELD_NAME); - assertNotNull(azureTokenCredentialResolver); - assertTrue(azureTokenCredentialResolver.resolve(properties) instanceof DefaultAzureCredential); + @SuppressWarnings("unchecked") TokenCredentialProvider tokenCredentialProvider = + (TokenCredentialProvider) ReflectionTestUtils.getField(handler, TOKEN_CREDENTIAL_PROVIDER_FIELD_NAME); + assertNotNull(tokenCredentialProvider); + assertTrue(tokenCredentialProvider.get() instanceof DefaultAzureCredential); } @Test @@ -93,13 +89,13 @@ void testCreateTokenCredentialByResolver() { KafkaOAuth2AuthenticateCallbackHandler handler = new KafkaOAuth2AuthenticateCallbackHandler(); handler.configure(configs, null, null); - AzurePasswordlessProperties properties = (AzurePasswordlessProperties) ReflectionTestUtils + AzureKafkaPasswordlessProperties properties = (AzureKafkaPasswordlessProperties) ReflectionTestUtils .getField(handler, AZURE_THIRD_PARTY_SERVICE_PROPERTIES_FIELD_NAME); assertTrue(properties.getCredential().isManagedIdentityEnabled()); - @SuppressWarnings("unchecked") AzureCredentialResolver azureTokenCredentialResolver = - (AzureCredentialResolver) ReflectionTestUtils.getField(handler, TOKEN_CREDENTIAL_RESOLVER_FIELD_NAME); - assertNotNull(azureTokenCredentialResolver); - assertTrue(azureTokenCredentialResolver.resolve(properties) instanceof ManagedIdentityCredential); + @SuppressWarnings("unchecked") TokenCredentialProvider tokenCredentialProvider = + (TokenCredentialProvider) ReflectionTestUtils.getField(handler, TOKEN_CREDENTIAL_PROVIDER_FIELD_NAME); + assertNotNull(tokenCredentialProvider); + assertTrue(tokenCredentialProvider.get() instanceof ManagedIdentityCredential); } @Test diff --git a/sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/implementation/passwordless/AzureRedisPasswordlessPropertiesTest.java b/sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/implementation/passwordless/AzureRedisPasswordlessPropertiesTest.java index 30c5f85c36350..82bb22b7e9d43 100644 --- a/sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/implementation/passwordless/AzureRedisPasswordlessPropertiesTest.java +++ b/sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/implementation/passwordless/AzureRedisPasswordlessPropertiesTest.java @@ -71,7 +71,7 @@ void testToProperties() { azureRedisPasswordlessProperties.setCredential(credential); azureRedisPasswordlessProperties.setProfile(profile); - Properties properties = azureRedisPasswordlessProperties.toProperties(); + Properties properties = azureRedisPasswordlessProperties.toPasswordlessProperties(); Assertions.assertEquals("fake-client-id", properties.getProperty(AuthProperty.CLIENT_ID.getPropertyKey())); From 005bf71b191fd716526c6cd6c7834e03f9460b0b Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Tue, 21 Feb 2023 19:31:37 +0800 Subject: [PATCH 13/29] Fix pipeline errors --- .../MergeAzureCommonPropertiesTest.java} | 48 ++++--------------- 1 file changed, 10 insertions(+), 38 deletions(-) rename sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/{redis/AzureJedisPasswordlessUtilTest.java => passwordless/MergeAzureCommonPropertiesTest.java} (66%) 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()); } } From 8309e4589b44dcd6cb2b6f2a16489f97c8424201 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Wed, 22 Feb 2023 14:26:22 +0800 Subject: [PATCH 14/29] add comment for PasswordlessProperties --- .../properties/PasswordlessProperties.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/properties/PasswordlessProperties.java b/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/properties/PasswordlessProperties.java index 0a353c234473c..1cd8e4e2488c1 100644 --- a/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/properties/PasswordlessProperties.java +++ b/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/properties/PasswordlessProperties.java @@ -59,33 +59,63 @@ default Properties toPasswordlessProperties() { */ enum AzurePasswordlessPropertiesMapping { + /** + * Getter function and setter biConsumer for scopes. + */ scopes(p -> p.getScopes(), (p, s) -> p.setProperty(AuthProperty.SCOPES.getPropertyKey(), s)), + /** + * Getter function and setter biConsumer for clientCertificatePassword. + */ clientCertificatePassword(p -> p.getCredential().getClientCertificatePassword(), (p, s) -> p.setProperty(AuthProperty.CLIENT_CERTIFICATE_PASSWORD.getPropertyKey(), s)), + /** + * Getter function and setter biConsumer for clientCertificatePath. + */ clientCertificatePath(p -> p.getCredential().getClientCertificatePath(), (p, s) -> p.setProperty(AuthProperty.CLIENT_CERTIFICATE_PATH.getPropertyKey(), s)), + /** + * Getter function and setter biConsumer for clientId. + */ clientId(p -> p.getCredential().getClientId(), (p, s) -> p.setProperty(AuthProperty.CLIENT_ID.getPropertyKey(), s)), + /** + * Getter function and setter biConsumer for clientSecret. + */ clientSecret(p -> p.getCredential().getClientSecret(), (p, s) -> p.setProperty(AuthProperty.CLIENT_SECRET.getPropertyKey(), s)), + /** + * Getter function and setter biConsumer for managedIdentityEnabled. + */ managedIdentityEnabled(p -> String.valueOf(p.getCredential().isManagedIdentityEnabled()), (p, s) -> p.setProperty(AuthProperty.MANAGED_IDENTITY_ENABLED.getPropertyKey(), s)), + /** + * Getter function and setter biConsumer for password. + */ password(p -> p.getCredential().getPassword(), (p, s) -> p.setProperty(AuthProperty.PASSWORD.getPropertyKey(), s)), + /** + * Getter function and setter biConsumer for username. + */ username(p -> p.getCredential().getUsername(), (p, s) -> p.setProperty(AuthProperty.USERNAME.getPropertyKey(), s)), + /** + * Getter function and setter biConsumer for tenantId. + */ tenantId(p -> p.getProfile().getTenantId(), (p, s) -> p.setProperty(AuthProperty.TENANT_ID.getPropertyKey(), s)), + /** + * Getter function and setter biConsumer for authorityHost. + */ authorityHost(p -> p.getProfile().getEnvironment().getActiveDirectoryEndpoint(), (p, s) -> p.setProperty(AuthProperty.AUTHORITY_HOST.getPropertyKey(), s)); From c4e775e4e136a4e6c57f81143c11ffb00bc7c3eb Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Wed, 22 Feb 2023 14:44:07 +0800 Subject: [PATCH 15/29] add comment for PasswordlessProperties --- .../core/properties/PasswordlessProperties.java | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/properties/PasswordlessProperties.java b/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/properties/PasswordlessProperties.java index 1cd8e4e2488c1..cf63ab2f116a9 100644 --- a/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/properties/PasswordlessProperties.java +++ b/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/properties/PasswordlessProperties.java @@ -44,6 +44,10 @@ public interface PasswordlessProperties extends TokenCredentialOptionsProvider, */ void setPasswordlessEnabled(boolean passwordlessEnabled); + /** + * Convert {@link PasswordlessProperties} to {@link Properties}. + * @return converted {@link Properties} instance + */ default Properties toPasswordlessProperties() { Properties target = new Properties(); for (AzurePasswordlessPropertiesMapping m : AzurePasswordlessPropertiesMapping.values()) { @@ -55,7 +59,7 @@ default Properties toPasswordlessProperties() { } /** - * A mapping util used to transfer a {@link PasswordlessProperties} instance to a {@link Properties} instance. + * A mapping util used to convert a {@link PasswordlessProperties} instance to a {@link Properties} instance. */ enum AzurePasswordlessPropertiesMapping { @@ -128,14 +132,6 @@ enum AzurePasswordlessPropertiesMapping { this.setter = setter; } - public Function getter() { - return getter; - } - - public BiConsumer setter() { - return setter; - } - } } From 22e3ac76442bb4286ec5b258bf11ffc3ec2a7a4b Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Wed, 22 Feb 2023 15:39:06 +0800 Subject: [PATCH 16/29] add more comments --- ...erviceBusJmsPasswordlessConfiguration.java | 2 +- .../AzureServiceBusJmsProperties.java | 97 +++++++++++++------ .../properties/PasswordlessProperties.java | 2 +- .../AzureJdbcPasswordlessProperties.java | 43 +++++++- .../AzureKafkaPasswordlessProperties.java | 40 ++++++++ .../AzureRedisPasswordlessProperties.java | 43 +++++++- 6 files changed, 192 insertions(+), 35 deletions(-) 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 index 82457c368e186..5d8b7237c9862 100644 --- 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 @@ -20,7 +20,7 @@ */ @Configuration(proxyBeanMethods = false) @ConditionalOnProperty(value = "spring.jms.servicebus.passwordless-enabled", havingValue = "true") -public class ServiceBusJmsPasswordlessConfiguration { +class ServiceBusJmsPasswordlessConfiguration { @Bean @ConditionalOnMissingBean 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 79ab37f2f49c6..1a255d0a85bc5 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 @@ -205,65 +205,104 @@ public void setNameSpace(String nameSpace) { this.nameSpace = nameSpace; } - public void setScopes(String scopes) { - this.scopes = scopes; - } - - public void setPasswordlessEnabled(boolean passwordlessEnabled) { - this.passwordlessEnabled = passwordlessEnabled; - } - /** - * Validate spring.jms.servicebus related properties. + * Get the scopes required for the access token. * - * @throws IllegalArgumentException If connectionString is empty. + * @return scopes required for the access token */ @Override - public void afterPropertiesSet() throws Exception { - 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")) { - throw new IllegalArgumentException("'spring.jms.servicebus.pricing-tier' is not valid"); - } - + public String getScopes() { + return this.scopes == null ? getDefaultScopes() : this.scopes; } - @Override - public String getScopes() { - return this.scopes == null ? getScopesFromMap() : 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 AzureProfileProperties getProfile() { return profile; } + /** + * Set the profile + * @param profile the profile properties related to an Azure subscription + */ public void setProfile(AzureProfileProperties profile) { this.profile = profile; } + /** + * Get the credential properties. + * + * @return the credential properties. + */ @Override public TokenCredentialProperties getCredential() { return credential; } + /** + * Set the credential properties. + * + * @param credential the credential properties + */ public void setCredential(TokenCredentialProperties credential) { this.credential = credential; } + /** + * Validate spring.jms.servicebus related properties. + * + * @throws IllegalArgumentException If connectionString is empty. + */ + @Override + public void afterPropertiesSet() throws Exception { + 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")) { + throw new IllegalArgumentException("'spring.jms.servicebus.pricing-tier' is not valid"); + } + + } + /** * Properties to configure {@link org.apache.qpid.jms.policy.JmsDefaultPrefetchPolicy} for {@link * org.apache.qpid.jms.JmsConnectionFactory} . @@ -504,7 +543,7 @@ public void setPhase(Integer phase) { } } - private String getScopesFromMap() { + private String getDefaultScopes() { return SERVICEBUS_SCOPE_MAP.getOrDefault(getProfile().getCloudType(), SERVICE_BUS_SCOPE_AZURE); } } diff --git a/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/properties/PasswordlessProperties.java b/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/properties/PasswordlessProperties.java index cf63ab2f116a9..2389c228a3645 100644 --- a/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/properties/PasswordlessProperties.java +++ b/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/properties/PasswordlessProperties.java @@ -17,7 +17,7 @@ public interface PasswordlessProperties extends TokenCredentialOptionsProvider, AzureProfileOptionsProvider { /** - * Gets the scopes required for the access token. + * Get the scopes required for the access token. * * @return scopes required for the access token */ diff --git a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureJdbcPasswordlessProperties.java b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureJdbcPasswordlessProperties.java index 41bafc0464fd6..cd431606f4797 100644 --- a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureJdbcPasswordlessProperties.java +++ b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureJdbcPasswordlessProperties.java @@ -37,43 +37,82 @@ public class AzureJdbcPasswordlessProperties implements PasswordlessProperties { private TokenCredentialProperties credential = new TokenCredentialProperties(); + /** + * Get the scopes required for the access token. + * + * @return scopes required for the access token + */ @Override public String getScopes() { - return this.scopes == null ? getScopesFromMap() : this.scopes; + 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 this.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 + */ @Override public void setPasswordlessEnabled(boolean passwordlessEnabled) { this.passwordlessEnabled = passwordlessEnabled; } - private String getScopesFromMap() { + private String getDefaultScopes() { return JDBC_SCOPE_MAP.getOrDefault(getProfile().getCloudType(), JDBC_SCOPE_AZURE); } + /** + * Get the profile + * @return the profile + */ @Override public AzureProfileProperties getProfile() { return profile; } + /** + * Set the profile + * @param profile the profile properties related to an Azure subscription + */ public void setProfile(AzureProfileProperties profile) { this.profile = profile; } + /** + * Get the credential properties. + * + * @return the credential properties. + */ @Override public TokenCredentialProperties getCredential() { return credential; } + /** + * Set the credential properties. + * + * @param credential the credential properties + */ public void setCredential(TokenCredentialProperties credential) { this.credential = credential; } diff --git a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureKafkaPasswordlessProperties.java b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureKafkaPasswordlessProperties.java index 4ba3da9c96be6..9b99c5f0795f0 100644 --- a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureKafkaPasswordlessProperties.java +++ b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureKafkaPasswordlessProperties.java @@ -18,40 +18,80 @@ public class AzureKafkaPasswordlessProperties implements PasswordlessProperties private TokenCredentialProperties credential = new TokenCredentialProperties(); + /** + * Get the scopes required for the access token. + * This method is not available in AzureKafkaPasswordlessProperties, will always return null. + * + * @return null + */ @Override public String getScopes() { return null; } + /** + * Set the scopes required for the access token. + * + * This method is not available in AzureKafkaPasswordlessProperties + */ @Override public void setScopes(String scopes) { throw new RuntimeException("This method is not available in AzureKafkaPasswordlessProperties"); } + /** + * 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 this.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 + */ @Override public void setPasswordlessEnabled(boolean passwordlessEnabled) { this.passwordlessEnabled = passwordlessEnabled; } + /** + * Get the profile + * @return the profile + */ @Override public AzureProfileProperties getProfile() { return profile; } + /** + * Set the profile + * @param profile the profile properties related to an Azure subscription + */ public void setProfile(AzureProfileProperties profile) { this.profile = profile; } + /** + * Get the credential properties. + * + * @return the credential properties. + */ @Override public TokenCredentialProperties getCredential() { return credential; } + /** + * Set the credential properties. + * + * @param credential the credential properties + */ public void setCredential(TokenCredentialProperties credential) { this.credential = credential; } diff --git a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureRedisPasswordlessProperties.java b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureRedisPasswordlessProperties.java index 44ba84277716f..c4d5facdac0a6 100644 --- a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureRedisPasswordlessProperties.java +++ b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureRedisPasswordlessProperties.java @@ -38,44 +38,83 @@ public class AzureRedisPasswordlessProperties implements PasswordlessProperties private TokenCredentialProperties credential = new TokenCredentialProperties(); + /** + * Get the scopes required for the access token. + * + * @return scopes required for the access token + */ @Override public String getScopes() { - return this.scopes == null ? getScopesFromMap() : this.scopes; + return this.scopes == null ? getDefaultScopes() : this.scopes; } + /** + * Set the scopes required for the access token. + * + * @param scopes the scopes required for the access token + */ @Override 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 this.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 + */ @Override public void setPasswordlessEnabled(boolean passwordlessEnabled) { this.passwordlessEnabled = passwordlessEnabled; } - private String getScopesFromMap() { + private String getDefaultScopes() { return REDIS_SCOPE_MAP.getOrDefault(getProfile().getCloudType(), REDIS_SCOPE_AZURE); } + /** + * Get the profile + * @return the profile + */ @Override public AzureProfileProperties getProfile() { return profile; } + /** + * Set the profile + * @param profile the profile properties related to an Azure subscription + */ public void setProfile(AzureProfileProperties profile) { this.profile = profile; } + /** + * Get the credential properties. + * + * @return the credential properties. + */ @Override public TokenCredentialProperties getCredential() { return credential; } + /** + * Set the credential properties. + * + * @param credential the credential properties + */ public void setCredential(TokenCredentialProperties credential) { this.credential = credential; } From 614d5206e6a01d4c5d049ccb4667c74488f076e5 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Wed, 22 Feb 2023 16:15:20 +0800 Subject: [PATCH 17/29] Fix revApi --- .../properties/AzureServiceBusJmsProperties.java | 6 +++--- .../util/AzurePasswordlessPropertiesUtils.java | 15 ++++++--------- 2 files changed, 9 insertions(+), 12 deletions(-) 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 1a255d0a85bc5..af52c1fc61ebf 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 @@ -20,6 +20,7 @@ /** * {@link ConfigurationProperties} for configuring Azure Service Bus JMS. */ +@ConfigurationProperties(prefix = AzureServiceBusJmsProperties.PREFIX) public class AzureServiceBusJmsProperties implements InitializingBean, PasswordlessProperties { private static final String SERVICE_BUS_SCOPE_AZURE = "https://servicebus.azure.net/.default"; @@ -267,7 +268,7 @@ public void setProfile(AzureProfileProperties profile) { * @return the credential properties. */ @Override - public TokenCredentialProperties getCredential() { + public TokenCredentialOptions getCredential() { return credential; } @@ -276,7 +277,7 @@ public TokenCredentialProperties getCredential() { * * @param credential the credential properties */ - public void setCredential(TokenCredentialProperties credential) { + public void setCredential(TokenCredentialOptions credential) { this.credential = credential; } @@ -300,7 +301,6 @@ public void afterPropertiesSet() throws Exception { if (null == pricingTier || !pricingTier.matches("(?i)premium|standard|basic")) { throw new IllegalArgumentException("'spring.jms.servicebus.pricing-tier' is not valid"); } - } /** diff --git a/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/implementation/util/AzurePasswordlessPropertiesUtils.java b/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/implementation/util/AzurePasswordlessPropertiesUtils.java index ac3c272387a98..34ef933ddf831 100644 --- a/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/implementation/util/AzurePasswordlessPropertiesUtils.java +++ b/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/implementation/util/AzurePasswordlessPropertiesUtils.java @@ -17,7 +17,7 @@ import java.util.function.Predicate; /** - * + * Util class for AzurePasswordlessProperties. */ public final class AzurePasswordlessPropertiesUtils { @@ -49,12 +49,11 @@ public static void copyAzureCommonProperties( } } - // TODO (xiada): add tests for this /** - * Copy common properties from source {@link AzureProperties} object to target {@link T} object. Ignore the source + * Copy common properties from source {@link PasswordlessProperties} object to target {@link T} object. Ignore the source * value if it is null. * - * @param source The source {@link AzureProperties} object. + * @param source The source {@link PasswordlessProperties} object. * @param target The target object. * @param The type of the target that extends AzureProperties. */ @@ -74,7 +73,7 @@ public static void copyAzureCommonPropertiesI } /** - * Merge properties from two {@link AzureProperties} objects. If a same property appears in both two objects, the + * 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 @@ -87,10 +86,8 @@ public static void mergeAzureCommonProperties T target) { copyAzureCommonProperties(defaultProperties, target); copyAzureCommonPropertiesIgnoreNull(properties, target); - if (target instanceof PasswordlessProperties) { - target.setScopes(properties.getScopes()); - target.setPasswordlessEnabled(properties.isPasswordlessEnabled()); - } + target.setScopes(properties.getScopes()); + target.setPasswordlessEnabled(properties.isPasswordlessEnabled()); } /** From 4082260f35ca068fbeeb4363c97d020525aa0078 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Wed, 22 Feb 2023 16:17:05 +0800 Subject: [PATCH 18/29] update metadata.json --- ...itional-spring-configuration-metadata.json | 68 ------------------- 1 file changed, 68 deletions(-) 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 7dff2bd639f2e..163684a885277 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 @@ -1668,74 +1668,6 @@ "sourceType": "com.azure.spring.cloud.service.implementation.passwordless.AzureRedisPasswordlessProperties", "defaultValue": false }, - { - "name": "spring.jms.servicebus.credential.client-id", - "type": "java.lang.String", - "description": "Client ID to use when performing service principal authentication with Azure.", - "sourceType": "com.azure.spring.cloud.autoconfigure.jms.properties.AzureServiceBusJmsProperties" - }, - { - "name": "spring.jms.servicebus.credential.client-secret", - "type": "java.lang.String", - "description": "Client secret to use when performing service principal authentication with Azure.", - "sourceType": "com.azure.spring.cloud.autoconfigure.jms.properties.AzureServiceBusJmsProperties" - }, - { - "name": "spring.jms.servicebus.credential.client-certificate-password", - "type": "java.lang.String", - "description": "Password of the certificate file.", - "sourceType": "com.azure.spring.cloud.autoconfigure.jms.properties.AzureServiceBusJmsProperties" - }, - { - "name": "spring.jms.servicebus.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.autoconfigure.jms.properties.AzureServiceBusJmsProperties" - }, - { - "name": "spring.jms.servicebus.credential.username", - "type": "java.lang.String", - "description": "Username to use when performing username\/password authentication with Azure.", - "sourceType": "com.azure.spring.cloud.autoconfigure.jms.properties.AzureServiceBusJmsProperties" - }, - { - "name": "spring.jms.servicebus.credential.password", - "type": "java.lang.String", - "description": "Password to use when performing username\/password authentication with Azure.", - "sourceType": "com.azure.spring.cloud.autoconfigure.jms.properties.AzureServiceBusJmsProperties" - }, - { - "name": "spring.jms.servicebus.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.autoconfigure.jms.properties.AzureServiceBusJmsProperties", - "defaultValue": false - }, - { - "name": "spring.jms.servicebus.profile.environment.active-directory-endpoint", - "type": "java.lang.String", - "description": "The Azure Active Directory endpoint to connect to.", - "sourceType": "com.azure.spring.cloud.autoconfigure.jms.properties.AzureServiceBusJmsProperties" - }, - { - "name": "spring.jms.servicebus.profile.tenant-id", - "type": "java.lang.String", - "description": "Tenant ID for Azure resources.", - "sourceType": "com.azure.spring.cloud.autoconfigure.jms.properties.AzureServiceBusJmsProperties" - }, - { - "name": "spring.jms.servicebus.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.autoconfigure.jms.properties.AzureServiceBusJmsProperties" - }, - { - "name": "spring.jms.servicebus.passwordless-enabled", - "type": "java.lang.Boolean", - "description": "Whether to enable passwordless connections to Azure ServiceBus by using OAuth2 Azure Active Directory token credentials.", - "sourceType": "com.azure.spring.cloud.autoconfigure.jms.properties.AzureServiceBusJmsProperties", - "defaultValue": false - }, { "name": "spring.cloud.azure.message-converter.isolated-object-mapper", "type": "java.lang.Boolean", From aa1c1f22a259173ef2d6ea877c7ee269793f09e4 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Wed, 22 Feb 2023 17:44:30 +0800 Subject: [PATCH 19/29] replace core properties with autoconfigure properties --- .../properties/AzureServiceBusJmsProperties.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) 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 af52c1fc61ebf..0c9584a5b69f6 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,9 +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 com.azure.spring.cloud.core.properties.authentication.TokenCredentialProperties; -import com.azure.spring.cloud.core.properties.profile.AzureProfileProperties; import org.springframework.beans.factory.InitializingBean; import org.springframework.boot.autoconfigure.jms.JmsPoolConnectionFactoryProperties; import org.springframework.boot.context.properties.ConfigurationProperties; @@ -44,11 +44,11 @@ public class AzureServiceBusJmsProperties implements InitializingBean, Passwordl */ public static final String PREFIX = "spring.jms.servicebus"; - private AzureProfileProperties profile = new AzureProfileProperties(); + private AzureProfileConfigurationProperties profile = new AzureProfileConfigurationProperties(); private String scopes; - private TokenCredentialProperties credential = new TokenCredentialProperties(); + private TokenCredentialConfigurationProperties credential = new TokenCredentialConfigurationProperties(); /** * Whether to enable Servive Bus JMS autoconfiguration. @@ -250,7 +250,7 @@ public void setPasswordlessEnabled(boolean passwordlessEnabled) { * @return the profile */ @Override - public AzureProfileProperties getProfile() { + public AzureProfileConfigurationProperties getProfile() { return profile; } @@ -258,7 +258,7 @@ public AzureProfileProperties getProfile() { * Set the profile * @param profile the profile properties related to an Azure subscription */ - public void setProfile(AzureProfileProperties profile) { + public void setProfile(AzureProfileConfigurationProperties profile) { this.profile = profile; } @@ -268,7 +268,7 @@ public void setProfile(AzureProfileProperties profile) { * @return the credential properties. */ @Override - public TokenCredentialOptions getCredential() { + public TokenCredentialConfigurationProperties getCredential() { return credential; } @@ -277,7 +277,7 @@ public TokenCredentialOptions getCredential() { * * @param credential the credential properties */ - public void setCredential(TokenCredentialOptions credential) { + public void setCredential(TokenCredentialConfigurationProperties credential) { this.credential = credential; } From 2e0554d5266e3aa350d6d04cdf9388b166c8d05c Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Tue, 28 Feb 2023 09:39:37 +0800 Subject: [PATCH 20/29] revert kafka passwordless codes --- .../jms/ServiceBusJmsAutoConfiguration.java | 2 - ...reKafkaOAuth2BinderConfigurationTests.java | 19 ++- ...zureKafkaOAuth2BootConfigurationTests.java | 34 ++-- .../kafka/AzureKafkaPropertiesUtils.java | 10 +- ...afkaOAuth2AuthenticateCallbackHandler.java | 40 +++-- .../AzurePasswordlessProperties.java | 152 ++++++++++++++++++ .../kafka/AzureKafkaPropertiesUtilsTest.java | 8 +- ...OAuth2AuthenticateCallbackHandlerTest.java | 36 +++-- 8 files changed, 236 insertions(+), 65 deletions(-) create mode 100644 sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzurePasswordlessProperties.java 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 6c49a46c9f5aa..25b4f7a82c22e 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 @@ -21,7 +21,6 @@ import org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration; import org.springframework.boot.autoconfigure.jms.JmsProperties; import org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration; -import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -52,7 +51,6 @@ public class ServiceBusJmsAutoConfiguration { @Bean - @ConfigurationProperties(prefix = AzureServiceBusJmsProperties.PREFIX) AzureServiceBusJmsProperties serviceBusJmsProperties() { return new AzureServiceBusJmsProperties(); } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/kafka/AzureKafkaOAuth2BinderConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/kafka/AzureKafkaOAuth2BinderConfigurationTests.java index 91a4d8057a28d..ade382a2b1169 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/kafka/AzureKafkaOAuth2BinderConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/kafka/AzureKafkaOAuth2BinderConfigurationTests.java @@ -2,13 +2,15 @@ // Licensed under the MIT License. package com.azure.spring.cloud.autoconfigure.kafka; +import com.azure.core.credential.TokenCredential; import com.azure.identity.DefaultAzureCredential; import com.azure.identity.ManagedIdentityCredential; -import com.azure.identity.extensions.implementation.credential.provider.TokenCredentialProvider; import com.azure.spring.cloud.autoconfigure.context.AzureGlobalProperties; import com.azure.spring.cloud.autoconfigure.context.AzureGlobalPropertiesAutoConfiguration; import com.azure.spring.cloud.autoconfigure.context.AzureTokenCredentialAutoConfiguration; +import com.azure.spring.cloud.core.credential.AzureCredentialResolver; import com.azure.spring.cloud.service.implementation.kafka.KafkaOAuth2AuthenticateCallbackHandler; +import com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties; import org.apache.kafka.common.config.types.Password; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; @@ -178,18 +180,21 @@ void testOAuth2ConfiguredToCallbackHandlerWithKafkaBinderProperties() { KafkaOAuth2AuthenticateCallbackHandler callbackHandler = new KafkaOAuth2AuthenticateCallbackHandler(); callbackHandler.configure(modifiedConfigs, null, null); - @SuppressWarnings("unchecked") TokenCredentialProvider tokenCredentialProvider = - (TokenCredentialProvider) ReflectionTestUtils.getField(callbackHandler, "tokenCredentialProvider"); - assertTrue(tokenCredentialProvider.get() instanceof ManagedIdentityCredential); + AzurePasswordlessProperties properties = (AzurePasswordlessProperties) ReflectionTestUtils + .getField(callbackHandler, "properties"); + AzureCredentialResolver azureTokenCredentialResolver = + (AzureCredentialResolver) ReflectionTestUtils.getField(callbackHandler, "tokenCredentialResolver"); + assertTrue(azureTokenCredentialResolver.resolve(properties) instanceof ManagedIdentityCredential); modifiedConfigs.clear(); modifiedConfigs.putAll(processor.getMergedProducerProperties(kafkaProperties)); modifiedConfigs.put(BOOTSTRAP_SERVERS_CONFIG, Arrays.asList("myehnamespace.servicebus.windows.net:9093")); modifiedConfigs.put(SASL_JAAS_CONFIG, new Password((String) modifiedConfigs.get(SASL_JAAS_CONFIG))); callbackHandler.configure(modifiedConfigs, null, null); - tokenCredentialProvider = - (TokenCredentialProvider) ReflectionTestUtils.getField(callbackHandler, "tokenCredentialProvider"); - assertTrue(tokenCredentialProvider.get() instanceof DefaultAzureCredential); + properties = (AzurePasswordlessProperties) ReflectionTestUtils.getField(callbackHandler, "properties"); + azureTokenCredentialResolver = + (AzureCredentialResolver) ReflectionTestUtils.getField(callbackHandler, "tokenCredentialResolver"); + assertTrue(azureTokenCredentialResolver.resolve(properties) instanceof DefaultAzureCredential); }); } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/kafka/AzureKafkaOAuth2BootConfigurationTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/kafka/AzureKafkaOAuth2BootConfigurationTests.java index 9e935f1b5559c..18ca365f99dac 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/kafka/AzureKafkaOAuth2BootConfigurationTests.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/kafka/AzureKafkaOAuth2BootConfigurationTests.java @@ -3,14 +3,15 @@ package com.azure.spring.cloud.autoconfigure.kafka; +import com.azure.core.credential.TokenCredential; import com.azure.identity.DefaultAzureCredential; import com.azure.identity.ManagedIdentityCredential; -import com.azure.identity.extensions.implementation.credential.provider.TokenCredentialProvider; import com.azure.spring.cloud.autoconfigure.context.AzureGlobalProperties; import com.azure.spring.cloud.autoconfigure.context.AzureGlobalPropertiesAutoConfiguration; import com.azure.spring.cloud.autoconfigure.context.AzureTokenCredentialAutoConfiguration; +import com.azure.spring.cloud.core.credential.AzureCredentialResolver; import com.azure.spring.cloud.service.implementation.kafka.KafkaOAuth2AuthenticateCallbackHandler; -import com.azure.spring.cloud.service.implementation.passwordless.AzureKafkaPasswordlessProperties; +import com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties; import org.apache.kafka.common.config.types.Password; import org.junit.jupiter.api.Test; import org.springframework.boot.autoconfigure.AutoConfigurations; @@ -178,23 +179,22 @@ void testOAuthConfiguredToCallbackHandler() { KafkaOAuth2AuthenticateCallbackHandler callbackHandler = new KafkaOAuth2AuthenticateCallbackHandler(); callbackHandler.configure(modifiedConfigs, null, null); - AzureKafkaPasswordlessProperties properties = (AzureKafkaPasswordlessProperties) ReflectionTestUtils + AzurePasswordlessProperties properties = (AzurePasswordlessProperties) ReflectionTestUtils .getField(callbackHandler, "properties"); - @SuppressWarnings("unchecked") TokenCredentialProvider tokenCredentialProvider = - (TokenCredentialProvider) ReflectionTestUtils.getField(callbackHandler, "tokenCredentialProvider"); - assertNotNull(tokenCredentialProvider); - assertTrue(tokenCredentialProvider.get() instanceof ManagedIdentityCredential); + AzureCredentialResolver azureTokenCredentialResolver = + (AzureCredentialResolver) ReflectionTestUtils.getField(callbackHandler, "tokenCredentialResolver"); + assertNotNull(azureTokenCredentialResolver); + assertTrue(azureTokenCredentialResolver.resolve(properties) instanceof ManagedIdentityCredential); Map consumerProperties = processor.getMergedConsumerProperties(getKafkaSpringProperties(context)); modifiedConfigs.clear(); modifiedConfigs.putAll(consumerProperties); modifiedConfigs.put(SASL_JAAS_CONFIG, new Password((String) modifiedConfigs.get(SASL_JAAS_CONFIG))); callbackHandler.configure(modifiedConfigs, null, null); - properties = (AzureKafkaPasswordlessProperties) ReflectionTestUtils.getField(callbackHandler, "properties"); - tokenCredentialProvider = - (TokenCredentialProvider) ReflectionTestUtils.getField(callbackHandler, "tokenCredentialProvider"); - assertNotNull(tokenCredentialProvider); - assertTrue(tokenCredentialProvider.get() instanceof DefaultAzureCredential); + properties = (AzurePasswordlessProperties) ReflectionTestUtils.getField(callbackHandler, "properties"); + azureTokenCredentialResolver = (AzureCredentialResolver) ReflectionTestUtils.getField(callbackHandler, "tokenCredentialResolver"); + assertNotNull(azureTokenCredentialResolver); + assertTrue(azureTokenCredentialResolver.resolve(properties) instanceof DefaultAzureCredential); }); } @@ -211,12 +211,12 @@ void testOAuthConfiguredToCallbackHandlerWithAzureProperties() { KafkaOAuth2AuthenticateCallbackHandler callbackHandler = new KafkaOAuth2AuthenticateCallbackHandler(); callbackHandler.configure(modifiedConfigs, null, null); - AzureKafkaPasswordlessProperties properties = (AzureKafkaPasswordlessProperties) ReflectionTestUtils + AzurePasswordlessProperties properties = (AzurePasswordlessProperties) ReflectionTestUtils .getField(callbackHandler, "properties"); - @SuppressWarnings("unchecked") TokenCredentialProvider tokenCredentialProvider = - (TokenCredentialProvider) ReflectionTestUtils.getField(callbackHandler, "tokenCredentialProvider"); - assertNotNull(tokenCredentialProvider); - assertTrue(tokenCredentialProvider.get() instanceof ManagedIdentityCredential); + AzureCredentialResolver azureTokenCredentialResolver = + (AzureCredentialResolver) ReflectionTestUtils.getField(callbackHandler, "tokenCredentialResolver"); + assertNotNull(azureTokenCredentialResolver); + assertTrue(azureTokenCredentialResolver.resolve(properties) instanceof ManagedIdentityCredential); }); } diff --git a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/kafka/AzureKafkaPropertiesUtils.java b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/kafka/AzureKafkaPropertiesUtils.java index 7585ca9f6b91a..a3ac2e2bd273e 100644 --- a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/kafka/AzureKafkaPropertiesUtils.java +++ b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/kafka/AzureKafkaPropertiesUtils.java @@ -7,7 +7,7 @@ import com.azure.spring.cloud.core.provider.AzureProfileOptionsProvider; import com.azure.spring.cloud.service.implementation.jaas.Jaas; import com.azure.spring.cloud.service.implementation.jaas.JaasResolver; -import com.azure.spring.cloud.service.implementation.passwordless.AzureKafkaPasswordlessProperties; +import com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties; import org.apache.kafka.common.security.oauthbearer.OAuthBearerLoginModule; import java.util.Collections; @@ -33,7 +33,7 @@ private AzureKafkaPropertiesUtils() { static final String PROFILE_PREFIX = "azure.profile."; static final String ENVIRONMENT_PREFIX = PROFILE_PREFIX + "environment."; - public static void copyJaasPropertyToAzureProperties(String source, AzureKafkaPasswordlessProperties target) { + public static void copyJaasPropertyToAzureProperties(String source, AzurePasswordlessProperties target) { JaasResolver resolver = new JaasResolver(); Jaas jaas = resolver.resolve(source).orElse(new Jaas(OAuthBearerLoginModule.class.getName())); Map map = jaas.getOptions(); @@ -172,9 +172,9 @@ private static List buildPropertyKeys() { private String propertyKey; private Function getter; - private BiConsumer setter; + private BiConsumer setter; - AzureKafkaPasswordlessPropertiesMapping(String propertyKey, Function getter, BiConsumer getter, BiConsumer setter) { this.propertyKey = propertyKey; this.getter = getter; @@ -189,7 +189,7 @@ public Function getter() { return getter; } - public BiConsumer setter() { + public BiConsumer setter() { return setter; } diff --git a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/kafka/KafkaOAuth2AuthenticateCallbackHandler.java b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/kafka/KafkaOAuth2AuthenticateCallbackHandler.java index a036af01a4ce9..3f325e33a8622 100644 --- a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/kafka/KafkaOAuth2AuthenticateCallbackHandler.java +++ b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/kafka/KafkaOAuth2AuthenticateCallbackHandler.java @@ -4,10 +4,11 @@ import com.azure.core.credential.TokenCredential; import com.azure.core.credential.TokenRequestContext; -import com.azure.identity.extensions.implementation.credential.TokenCredentialProviderOptions; -import com.azure.identity.extensions.implementation.credential.provider.TokenCredentialProvider; import com.azure.spring.cloud.core.credential.AzureCredentialResolver; -import com.azure.spring.cloud.service.implementation.passwordless.AzureKafkaPasswordlessProperties; +import com.azure.spring.cloud.core.implementation.credential.resolver.AzureTokenCredentialResolver; +import com.azure.spring.cloud.core.implementation.factory.credential.DefaultAzureCredentialBuilderFactory; +import com.azure.spring.cloud.core.properties.AzureProperties; +import com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties; import org.apache.kafka.common.config.types.Password; import org.apache.kafka.common.security.auth.AuthenticateCallbackHandler; import org.apache.kafka.common.security.oauthbearer.OAuthBearerTokenCallback; @@ -34,17 +35,19 @@ public class KafkaOAuth2AuthenticateCallbackHandler implements AuthenticateCallb private static final Duration ACCESS_TOKEN_REQUEST_BLOCK_TIME = Duration.ofSeconds(30); private static final String TOKEN_AUDIENCE_FORMAT = "%s://%s/.default"; - private final AzureKafkaPasswordlessProperties properties; + private final AzurePasswordlessProperties properties; + private final AzureCredentialResolver externalTokenCredentialResolver; - private TokenCredentialProvider tokenCredentialProvider; + private AzureCredentialResolver tokenCredentialResolver; private Function> resolveToken; public KafkaOAuth2AuthenticateCallbackHandler() { this(null, null); } - public KafkaOAuth2AuthenticateCallbackHandler(AzureKafkaPasswordlessProperties properties, AzureCredentialResolver externalTokenCredentialResolver) { - this.properties = properties == null ? new AzureKafkaPasswordlessProperties() : properties; + public KafkaOAuth2AuthenticateCallbackHandler(AzurePasswordlessProperties properties, AzureCredentialResolver externalTokenCredentialResolver) { + this.properties = properties == null ? new AzurePasswordlessProperties() : properties; + this.externalTokenCredentialResolver = externalTokenCredentialResolver == null ? new AzureTokenCredentialResolver() : externalTokenCredentialResolver; } @Override @@ -54,7 +57,7 @@ public void configure(Map configs, String mechanism, List tokenCredential.getToken(request).map(AzureOAuthBearerToken::new); - this.tokenCredentialProvider = new InternalTokenCredentialProvider(TokenCredentialProvider.createDefault(new TokenCredentialProviderOptions(properties.toPasswordlessProperties())), configs); + this.tokenCredentialResolver = new InternalCredentialResolver(externalTokenCredentialResolver, configs); } private TokenRequestContext buildTokenRequestContext(Map configs) { @@ -92,7 +95,7 @@ public void handle(Callback[] callbacks) throws UnsupportedCallbackException { if (callback instanceof OAuthBearerTokenCallback) { OAuthBearerTokenCallback oauthCallback = (OAuthBearerTokenCallback) callback; this.resolveToken - .apply(tokenCredentialProvider.get()) + .apply(tokenCredentialResolver.resolve(properties)) .doOnNext(oauthCallback::token) .doOnError(throwable -> oauthCallback.error("invalid_grant", throwable.getMessage(), null)) .block(ACCESS_TOKEN_REQUEST_BLOCK_TIME); @@ -107,26 +110,35 @@ public void close() { // NOOP } - private static class InternalTokenCredentialProvider implements TokenCredentialProvider { - private final TokenCredentialProvider delegated; + private static class InternalCredentialResolver implements AzureCredentialResolver { + private final AzureCredentialResolver delegated; private final Map configs; private TokenCredential credential; - InternalTokenCredentialProvider(TokenCredentialProvider delegated, Map configs) { + InternalCredentialResolver(AzureCredentialResolver delegated, Map configs) { this.delegated = delegated; this.configs = configs; } @Override - public TokenCredential get() { + public TokenCredential resolve(AzureProperties properties) { if (credential == null) { credential = (TokenCredential) configs.get(AZURE_TOKEN_CREDENTIAL); // Resolve the token credential when there is no credential passed from configs. if (credential == null) { - credential = delegated.get(); + credential = delegated.resolve(properties); + if (credential == null) { + // Create DefaultAzureCredential when no credential can be resolved from configs. + credential = new DefaultAzureCredentialBuilderFactory(properties).build().build(); + } } } return credential; } + + @Override + public boolean isResolvable(AzureProperties properties) { + return true; + } } } diff --git a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzurePasswordlessProperties.java b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzurePasswordlessProperties.java new file mode 100644 index 0000000000000..53b08b91327a5 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzurePasswordlessProperties.java @@ -0,0 +1,152 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.cloud.service.implementation.passwordless; + +import com.azure.identity.extensions.implementation.enums.AuthProperty; +import com.azure.spring.cloud.core.properties.AzureProperties; +import com.azure.spring.cloud.core.properties.authentication.TokenCredentialProperties; +import com.azure.spring.cloud.core.properties.client.ClientProperties; +import com.azure.spring.cloud.core.properties.profile.AzureProfileProperties; +import com.azure.spring.cloud.core.properties.proxy.ProxyProperties; +import com.azure.spring.cloud.core.provider.AzureProfileOptionsProvider; +import com.azure.spring.cloud.core.provider.authentication.TokenCredentialOptionsProvider; + +import java.util.Properties; +import java.util.function.BiConsumer; +import java.util.function.Function; + +/** + * Implement {@link TokenCredentialOptionsProvider} and {@link AzureProfileOptionsProvider} for Spring Cloud Azure + * support for other third party services. + */ +public class AzurePasswordlessProperties implements AzureProperties { + + private AzureProfileProperties profile = new AzureProfileProperties(); + + private String scopes; + + private TokenCredentialProperties credential = new TokenCredentialProperties(); + + // Use client options inside credential for azure identity + private ClientProperties client = new ClientProperties(); + + // Use proxy options inside credential for azure identity + private ProxyProperties proxy = new ProxyProperties(); + + // Whether to enable supporting azure identity token credentials + private boolean passwordlessEnabled = false; + + @Override + public AzureProfileProperties getProfile() { + return profile; + } + + public void setProfile(AzureProfileProperties profile) { + this.profile = profile; + } + + @Override + public TokenCredentialProperties getCredential() { + return credential; + } + + public void setCredential(TokenCredentialProperties credential) { + this.credential = credential; + } + + @Override + public ClientOptions getClient() { + return client; + } + + public void setClient(ClientProperties client) { + this.client = client; + } + + @Override + public ProxyOptions getProxy() { + return proxy; + } + + public void setProxy(ProxyProperties proxy) { + this.proxy = proxy; + } + + public boolean isPasswordlessEnabled() { + return passwordlessEnabled; + } + + public void setPasswordlessEnabled(boolean passwordlessEnabled) { + this.passwordlessEnabled = passwordlessEnabled; + } + + public String getScopes() { + return scopes; + } + + public void setScopes(String scopes) { + this.scopes = scopes; + } + + public Properties toProperties() { + Properties target = new Properties(); + for (AzurePasswordlessPropertiesMapping m : AzurePasswordlessPropertiesMapping.values()) { + if (m.getter.apply(this) != null) { + m.setter.accept(target, m.getter.apply(this)); + } + } + return target; + } + + private enum AzurePasswordlessPropertiesMapping { + + scopes(p -> p.getScopes(), + (p, s) -> p.setProperty(AuthProperty.SCOPES.getPropertyKey(), s)), + + clientCertificatePassword(p -> p.getCredential().getClientCertificatePassword(), + (p, s) -> p.setProperty(AuthProperty.CLIENT_CERTIFICATE_PASSWORD.getPropertyKey(), s)), + + clientCertificatePath(p -> p.getCredential().getClientCertificatePath(), + (p, s) -> p.setProperty(AuthProperty.CLIENT_CERTIFICATE_PATH.getPropertyKey(), s)), + + clientId(p -> p.getCredential().getClientId(), + (p, s) -> p.setProperty(AuthProperty.CLIENT_ID.getPropertyKey(), s)), + + clientSecret(p -> p.getCredential().getClientSecret(), + (p, s) -> p.setProperty(AuthProperty.CLIENT_SECRET.getPropertyKey(), s)), + + managedIdentityEnabled(p -> String.valueOf(p.getCredential().isManagedIdentityEnabled()), + (p, s) -> p.setProperty(AuthProperty.MANAGED_IDENTITY_ENABLED.getPropertyKey(), s)), + + password(p -> p.getCredential().getPassword(), + (p, s) -> p.setProperty(AuthProperty.PASSWORD.getPropertyKey(), s)), + + username(p -> p.getCredential().getUsername(), + (p, s) -> p.setProperty(AuthProperty.USERNAME.getPropertyKey(), s)), + + tenantId(p -> p.getProfile().getTenantId(), + (p, s) -> p.setProperty(AuthProperty.TENANT_ID.getPropertyKey(), s)), + + authorityHost(p -> p.getProfile().getEnvironment().getActiveDirectoryEndpoint(), + (p, s) -> p.setProperty(AuthProperty.AUTHORITY_HOST.getPropertyKey(), s)); + + private Function getter; + private BiConsumer setter; + + AzurePasswordlessPropertiesMapping(Function getter, BiConsumer setter) { + this.getter = getter; + this.setter = setter; + } + + public Function getter() { + return getter; + } + + public BiConsumer setter() { + return setter; + } + + } +} diff --git a/sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/implementation/kafka/AzureKafkaPropertiesUtilsTest.java b/sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/implementation/kafka/AzureKafkaPropertiesUtilsTest.java index f8592b2213581..fe7b7601d7a1e 100644 --- a/sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/implementation/kafka/AzureKafkaPropertiesUtilsTest.java +++ b/sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/implementation/kafka/AzureKafkaPropertiesUtilsTest.java @@ -3,7 +3,7 @@ package com.azure.spring.cloud.service.implementation.kafka; import com.azure.spring.cloud.core.provider.AzureProfileOptionsProvider; -import com.azure.spring.cloud.service.implementation.passwordless.AzureKafkaPasswordlessProperties; +import com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties; import org.junit.jupiter.api.Test; import static com.azure.spring.cloud.service.implementation.kafka.AzureKafkaPropertiesUtils.copyJaasPropertyToAzureProperties; @@ -17,7 +17,7 @@ class AzureKafkaPropertiesUtilsTest { @Test void testCopyJaasPropertyToAzureProperties() { String jaasConfig = "org.apache.kafka.common.security.oauthbearer.OAuthBearerLoginModule required azure.configured=\"true\";"; - AzureKafkaPasswordlessProperties properties = new AzureKafkaPasswordlessProperties(); + AzurePasswordlessProperties properties = new AzurePasswordlessProperties(); copyJaasPropertyToAzureProperties(jaasConfig, properties); assertFalse(properties.getCredential().isManagedIdentityEnabled()); @@ -29,7 +29,7 @@ void testCopyJaasPropertyToAzureProperties() { void testCopyJaasPropertyWithCustomizedValuesToAzureProperties() { String jaasConfig = "org.apache.kafka.common.security.oauthbearer.OAuthBearerLoginModule required azure.configured=\"true\" " + "azure.credential.managed-identity-enabled=\"true\" azure.credential.client-id=\"test\" azure.profile.cloud-type=\"azure\";"; - AzureKafkaPasswordlessProperties properties = new AzureKafkaPasswordlessProperties(); + AzurePasswordlessProperties properties = new AzurePasswordlessProperties(); copyJaasPropertyToAzureProperties(jaasConfig, properties); assertTrue(properties.getCredential().isManagedIdentityEnabled()); @@ -40,7 +40,7 @@ void testCopyJaasPropertyWithCustomizedValuesToAzureProperties() { @Test void testClearAzureProperties() { - AzureKafkaPasswordlessProperties properties = new AzureKafkaPasswordlessProperties(); + AzurePasswordlessProperties properties = new AzurePasswordlessProperties(); properties.getProfile().setCloudType(AzureProfileOptionsProvider.CloudType.AZURE); properties.getProfile().setTenantId("fake-tenant-id"); } diff --git a/sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/implementation/kafka/KafkaOAuth2AuthenticateCallbackHandlerTest.java b/sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/implementation/kafka/KafkaOAuth2AuthenticateCallbackHandlerTest.java index 26d28edbb998f..403828fa7bdb5 100644 --- a/sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/implementation/kafka/KafkaOAuth2AuthenticateCallbackHandlerTest.java +++ b/sdk/spring/spring-cloud-azure-service/src/test/java/com/azure/spring/cloud/service/implementation/kafka/KafkaOAuth2AuthenticateCallbackHandlerTest.java @@ -7,9 +7,9 @@ import com.azure.core.credential.TokenRequestContext; import com.azure.identity.DefaultAzureCredential; import com.azure.identity.ManagedIdentityCredential; -import com.azure.identity.extensions.implementation.credential.provider.TokenCredentialProvider; +import com.azure.spring.cloud.core.credential.AzureCredentialResolver; import com.azure.spring.cloud.service.implementation.jaas.Jaas; -import com.azure.spring.cloud.service.implementation.passwordless.AzureKafkaPasswordlessProperties; +import com.azure.spring.cloud.service.implementation.passwordless.AzurePasswordlessProperties; import org.apache.kafka.common.config.types.Password; import org.apache.kafka.common.security.oauthbearer.OAuthBearerLoginModule; import org.apache.kafka.common.security.oauthbearer.OAuthBearerTokenCallback; @@ -43,7 +43,7 @@ class KafkaOAuth2AuthenticateCallbackHandlerTest { private static final List KAFKA_BOOTSTRAP_SERVER = Arrays.asList("namespace.servicebus.windows.net:9093"); private static final String AZURE_THIRD_PARTY_SERVICE_PROPERTIES_FIELD_NAME = "properties"; - private static final String TOKEN_CREDENTIAL_PROVIDER_FIELD_NAME = "tokenCredentialProvider"; + private static final String TOKEN_CREDENTIAL_RESOLVER_FIELD_NAME = "tokenCredentialResolver"; @Test void testTokenCredentialShouldConfig() { TokenCredential tokenCredential = new TokenCredential() { @@ -59,10 +59,12 @@ public Mono getToken(TokenRequestContext tokenRequestContext) { KafkaOAuth2AuthenticateCallbackHandler handler = new KafkaOAuth2AuthenticateCallbackHandler(); handler.configure(configs, null, null); - @SuppressWarnings("unchecked") TokenCredentialProvider tokenCredentialProvider = - (TokenCredentialProvider) ReflectionTestUtils.getField(handler, TOKEN_CREDENTIAL_PROVIDER_FIELD_NAME); - assertNotNull(tokenCredentialProvider); - assertEquals(tokenCredential, tokenCredentialProvider.get()); + AzurePasswordlessProperties properties = (AzurePasswordlessProperties) ReflectionTestUtils + .getField(handler, AZURE_THIRD_PARTY_SERVICE_PROPERTIES_FIELD_NAME); + @SuppressWarnings("unchecked") AzureCredentialResolver azureTokenCredentialResolver = + (AzureCredentialResolver) ReflectionTestUtils.getField(handler, TOKEN_CREDENTIAL_RESOLVER_FIELD_NAME); + assertNotNull(azureTokenCredentialResolver); + assertEquals(tokenCredential, azureTokenCredentialResolver.resolve(properties)); } @Test @@ -72,10 +74,12 @@ void testCreateDefaultTokenCredential() { KafkaOAuth2AuthenticateCallbackHandler handler = new KafkaOAuth2AuthenticateCallbackHandler(); handler.configure(configs, null, null); - @SuppressWarnings("unchecked") TokenCredentialProvider tokenCredentialProvider = - (TokenCredentialProvider) ReflectionTestUtils.getField(handler, TOKEN_CREDENTIAL_PROVIDER_FIELD_NAME); - assertNotNull(tokenCredentialProvider); - assertTrue(tokenCredentialProvider.get() instanceof DefaultAzureCredential); + AzurePasswordlessProperties properties = (AzurePasswordlessProperties) ReflectionTestUtils + .getField(handler, AZURE_THIRD_PARTY_SERVICE_PROPERTIES_FIELD_NAME); + @SuppressWarnings("unchecked") AzureCredentialResolver azureTokenCredentialResolver = + (AzureCredentialResolver) ReflectionTestUtils.getField(handler, TOKEN_CREDENTIAL_RESOLVER_FIELD_NAME); + assertNotNull(azureTokenCredentialResolver); + assertTrue(azureTokenCredentialResolver.resolve(properties) instanceof DefaultAzureCredential); } @Test @@ -89,13 +93,13 @@ void testCreateTokenCredentialByResolver() { KafkaOAuth2AuthenticateCallbackHandler handler = new KafkaOAuth2AuthenticateCallbackHandler(); handler.configure(configs, null, null); - AzureKafkaPasswordlessProperties properties = (AzureKafkaPasswordlessProperties) ReflectionTestUtils + AzurePasswordlessProperties properties = (AzurePasswordlessProperties) ReflectionTestUtils .getField(handler, AZURE_THIRD_PARTY_SERVICE_PROPERTIES_FIELD_NAME); assertTrue(properties.getCredential().isManagedIdentityEnabled()); - @SuppressWarnings("unchecked") TokenCredentialProvider tokenCredentialProvider = - (TokenCredentialProvider) ReflectionTestUtils.getField(handler, TOKEN_CREDENTIAL_PROVIDER_FIELD_NAME); - assertNotNull(tokenCredentialProvider); - assertTrue(tokenCredentialProvider.get() instanceof ManagedIdentityCredential); + @SuppressWarnings("unchecked") AzureCredentialResolver azureTokenCredentialResolver = + (AzureCredentialResolver) ReflectionTestUtils.getField(handler, TOKEN_CREDENTIAL_RESOLVER_FIELD_NAME); + assertNotNull(azureTokenCredentialResolver); + assertTrue(azureTokenCredentialResolver.resolve(properties) instanceof ManagedIdentityCredential); } @Test From 61ee897a2a364ef16f88e99c99476671c0dfb526 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Tue, 28 Feb 2023 10:36:31 +0800 Subject: [PATCH 21/29] fix some typos --- .../jdbc/JdbcPropertiesBeanPostProcessor.java | 20 +++++++++---------- .../jms/ServiceBusJmsAutoConfiguration.java | 4 ++-- .../AzureServiceBusJmsProperties.java | 20 ++++++++++++------- ...ePasswordlessEnvironmentPostProcessor.java | 2 +- .../passwordless/package-info.java | 2 +- .../AzurePasswordlessPropertiesUtils.java | 4 ++-- 6 files changed, 29 insertions(+), 23 deletions(-) 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 6949361d08e40..19c2a830acf87 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 @@ -78,9 +78,9 @@ public Object postProcessBeforeInitialization(Object bean, String beanName) thro boolean isPasswordProvided = StringUtils.hasText(dataSourceProperties.getPassword()); if (isPasswordProvided) { LOGGER.debug( - "If you are using Azure hosted services," - + "it is encouraged to use the passwordless feature. " - + "Please refer to https://aka.ms/passwordless-connections."); + "If you are using Azure hosted services," + + "it is encouraged to use the passwordless feature. " + + "Please refer to https://aka.ms/passwordless-connections."); return bean; } @@ -106,17 +106,17 @@ private void enhanceUserAgent(DatabaseType databaseType, JdbcConnectionStringEnh if (DatabaseType.MYSQL == databaseType) { Map enhancedAttributes = new HashMap<>(); enhancedAttributes.put(MYSQL_PROPERTY_CONNECTION_ATTRIBUTES_ATTRIBUTE_EXTENSION_VERSION, - AzureSpringIdentifier.AZURE_SPRING_MYSQL_OAUTH); + AzureSpringIdentifier.AZURE_SPRING_MYSQL_OAUTH); enhancer.enhancePropertyAttributes( - MYSQL_PROPERTY_NAME_CONNECTION_ATTRIBUTES, - enhancedAttributes, - MYSQL_PROPERTY_CONNECTION_ATTRIBUTES_DELIMITER, - MYSQL_PROPERTY_CONNECTION_ATTRIBUTES_KV_DELIMITER + MYSQL_PROPERTY_NAME_CONNECTION_ATTRIBUTES, + enhancedAttributes, + MYSQL_PROPERTY_CONNECTION_ATTRIBUTES_DELIMITER, + MYSQL_PROPERTY_CONNECTION_ATTRIBUTES_KV_DELIMITER ); } else if (DatabaseType.POSTGRESQL == databaseType) { Map enhancedProperties = new HashMap<>(); enhancedProperties.put(POSTGRESQL_PROPERTY_NAME_APPLICATION_NAME, - AzureSpringIdentifier.AZURE_SPRING_POSTGRESQL_OAUTH); + AzureSpringIdentifier.AZURE_SPRING_POSTGRESQL_OAUTH); // Set property assumeMinServerVersion with value "9.0.0" here for the following reasons: // 1. We need to set application_name in paramList to build connections with postgresql server, in order to do that, the number of assumeVersion must >= 9.0.0. // https://github.com/pgjdbc/pgjdbc/blob/98c04a0c903e90f2d5d10a09baf1f753747b2556/pgjdbc/src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java#L360 @@ -124,7 +124,7 @@ private void enhanceUserAgent(DatabaseType databaseType, JdbcConnectionStringEnh // https://learn.microsoft.com/azure/postgresql/single-server/concepts-supported-versions // https://learn.microsoft.com/azure/postgresql/flexible-server/concepts-supported-versions enhancedProperties.put(POSTGRESQL_PROPERTY_NAME_ASSUME_MIN_SERVER_VERSION, - POSTGRESQL_PROPERTY_VALUE_ASSUME_MIN_SERVER_VERSION); + POSTGRESQL_PROPERTY_VALUE_ASSUME_MIN_SERVER_VERSION); enhancer.enhanceProperties(enhancedProperties, true); } } 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 25b4f7a82c22e..df8efbbc6ce39 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 @@ -46,8 +46,8 @@ AzureServiceBusResourceManagerAutoConfiguration.class }) @ConditionalOnProperty(value = "spring.jms.servicebus.enabled", matchIfMissing = true) @ConditionalOnClass({ ConnectionFactory.class, JmsConnectionFactory.class, JmsTemplate.class }) -@EnableConfigurationProperties({JmsProperties.class}) -@Import({ServiceBusJmsPasswordlessConfiguration.class, ServiceBusJmsConnectionFactoryConfiguration.class, ServiceBusJmsContainerConfiguration.class}) +@EnableConfigurationProperties({ JmsProperties.class }) +@Import({ ServiceBusJmsPasswordlessConfiguration.class, ServiceBusJmsConnectionFactoryConfiguration.class, ServiceBusJmsContainerConfiguration.class }) public class ServiceBusJmsAutoConfiguration { @Bean 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 0c9584a5b69f6..da6f9e3e0c017 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 @@ -23,14 +23,16 @@ @ConfigurationProperties(prefix = AzureServiceBusJmsProperties.PREFIX) 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; - // Whether to enable supporting azure identity token credentials - private boolean passwordlessEnabled = false; - private static final Map SERVICEBUS_SCOPE_MAP = new HashMap() { { put(CloudType.AZURE, SERVICE_BUS_SCOPE_AZURE); @@ -39,17 +41,21 @@ public class AzureServiceBusJmsProperties implements InitializingBean, Passwordl put(CloudType.AZURE_US_GOVERNMENT, SERVICE_BUS_SCOPE_AZURE_US_GOVERNMENT); } }; - /** - * Service Bus JMS properties prefix. - */ - public static final String PREFIX = "spring.jms.servicebus"; 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. + */ + private boolean passwordlessEnabled = false; + /** * Whether to enable Servive Bus JMS autoconfiguration. */ diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/passwordless/AzurePasswordlessEnvironmentPostProcessor.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/passwordless/AzurePasswordlessEnvironmentPostProcessor.java index c962be40eaa3a..7996a7d40045c 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/passwordless/AzurePasswordlessEnvironmentPostProcessor.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/passwordless/AzurePasswordlessEnvironmentPostProcessor.java @@ -15,7 +15,7 @@ import java.util.Properties; /** - * Add properties to 'spring.cloud.function.ineligible-definitions' to filter ineligible functions that used by passwordless autoconfiguration. + * Add properties to 'spring.cloud.function.ineligible-definitions' to filter ineligible functions that used by passwordless autoconfigurations. * * @since 4.7.0 */ diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/passwordless/package-info.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/passwordless/package-info.java index a5407daf14788..3b891d048b727 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/passwordless/package-info.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/passwordless/package-info.java @@ -2,6 +2,6 @@ // Licensed under the MIT License. /** - * Spring Cloud Azure's auto-configuration for Spring Message. + * Spring Cloud Azure's EnvironmentPostProcessor for passwordless auto-configurations. */ package com.azure.spring.cloud.autoconfigure.passwordless; diff --git a/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/implementation/util/AzurePasswordlessPropertiesUtils.java b/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/implementation/util/AzurePasswordlessPropertiesUtils.java index 34ef933ddf831..9f79f5335a53b 100644 --- a/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/implementation/util/AzurePasswordlessPropertiesUtils.java +++ b/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/implementation/util/AzurePasswordlessPropertiesUtils.java @@ -31,7 +31,7 @@ private AzurePasswordlessPropertiesUtils() { * * @param source The source {@link AzureProperties} object. * @param target The target object. - * @param The type of the target that extends AzureProperties. + * @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 @@ -55,7 +55,7 @@ public static void copyAzureCommonProperties( * * @param source The source {@link PasswordlessProperties} object. * @param target The target object. - * @param The type of the target that extends AzureProperties. + * @param The type of the target that extends PasswordlessProperties. */ public static void copyAzureCommonPropertiesIgnoreNull(PasswordlessProperties source, T target) { From d9153841d4edd445864cec8b1a3a0f97f6b3cc07 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Wed, 1 Mar 2023 14:52:04 +0800 Subject: [PATCH 22/29] add test case for AzurePasswordlessPropertiesUtils --- .../AzurePasswordlessPropertiesUtils.java | 21 +- .../AzurePasswordlessPropertiesUtilsTest.java | 385 ++++++++++++++++++ 2 files changed, 388 insertions(+), 18 deletions(-) create mode 100644 sdk/spring/spring-cloud-azure-core/src/test/java/com/azure/spring/cloud/core/implementation/util/AzurePasswordlessPropertiesUtilsTest.java diff --git a/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/implementation/util/AzurePasswordlessPropertiesUtils.java b/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/implementation/util/AzurePasswordlessPropertiesUtils.java index 9f79f5335a53b..dc950c5108454 100644 --- a/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/implementation/util/AzurePasswordlessPropertiesUtils.java +++ b/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/implementation/util/AzurePasswordlessPropertiesUtils.java @@ -3,9 +3,8 @@ package com.azure.spring.cloud.core.implementation.util; -import com.azure.spring.cloud.core.properties.PasswordlessProperties; import com.azure.spring.cloud.core.properties.AzureProperties; -import com.azure.spring.cloud.core.provider.RetryOptionsProvider; +import com.azure.spring.cloud.core.properties.PasswordlessProperties; import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanWrapper; import org.springframework.beans.BeanWrapperImpl; @@ -40,13 +39,6 @@ public static void copyAzureCommonProperties( BeanUtils.copyProperties(source.getProfile().getEnvironment(), target.getProfile().getEnvironment()); BeanUtils.copyProperties(source.getCredential(), target.getCredential()); - if (source instanceof RetryOptionsProvider && target instanceof RetryOptionsProvider) { - RetryOptionsProvider.RetryOptions sourceRetry = ((RetryOptionsProvider) source).getRetry(); - RetryOptionsProvider.RetryOptions targetRetry = ((RetryOptionsProvider) target).getRetry(); - BeanUtils.copyProperties(sourceRetry, targetRetry); - BeanUtils.copyProperties(sourceRetry.getExponential(), targetRetry.getExponential()); - BeanUtils.copyProperties(sourceRetry.getFixed(), targetRetry.getFixed()); - } } /** @@ -63,13 +55,8 @@ public static void copyAzureCommonPropertiesI copyPropertiesIgnoreNull(source.getProfile().getEnvironment(), target.getProfile().getEnvironment()); copyPropertiesIgnoreNull(source.getCredential(), target.getCredential()); - if (source instanceof RetryOptionsProvider && target instanceof RetryOptionsProvider) { - RetryOptionsProvider.RetryOptions sourceRetry = ((RetryOptionsProvider) source).getRetry(); - RetryOptionsProvider.RetryOptions targetRetry = ((RetryOptionsProvider) target).getRetry(); - copyPropertiesIgnoreNull(sourceRetry, targetRetry); - copyPropertiesIgnoreNull(sourceRetry.getExponential(), targetRetry.getExponential()); - copyPropertiesIgnoreNull(sourceRetry.getFixed(), targetRetry.getFixed()); - } + target.setScopes(source.getScopes()); + target.setPasswordlessEnabled(source.isPasswordlessEnabled()); } /** @@ -86,8 +73,6 @@ public static void mergeAzureCommonProperties T target) { copyAzureCommonProperties(defaultProperties, target); copyAzureCommonPropertiesIgnoreNull(properties, target); - target.setScopes(properties.getScopes()); - target.setPasswordlessEnabled(properties.isPasswordlessEnabled()); } /** diff --git a/sdk/spring/spring-cloud-azure-core/src/test/java/com/azure/spring/cloud/core/implementation/util/AzurePasswordlessPropertiesUtilsTest.java b/sdk/spring/spring-cloud-azure-core/src/test/java/com/azure/spring/cloud/core/implementation/util/AzurePasswordlessPropertiesUtilsTest.java new file mode 100644 index 0000000000000..dc552df767f73 --- /dev/null +++ b/sdk/spring/spring-cloud-azure-core/src/test/java/com/azure/spring/cloud/core/implementation/util/AzurePasswordlessPropertiesUtilsTest.java @@ -0,0 +1,385 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.spring.cloud.core.implementation.util; + +import com.azure.core.management.AzureEnvironment; +import com.azure.spring.cloud.core.properties.AzureProperties; +import com.azure.spring.cloud.core.properties.PasswordlessProperties; +import com.azure.spring.cloud.core.properties.authentication.TokenCredentialProperties; +import com.azure.spring.cloud.core.properties.client.ClientProperties; +import com.azure.spring.cloud.core.properties.profile.AzureProfileProperties; +import com.azure.spring.cloud.core.properties.proxy.ProxyProperties; +import com.azure.spring.cloud.core.properties.retry.RetryProperties; +import com.azure.spring.cloud.core.provider.RetryOptionsProvider; +import org.junit.jupiter.api.Test; + +import java.time.Duration; + +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 com.azure.spring.cloud.core.provider.AzureProfileOptionsProvider.CloudType.OTHER; +import static com.azure.spring.cloud.core.provider.RetryOptionsProvider.RetryMode.FIXED; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class AzurePasswordlessPropertiesUtilsTest { + + @Test + void testCopyPropertiesToNewObjectShouldEqual() { + AzurePropertiesA source = new AzurePropertiesA(); + source.client.setApplicationId("client-application-id-A"); + source.profile.setCloudType(AZURE_CHINA); + source.profile.setTenantId("profile-tenant-id-A"); + source.profile.setSubscriptionId("profile-sub-id-A"); + source.profile.getEnvironment().setActiveDirectoryEndpoint("aad-endpoint-A"); + source.proxy.setType("proxy-type-A"); + source.proxy.setHostname("proxy-hostname-A"); + source.proxy.setPort(1234); + source.proxy.setUsername("proxy-username-A"); + source.proxy.setPassword("proxy-password-A"); + source.retry.setMode(FIXED); + source.retry.getExponential().setMaxRetries(3); + source.retry.getExponential().setBaseDelay(Duration.ofSeconds(4)); + source.retry.getExponential().setMaxDelay(Duration.ofSeconds(5)); + source.retry.getFixed().setDelay(Duration.ofSeconds(6)); + source.retry.getFixed().setMaxRetries(7); + source.credential.setClientId("credential-client-id-A"); + source.credential.setClientSecret("credential-client-secret-A"); + source.credential.setClientCertificatePath("credential-client-cert-path-A"); + source.credential.setClientCertificatePassword("credential-client-cert-password-A"); + source.credential.setUsername("credential-username-A"); + source.credential.setPassword("credential-password-A"); + source.credential.setManagedIdentityEnabled(true); + + final PasswordlessPropertiesB target = new PasswordlessPropertiesB(); + AzurePasswordlessPropertiesUtils.copyAzureCommonProperties(source, target); + + assertEquals(AZURE_CHINA, target.profile.getCloudType()); + assertEquals("profile-tenant-id-A", target.profile.getTenantId()); + assertEquals("profile-sub-id-A", target.profile.getSubscriptionId()); + assertEquals("aad-endpoint-A", target.profile.getEnvironment().getActiveDirectoryEndpoint()); + assertEquals(AzureEnvironment.AZURE_CHINA.getActiveDirectoryGraphApiVersion(), target.profile.getEnvironment().getActiveDirectoryGraphApiVersion()); + assertEquals("credential-client-id-A", target.credential.getClientId()); + assertEquals("credential-client-secret-A", target.credential.getClientSecret()); + assertEquals("credential-client-cert-path-A", target.credential.getClientCertificatePath()); + assertEquals("credential-client-cert-password-A", target.credential.getClientCertificatePassword()); + assertEquals("credential-username-A", target.credential.getUsername()); + assertEquals("credential-password-A", target.credential.getPassword()); + + } + + @Test + void testCopyPropertiesToObjectWithSameFieldsSetShouldOverrideWithNull() { + PasswordlessPropertiesB target = new PasswordlessPropertiesB(); + target.profile.setCloudType(AZURE); + target.profile.setTenantId("profile-tenant-id-B"); + target.profile.setSubscriptionId("profile-sub-id-B"); + target.profile.getEnvironment().setActiveDirectoryEndpoint("aad-endpoint-B"); + target.credential.setClientId("credential-client-id-B"); + target.credential.setClientSecret("credential-client-secret-B"); + target.credential.setClientCertificatePath("credential-client-cert-path-B"); + target.credential.setClientCertificatePassword("credential-client-cert-password-B"); + target.credential.setUsername("credential-username-B"); + target.credential.setPassword("credential-password-B"); + target.credential.setManagedIdentityEnabled(true); + + // assert properties have been set correctly to target + assertEquals(AZURE, target.profile.getCloudType()); + assertEquals("profile-tenant-id-B", target.profile.getTenantId()); + assertEquals("profile-sub-id-B", target.profile.getSubscriptionId()); + assertEquals("aad-endpoint-B", target.profile.getEnvironment().getActiveDirectoryEndpoint()); + assertEquals(AzureEnvironment.AZURE.getActiveDirectoryGraphApiVersion(), target.profile.getEnvironment().getActiveDirectoryGraphApiVersion()); + assertEquals("credential-client-id-B", target.credential.getClientId()); + assertEquals("credential-client-secret-B", target.credential.getClientSecret()); + assertEquals("credential-client-cert-path-B", target.credential.getClientCertificatePath()); + assertEquals("credential-client-cert-password-B", target.credential.getClientCertificatePassword()); + assertEquals("credential-username-B", target.credential.getUsername()); + assertEquals("credential-password-B", target.credential.getPassword()); + + AzurePropertiesA source = new AzurePropertiesA(); + source.client.setApplicationId("client-application-id-A"); + source.profile.setCloudType(AZURE_CHINA); + source.profile.setTenantId("profile-tenant-id-A"); + source.proxy.setHostname("proxy-hostname-A"); + source.retry.getExponential().setMaxRetries(13); + source.retry.getExponential().setMaxDelay(Duration.ofSeconds(14)); + source.credential.setClientId("credential-client-id-A"); + + AzurePasswordlessPropertiesUtils.copyAzureCommonProperties(source, target); + + // assert properties have been set correctly to target after copy + assertEquals(AZURE_CHINA, target.profile.getCloudType()); + assertEquals("profile-tenant-id-A", target.profile.getTenantId()); + assertNull(target.profile.getSubscriptionId()); + assertEquals(AzureEnvironment.AZURE_CHINA.getActiveDirectoryEndpoint(), target.profile.getEnvironment().getActiveDirectoryEndpoint()); + assertEquals(AzureEnvironment.AZURE_CHINA.getActiveDirectoryGraphApiVersion(), target.profile.getEnvironment().getActiveDirectoryGraphApiVersion()); + assertEquals("credential-client-id-A", target.credential.getClientId()); + assertNull(target.credential.getClientSecret()); + assertNull(target.credential.getClientCertificatePath()); + assertNull(target.credential.getClientCertificatePassword()); + assertNull(target.credential.getUsername()); + assertNull(target.credential.getPassword()); + } + + @Test + void testCopyPropertiesIgnoresNullToObjectWithSameFieldsSetShouldOverrideWithoutNull() { + PasswordlessPropertiesB target = new PasswordlessPropertiesB(); + target.profile.setCloudType(AZURE); + target.profile.setTenantId("profile-tenant-id-B"); + target.profile.setSubscriptionId("profile-sub-id-B"); + target.profile.getEnvironment().setActiveDirectoryEndpoint("aad-endpoint-B"); + target.credential.setClientId("credential-client-id-B"); + target.credential.setClientSecret("credential-client-secret-B"); + target.credential.setClientCertificatePath("credential-client-cert-path-B"); + target.credential.setClientCertificatePassword("credential-client-cert-password-B"); + target.credential.setUsername("credential-username-B"); + target.credential.setPassword("credential-password-B"); + target.credential.setManagedIdentityEnabled(true); + + // assert properties have been set correctly to target + assertEquals(AZURE, target.profile.getCloudType()); + assertEquals("profile-tenant-id-B", target.profile.getTenantId()); + assertEquals("profile-sub-id-B", target.profile.getSubscriptionId()); + assertEquals("aad-endpoint-B", target.profile.getEnvironment().getActiveDirectoryEndpoint()); + assertEquals(AzureEnvironment.AZURE.getActiveDirectoryGraphApiVersion(), target.profile.getEnvironment().getActiveDirectoryGraphApiVersion()); + assertEquals("credential-client-id-B", target.credential.getClientId()); + assertEquals("credential-client-secret-B", target.credential.getClientSecret()); + assertEquals("credential-client-cert-path-B", target.credential.getClientCertificatePath()); + assertEquals("credential-client-cert-password-B", target.credential.getClientCertificatePassword()); + assertEquals("credential-username-B", target.credential.getUsername()); + assertEquals("credential-password-B", target.credential.getPassword()); + + PasswordlessPropertiesB source = new PasswordlessPropertiesB(); + source.profile.setCloudType(AZURE_CHINA); + source.profile.setTenantId("profile-tenant-id-A"); + source.profile.getEnvironment().setActiveDirectoryEndpoint("aad-endpoint-A"); + source.credential.setClientId("credential-client-id-A"); + source.setPasswordlessEnabled(true); + source.setScopes("fake-scopes"); + + AzurePasswordlessPropertiesUtils.copyAzureCommonPropertiesIgnoreNull(source, target); + + // assert properties have been set correctly to target after copy + assertEquals(AZURE_CHINA, target.profile.getCloudType()); + assertEquals("profile-tenant-id-A", target.profile.getTenantId()); + assertEquals("profile-sub-id-B", target.profile.getSubscriptionId()); + assertEquals("aad-endpoint-A", target.profile.getEnvironment().getActiveDirectoryEndpoint()); + assertEquals(AzureEnvironment.AZURE_CHINA.getActiveDirectoryGraphApiVersion(), target.profile.getEnvironment().getActiveDirectoryGraphApiVersion()); + assertEquals("credential-client-id-A", target.credential.getClientId()); + assertEquals("credential-client-secret-B", target.credential.getClientSecret()); + assertEquals("credential-client-cert-path-B", target.credential.getClientCertificatePath()); + assertEquals("credential-client-cert-password-B", target.credential.getClientCertificatePassword()); + assertEquals("credential-username-B", target.credential.getUsername()); + assertEquals("credential-password-B", target.credential.getPassword()); + assertTrue(target.isPasswordlessEnabled()); + assertEquals("fake-scopes", target.getScopes()); + + } + + @Test + void testMergePropertiesObjectWithSameFieldsSetShouldTakeLater() { + AzurePropertiesA defaultProperties = new AzurePropertiesA(); + defaultProperties.client.setApplicationId("client-application-id-B"); + defaultProperties.profile.setCloudType(AZURE); + defaultProperties.profile.setTenantId("profile-tenant-id-B"); + defaultProperties.profile.setSubscriptionId("profile-sub-id-B"); + defaultProperties.profile.getEnvironment().setActiveDirectoryEndpoint("aad-endpoint-B"); + defaultProperties.proxy.setType("proxy-type-B"); + defaultProperties.proxy.setHostname("proxy-hostname-B"); + defaultProperties.proxy.setPort(1234); + defaultProperties.proxy.setUsername("proxy-username-B"); + defaultProperties.proxy.setPassword("proxy-password-B"); + defaultProperties.retry.setMode(FIXED); + defaultProperties.retry.getExponential().setMaxRetries(3); + defaultProperties.retry.getExponential().setBaseDelay(Duration.ofSeconds(4)); + defaultProperties.retry.getExponential().setMaxDelay(Duration.ofSeconds(5)); + defaultProperties.retry.getFixed().setDelay(Duration.ofSeconds(6)); + defaultProperties.retry.getFixed().setMaxRetries(7); + defaultProperties.credential.setClientId("credential-client-id-B"); + defaultProperties.credential.setClientSecret("credential-client-secret-B"); + defaultProperties.credential.setClientCertificatePath("credential-client-cert-path-B"); + defaultProperties.credential.setClientCertificatePassword("credential-client-cert-password-B"); + defaultProperties.credential.setUsername("credential-username-B"); + defaultProperties.credential.setPassword("credential-password-B"); + defaultProperties.credential.setManagedIdentityEnabled(true); + + // assert properties have been set correctly to defaultProperties + assertEquals(AZURE, defaultProperties.profile.getCloudType()); + assertEquals("profile-tenant-id-B", defaultProperties.profile.getTenantId()); + assertEquals("profile-sub-id-B", defaultProperties.profile.getSubscriptionId()); + assertEquals("aad-endpoint-B", defaultProperties.profile.getEnvironment().getActiveDirectoryEndpoint()); + assertEquals(AzureEnvironment.AZURE.getActiveDirectoryGraphApiVersion(), defaultProperties.profile.getEnvironment().getActiveDirectoryGraphApiVersion()); + assertEquals("credential-client-id-B", defaultProperties.credential.getClientId()); + assertEquals("credential-client-secret-B", defaultProperties.credential.getClientSecret()); + assertEquals("credential-client-cert-path-B", defaultProperties.credential.getClientCertificatePath()); + assertEquals("credential-client-cert-password-B", defaultProperties.credential.getClientCertificatePassword()); + assertEquals("credential-username-B", defaultProperties.credential.getUsername()); + assertEquals("credential-password-B", defaultProperties.credential.getPassword()); + + PasswordlessPropertiesB propertiesToOverride = new PasswordlessPropertiesB(); + propertiesToOverride.profile.setCloudType(AZURE_CHINA); + propertiesToOverride.profile.setTenantId("profile-tenant-id-A"); + propertiesToOverride.profile.getEnvironment().setActiveDirectoryEndpoint("aad-endpoint-A"); + propertiesToOverride.credential.setClientId("credential-client-id-A"); + propertiesToOverride.setScopes("fake-scopes-1"); + + PasswordlessPropertiesB result = new PasswordlessPropertiesB(); + AzurePasswordlessPropertiesUtils.mergeAzureCommonProperties(defaultProperties, propertiesToOverride, result); + + // assert properties have been set correctly to result after copy + assertEquals(AZURE_CHINA, result.profile.getCloudType()); + assertEquals("profile-tenant-id-A", result.profile.getTenantId()); + assertEquals("profile-sub-id-B", result.profile.getSubscriptionId()); + assertEquals("aad-endpoint-A", result.profile.getEnvironment().getActiveDirectoryEndpoint()); + assertEquals(AzureEnvironment.AZURE_CHINA.getActiveDirectoryGraphApiVersion(), result.profile.getEnvironment().getActiveDirectoryGraphApiVersion()); + assertEquals("credential-client-id-A", result.credential.getClientId()); + assertEquals("credential-client-secret-B", result.credential.getClientSecret()); + assertEquals("credential-client-cert-path-B", result.credential.getClientCertificatePath()); + assertEquals("credential-client-cert-password-B", result.credential.getClientCertificatePassword()); + assertEquals("credential-username-B", result.credential.getUsername()); + assertEquals("credential-password-B", result.credential.getPassword()); + assertEquals("fake-scopes-1", result.getScopes()); + assertFalse(result.isPasswordlessEnabled()); + } + + @Test + void testCopyPropertiesIgnoreNullToObjectWithDifferentFieldsSetShouldMerge() { + PasswordlessPropertiesB source = new PasswordlessPropertiesB(); + source.credential.setClientId("client-id-A"); + source.getProfile().setCloudType(AZURE); + source.setScopes("fake-scopes"); + source.setPasswordlessEnabled(true); + + PasswordlessPropertiesB target = new PasswordlessPropertiesB(); + target.credential.setClientSecret("client-secret-B"); + target.profile.setCloudType(OTHER); + target.profile.getEnvironment().setActiveDirectoryEndpoint("abc"); + + assertTrue(source.isPasswordlessEnabled()); + assertFalse(target.isPasswordlessEnabled()); + assertEquals("fake-scopes", source.getScopes()); + assertNull(target.getScopes()); + + assertEquals(AZURE, source.getProfile().getCloudType()); + assertEquals(AzureEnvironment.AZURE.getActiveDirectoryEndpoint(), + source.profile.getEnvironment().getActiveDirectoryEndpoint()); + assertEquals("client-secret-B", target.credential.getClientSecret()); + + AzurePasswordlessPropertiesUtils.copyAzureCommonPropertiesIgnoreNull(source, target); + + // target properties should be merged properties from source + target + assertEquals("client-id-A", target.credential.getClientId()); + assertEquals("client-secret-B", target.credential.getClientSecret()); + assertEquals(AZURE, source.getProfile().getCloudType()); + assertEquals(AzureEnvironment.AZURE.getActiveDirectoryEndpoint(), + target.profile.getEnvironment().getActiveDirectoryEndpoint()); + + + // source properties should not be updated + assertNull(source.credential.getClientSecret()); + assertEquals(AzureEnvironment.AZURE.getActiveDirectoryEndpoint(), + source.profile.getEnvironment().getActiveDirectoryEndpoint()); + + assertTrue(source.isPasswordlessEnabled()); + assertTrue(target.isPasswordlessEnabled()); + assertEquals("fake-scopes", source.getScopes()); + assertEquals("fake-scopes", target.getScopes()); + } + + @Test + void testCopyPropertiesSourceNotChanged() { + AzurePropertiesA source = new AzurePropertiesA(); + source.credential.setClientId("client-id-A"); + source.getProfile().setCloudType(AZURE); + + PasswordlessPropertiesB target = new PasswordlessPropertiesB(); + + AzurePasswordlessPropertiesUtils.copyAzureCommonProperties(source, target); + + assertEquals("client-id-A", target.credential.getClientId()); + + // Update target will not affect source + target.profile.setCloudType(OTHER); + target.profile.getEnvironment().setActiveDirectoryEndpoint("abc"); + + assertNull(source.retry.getExponential().getBaseDelay()); + assertEquals(AzureEnvironment.AZURE.getActiveDirectoryEndpoint(), + source.profile.getEnvironment().getActiveDirectoryEndpoint()); + } + + + static class AzurePropertiesA implements AzureProperties, RetryOptionsProvider { + + private final ClientProperties client = new ClientProperties(); + private final ProxyProperties proxy = new ProxyProperties(); + private final RetryProperties retry = new RetryProperties(); + private final TokenCredentialProperties credential = new TokenCredentialProperties(); + private final AzureProfileProperties profile = new AzureProfileProperties(); + + @Override + public ClientProperties getClient() { + return client; + } + + @Override + public ProxyProperties getProxy() { + return proxy; + } + + @Override + public RetryProperties getRetry() { + return retry; + } + + @Override + public TokenCredentialProperties getCredential() { + return credential; + } + + @Override + public AzureProfileProperties getProfile() { + return profile; + } + } + + static class PasswordlessPropertiesB implements PasswordlessProperties { + + private final TokenCredentialProperties credential = new TokenCredentialProperties(); + private final AzureProfileProperties profile = new AzureProfileProperties(); + private String scopes; + private boolean passwordlessEnabled = false; + + @Override + public TokenCredentialProperties getCredential() { + return credential; + } + + @Override + public AzureProfileProperties getProfile() { + return profile; + } + + @Override + public String getScopes() { + return scopes; + } + + @Override + public void setScopes(String scopes) { + this.scopes = scopes; + } + + @Override + public boolean isPasswordlessEnabled() { + return passwordlessEnabled; + } + + @Override + public void setPasswordlessEnabled(boolean passwordlessEnabled) { + this.passwordlessEnabled = passwordlessEnabled; + } + } +} From 99a1c5d8ef0206a56f366df2f223e808fe50b47a Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Wed, 1 Mar 2023 14:55:28 +0800 Subject: [PATCH 23/29] update java doc --- .../implementation/util/AzurePasswordlessPropertiesUtils.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/implementation/util/AzurePasswordlessPropertiesUtils.java b/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/implementation/util/AzurePasswordlessPropertiesUtils.java index dc950c5108454..54df0ceecbe78 100644 --- a/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/implementation/util/AzurePasswordlessPropertiesUtils.java +++ b/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/implementation/util/AzurePasswordlessPropertiesUtils.java @@ -25,7 +25,7 @@ private AzurePasswordlessPropertiesUtils() { } /** - * Copy common properties from source {@link AzureProperties} object to target {@link T} object. + * 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. @@ -42,7 +42,7 @@ public static void copyAzureCommonProperties( } /** - * Copy common properties from source {@link PasswordlessProperties} object to target {@link T} object. Ignore the source + * 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. From 115ec199781167a44582970b52f216d1b605d086 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Thu, 2 Mar 2023 09:40:52 +0800 Subject: [PATCH 24/29] use ArrayList --- .../AzurePasswordlessEnvironmentPostProcessor.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/passwordless/AzurePasswordlessEnvironmentPostProcessor.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/passwordless/AzurePasswordlessEnvironmentPostProcessor.java index 7996a7d40045c..7665cc5099de8 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/passwordless/AzurePasswordlessEnvironmentPostProcessor.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/passwordless/AzurePasswordlessEnvironmentPostProcessor.java @@ -3,7 +3,6 @@ package com.azure.spring.cloud.autoconfigure.passwordless; -import com.azure.cosmos.implementation.guava25.collect.Lists; import org.springframework.boot.SpringApplication; import org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor; import org.springframework.boot.env.EnvironmentPostProcessor; @@ -11,6 +10,7 @@ import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.PropertiesPropertySource; +import java.util.ArrayList; import java.util.List; import java.util.Properties; @@ -29,7 +29,7 @@ public class AzurePasswordlessEnvironmentPostProcessor implements EnvironmentPos @Override public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { Properties properties = new Properties(); - List passwordlessCredentialSupplier = Lists.newArrayList(); + List passwordlessCredentialSupplier = new ArrayList<>(); passwordlessCredentialSupplier.add("azureRedisCredentialSupplier"); passwordlessCredentialSupplier.add("azureServiceBusJmsCredentialSupplier"); properties.setProperty("spring.cloud.function.ineligible-definitions", String.join(",", passwordlessCredentialSupplier)); From ed2386d5eae7e50b703705516a835d5997ae4495 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Thu, 2 Mar 2023 11:51:04 +0800 Subject: [PATCH 25/29] fix jms bug --- .../jms/ServiceBusJmsAutoConfiguration.java | 13 +++++++++++-- .../jms/ServiceBusJmsPasswordlessConfiguration.java | 12 +++--------- 2 files changed, 14 insertions(+), 11 deletions(-) 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 df8efbbc6ce39..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; @@ -51,8 +53,9 @@ public class ServiceBusJmsAutoConfiguration { @Bean - AzureServiceBusJmsProperties serviceBusJmsProperties() { - return new AzureServiceBusJmsProperties(); + AzureServiceBusJmsProperties serviceBusJmsProperties(AzureGlobalProperties azureGlobalProperties) { + AzureServiceBusJmsProperties properties = new AzureServiceBusJmsProperties(); + return mergeAzureProperties(azureGlobalProperties, properties); } @Bean @@ -85,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/ServiceBusJmsPasswordlessConfiguration.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/jms/ServiceBusJmsPasswordlessConfiguration.java index 5d8b7237c9862..537eba723c89f 100644 --- 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 @@ -3,9 +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.implementation.util.AzurePasswordlessPropertiesUtils; import org.apache.qpid.jms.JmsConnectionExtensions; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -24,8 +22,8 @@ class ServiceBusJmsPasswordlessConfiguration { @Bean @ConditionalOnMissingBean - AzureServiceBusJmsCredentialSupplier azureServiceBusJmsCredentialSupplier(AzureGlobalProperties azureGlobalProperties, AzureServiceBusJmsProperties azureServiceBusJmsProperties) { - return new AzureServiceBusJmsCredentialSupplier(mergeAzureProperties(azureGlobalProperties, azureServiceBusJmsProperties).toPasswordlessProperties()); + AzureServiceBusJmsCredentialSupplier azureServiceBusJmsCredentialSupplier(AzureServiceBusJmsProperties azureServiceBusJmsProperties) { + return new AzureServiceBusJmsCredentialSupplier(azureServiceBusJmsProperties.toPasswordlessProperties()); } @Bean @@ -38,9 +36,5 @@ ServiceBusJmsConnectionFactoryCustomizer jmsAADAuthenticationCustomizer(AzureSer }; } - private AzureServiceBusJmsProperties mergeAzureProperties(AzureGlobalProperties azureGlobalProperties, AzureServiceBusJmsProperties azurePasswordlessProperties) { - AzureServiceBusJmsProperties mergedProperties = new AzureServiceBusJmsProperties(); - AzurePasswordlessPropertiesUtils.mergeAzureCommonProperties(azureGlobalProperties, azurePasswordlessProperties, mergedProperties); - return mergedProperties; - } + } From 64bd4a3371e64f0d78ca10a11fc0e4110d566f2d Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Thu, 2 Mar 2023 11:59:19 +0800 Subject: [PATCH 26/29] update azure-sdk-bom version --- sdk/boms/spring-cloud-azure-dependencies/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From ae1bce72677af7225d9b9659377a46cccf517bbc Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Thu, 2 Mar 2023 15:56:12 +0800 Subject: [PATCH 27/29] fix bug of ServiceBusJmsPasswordlessIT --- .../jms/ServiceBusJmsPasswordlessIT.java | 2 +- .../servicebus/test-resources.json | 25 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/sdk/spring/spring-cloud-azure-integration-tests/src/test/java/com/azure/spring/cloud/integration/tests/servicebus/jms/ServiceBusJmsPasswordlessIT.java b/sdk/spring/spring-cloud-azure-integration-tests/src/test/java/com/azure/spring/cloud/integration/tests/servicebus/jms/ServiceBusJmsPasswordlessIT.java index 51d5fc1aca61f..76a5ffdd46514 100644 --- a/sdk/spring/spring-cloud-azure-integration-tests/src/test/java/com/azure/spring/cloud/integration/tests/servicebus/jms/ServiceBusJmsPasswordlessIT.java +++ b/sdk/spring/spring-cloud-azure-integration-tests/src/test/java/com/azure/spring/cloud/integration/tests/servicebus/jms/ServiceBusJmsPasswordlessIT.java @@ -20,7 +20,7 @@ public class ServiceBusJmsPasswordlessIT { private static final Logger LOGGER = LoggerFactory.getLogger(ServiceBusJmsPasswordlessIT.class); private static final String DATA = "service bus jms passwordless test"; - private static final String QUEUE_NAME = "que001"; + private static final String QUEUE_NAME = "passwordless_que001"; private final Exchanger EXCHANGER = new Exchanger<>(); @Autowired diff --git a/sdk/spring/spring-cloud-azure-integration-tests/test-resources/servicebus/test-resources.json b/sdk/spring/spring-cloud-azure-integration-tests/test-resources/servicebus/test-resources.json index 636ef04e1bbf2..fce64f6350827 100644 --- a/sdk/spring/spring-cloud-azure-integration-tests/test-resources/servicebus/test-resources.json +++ b/sdk/spring/spring-cloud-azure-integration-tests/test-resources/servicebus/test-resources.json @@ -84,6 +84,31 @@ "enableExpress": false } }, + { + "type": "Microsoft.ServiceBus/namespaces/queues", + "apiVersion": "2022-01-01-preview", + "name": "[concat(variables('azureServiceBusNamespaceName'), '/passwordless_que001')]", + "location": "[variables('location')]", + "dependsOn": [ + "[resourceId('Microsoft.ServiceBus/namespaces', variables('azureServiceBusNamespaceName'))]" + ], + "properties": { + "maxMessageSizeInKilobytes": 256, + "lockDuration": "PT30S", + "maxSizeInMegabytes": 1024, + "requiresDuplicateDetection": false, + "requiresSession": false, + "defaultMessageTimeToLive": "P14D", + "deadLetteringOnMessageExpiration": false, + "enableBatchedOperations": true, + "duplicateDetectionHistoryTimeWindow": "PT10M", + "maxDeliveryCount": 10, + "status": "Active", + "autoDeleteOnIdle": "P10675199DT2H48M5.4775807S", + "enablePartitioning": false, + "enableExpress": false + } + }, { "type": "Microsoft.ServiceBus/namespaces/queues", "apiVersion": "2022-01-01-preview", From 97a6480b9532e36ae37ae206424db6eacab92136 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Fri, 3 Mar 2023 11:05:42 +0800 Subject: [PATCH 28/29] address some comments --- .../AzurePasswordlessEnvironmentPostProcessor.java | 2 +- .../jdbc/JdbcPropertiesBeanPostProcessor.java | 2 +- .../jms/AzureServiceBusJmsCredentialSupplier.java | 2 +- .../jms/ServiceBusJmsConnectionFactoryFactory.java | 2 +- .../properties/AzureServiceBusJmsProperties.java | 14 +++++++------- .../autoconfigure/passwordless/package-info.java | 7 ------- .../src/main/resources/META-INF/spring.factories | 2 +- 7 files changed, 12 insertions(+), 19 deletions(-) rename sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/{ => implementation}/passwordless/AzurePasswordlessEnvironmentPostProcessor.java (95%) delete mode 100644 sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/passwordless/package-info.java diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/passwordless/AzurePasswordlessEnvironmentPostProcessor.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/passwordless/AzurePasswordlessEnvironmentPostProcessor.java similarity index 95% rename from sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/passwordless/AzurePasswordlessEnvironmentPostProcessor.java rename to sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/passwordless/AzurePasswordlessEnvironmentPostProcessor.java index 7665cc5099de8..fa46fef2af5fb 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/passwordless/AzurePasswordlessEnvironmentPostProcessor.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/passwordless/AzurePasswordlessEnvironmentPostProcessor.java @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -package com.azure.spring.cloud.autoconfigure.passwordless; +package com.azure.spring.cloud.autoconfigure.implementation.passwordless; import org.springframework.boot.SpringApplication; import org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor; 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 19c2a830acf87..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 @@ -134,10 +134,10 @@ private Map buildEnhancedProperties(DatabaseType databaseType, A TokenCredentialProvider tokenCredentialProvider = TokenCredentialProvider.createDefault(new TokenCredentialProviderOptions(properties.toPasswordlessProperties())); TokenCredential tokenCredential = tokenCredentialProvider.get(); - 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); + 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()); 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 index caa19c0645e7e..a54f916913b56 100644 --- 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 @@ -9,7 +9,7 @@ import java.util.function.Supplier; /** - * AzureServiceBusJmsCredentialSupplier that provide a String as the password to connect Azure ServiceBus. + * AzureServiceBusJmsCredentialSupplier that provides a String as the password to connect Azure ServiceBus. * * @since 4.7.0 */ 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 065e04fca2ec7..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 @@ -59,7 +59,7 @@ private T createConnectionFactoryInst T factory; if (properties.isPasswordlessEnabled()) { String remoteUrl = String.format(AMQP_URI_FORMAT, - properties.getNameSpace() + "." + properties.getProfile().getEnvironment().getServiceBusDomainName(), + properties.getNamespace() + "." + properties.getProfile().getEnvironment().getServiceBusDomainName(), properties.getIdleTimeout().toMillis()); factory = factoryClass.getConstructor(String.class).newInstance(remoteUrl); } else { 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 da6f9e3e0c017..2157ece6f2b14 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 @@ -64,7 +64,7 @@ public class AzureServiceBusJmsProperties implements InitializingBean, Passwordl /** * The Service Bus namespace. */ - private String nameSpace; + private String namespace; /** * Connection string to connect to a Service Bus namespace. @@ -200,16 +200,16 @@ public PrefetchPolicy getPrefetchPolicy() { * Get the Service Bus namespace. * @return the Service Bus namespace. */ - public String getNameSpace() { - return nameSpace; + public String getNamespace() { + return namespace; } /** * Set the Service Bus namespace. - * @param nameSpace the Service Bus namespace. + * @param namespace the Service Bus namespace. */ - public void setNameSpace(String nameSpace) { - this.nameSpace = nameSpace; + public void setNamespace(String namespace) { + this.namespace = namespace; } /** @@ -295,7 +295,7 @@ public void setCredential(TokenCredentialConfigurationProperties credential) { @Override public void afterPropertiesSet() throws Exception { if (isPasswordlessEnabled()) { - if (!StringUtils.hasText(nameSpace)) { + if (!StringUtils.hasText(namespace)) { throw new IllegalArgumentException("Passwordless connections enabled, 'spring.jms.servicebus.namespace' should be provided."); } } else { diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/passwordless/package-info.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/passwordless/package-info.java deleted file mode 100644 index 3b891d048b727..0000000000000 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/passwordless/package-info.java +++ /dev/null @@ -1,7 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -/** - * Spring Cloud Azure's EnvironmentPostProcessor for passwordless auto-configurations. - */ -package com.azure.spring.cloud.autoconfigure.passwordless; 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 1adb7c079c093..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 @@ -2,7 +2,7 @@ org.springframework.boot.env.EnvironmentPostProcessor=com.azure.spring.cloud.aut 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.passwordless.AzurePasswordlessEnvironmentPostProcessor +com.azure.spring.cloud.autoconfigure.implementation.passwordless.AzurePasswordlessEnvironmentPostProcessor org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.azure.spring.cloud.autoconfigure.aad.AadAutoConfiguration,\ From db2691e3cc6c0965190eaab9999d2969cf52d648 Mon Sep 17 00:00:00 2001 From: zhihaoguo Date: Fri, 3 Mar 2023 11:35:03 +0800 Subject: [PATCH 29/29] add some java docs --- .../jms/properties/AzureServiceBusJmsProperties.java | 3 +++ .../passwordless/AzureJdbcPasswordlessProperties.java | 6 ++++++ .../passwordless/AzureRedisPasswordlessProperties.java | 5 +++++ 3 files changed, 14 insertions(+) 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 2157ece6f2b14..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 @@ -53,6 +53,9 @@ public class AzureServiceBusJmsProperties implements InitializingBean, Passwordl /** * 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; diff --git a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureJdbcPasswordlessProperties.java b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureJdbcPasswordlessProperties.java index cd431606f4797..689b13a25c0af 100644 --- a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureJdbcPasswordlessProperties.java +++ b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureJdbcPasswordlessProperties.java @@ -29,6 +29,12 @@ public class AzureJdbcPasswordlessProperties implements PasswordlessProperties { } }; + /** + * Whether to enable supporting azure identity token credentials, by default is false. + * + * If the passwordlessEnabled is true, but the 'spring.datasource.password' property is not empty, it will still use username/password to authenticate connections. + * To use passwordless connections, you need to remove 'spring.datasource.password' property. + */ private boolean passwordlessEnabled = false; private AzureProfileProperties profile = new AzureProfileProperties(); diff --git a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureRedisPasswordlessProperties.java b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureRedisPasswordlessProperties.java index c4d5facdac0a6..93bfe4680e0ba 100644 --- a/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureRedisPasswordlessProperties.java +++ b/sdk/spring/spring-cloud-azure-service/src/main/java/com/azure/spring/cloud/service/implementation/passwordless/AzureRedisPasswordlessProperties.java @@ -34,6 +34,11 @@ public class AzureRedisPasswordlessProperties implements PasswordlessProperties private String scopes; + /** + * Whether to enable supporting azure identity token credentials, by default is false. + * + * If the passwordlessEnabled is true, but the redis password properties is not null, it will still use username/password to authenticate connections. + */ private boolean passwordlessEnabled = false; private TokenCredentialProperties credential = new TokenCredentialProperties();