From 7237905ad8dadc1dd4d125719da526e53555b2bf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 3 Apr 2026 06:55:59 +0000 Subject: [PATCH 1/3] Initial plan From 6ccc847d25617dd6467d4c8667411c85f9d42a86 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 3 Apr 2026 07:39:01 +0000 Subject: [PATCH 2/3] Fix azure.scopes wrong value when cloud-type=azure_china or azure_us_government Agent-Logs-Url: https://github.com/Azure/azure-sdk-for-java/sessions/5f56cd61-af29-4585-a48b-d7bc109977a6 Co-authored-by: rujche <171773178+rujche@users.noreply.github.com> --- .../CHANGELOG.md | 2 + .../AzureJdbcPasswordlessProperties.java | 36 +++++++++++++++- .../JdbcPropertiesBeanPostProcessorTest.java | 30 +++++++++++++- .../MergeAzureCommonPropertiesTest.java | 41 +++++++++++++++++++ .../spring-cloud-azure-core/CHANGELOG.md | 2 + .../AzurePasswordlessPropertiesUtils.java | 4 +- 6 files changed, 112 insertions(+), 3 deletions(-) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/CHANGELOG.md b/sdk/spring/spring-cloud-azure-autoconfigure/CHANGELOG.md index 68068a92722e..e766e36a38f1 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/CHANGELOG.md +++ b/sdk/spring/spring-cloud-azure-autoconfigure/CHANGELOG.md @@ -8,6 +8,8 @@ ### Bugs Fixed +- Fixed `azure.scopes` using wrong default value for Azure China and Azure US Government when `spring.cloud.azure.profile.cloud-type` is set to `azure_china` or `azure_us_government`. The scopes are now correctly derived from the merged cloud type. ([#44945](https://github.com/Azure/azure-sdk-for-java/issues/44945)) + ### Other Changes ## 7.1.0 (2026-03-11) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/passwordless/properties/AzureJdbcPasswordlessProperties.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/passwordless/properties/AzureJdbcPasswordlessProperties.java index f9951793aad1..1297ffbe9f45 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/passwordless/properties/AzureJdbcPasswordlessProperties.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/passwordless/properties/AzureJdbcPasswordlessProperties.java @@ -3,12 +3,14 @@ package com.azure.spring.cloud.autoconfigure.implementation.passwordless.properties; +import com.azure.spring.cloud.core.implementation.properties.AzurePasswordlessPropertiesMapping; 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; +import java.util.Properties; /** * Configuration properties for passwordless connections with Azure Database. @@ -43,11 +45,22 @@ public class AzureJdbcPasswordlessProperties implements PasswordlessProperties { /** * Get the scopes required for the access token. + * Returns null if scopes have not been explicitly set, so that the default + * scopes can be computed from the merged cloud type after property merging. * - * @return scopes required for the access token + * @return scopes required for the access token, or null if not explicitly set */ @Override public String getScopes() { + return this.scopes; + } + + /** + * Get the effective scopes, returning default cloud-specific scopes when not explicitly set. + * + * @return scopes required for the access token + */ + public String getEffectiveScopes() { return this.scopes == null ? getDefaultScopes() : this.scopes; } @@ -120,4 +133,25 @@ public TokenCredentialProperties getCredential() { public void setCredential(TokenCredentialProperties credential) { this.credential = credential; } + + /** + * Convert {@link AzureJdbcPasswordlessProperties} to {@link Properties}. + * Uses the effective scopes (cloud-type-aware) rather than the raw scopes value, + * ensuring the correct default scope is used when scopes have not been explicitly set. + * + * @return converted {@link Properties} instance + */ + @Override + public Properties toPasswordlessProperties() { + Properties properties = new Properties(); + for (AzurePasswordlessPropertiesMapping m : AzurePasswordlessPropertiesMapping.values()) { + String value = m == AzurePasswordlessPropertiesMapping.SCOPES + ? getEffectiveScopes() + : m.getGetter().apply(this); + if (value != null) { + m.getSetter().accept(properties, value); + } + } + return properties; + } } diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/jdbc/JdbcPropertiesBeanPostProcessorTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/jdbc/JdbcPropertiesBeanPostProcessorTest.java index f32493eba87a..385299aac131 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/jdbc/JdbcPropertiesBeanPostProcessorTest.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/jdbc/JdbcPropertiesBeanPostProcessorTest.java @@ -39,11 +39,14 @@ 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/"; + private static final String CHINA_AUTHORITY_HOST_STRING = AuthProperty.AUTHORITY_HOST.getPropertyKey() + "=" + "https://login.chinacloudapi.cn/"; public static final String PUBLIC_TOKEN_CREDENTIAL_BEAN_NAME_STRING = AuthProperty.TOKEN_CREDENTIAL_BEAN_NAME.getPropertyKey() + "="; private static final String POSTGRESQL_ASSUME_MIN_SERVER_VERSION = POSTGRESQL_PROPERTY_NAME_ASSUME_MIN_SERVER_VERSION + "=" + POSTGRESQL_PROPERTY_VALUE_ASSUME_MIN_SERVER_VERSION; protected static final String MANAGED_IDENTITY_ENABLED_DEFAULT = "azure.managedIdentityEnabled=false"; protected static final String SCOPES_DEFAULT = "azure.scopes=https://ossrdbms-aad.database.windows.net/.default"; + private static final String SCOPES_CHINA = "azure.scopes=https://ossrdbms-aad.database.chinacloudapi.cn/.default"; + private static final String SCOPES_US_GOVERNMENT = "azure.scopes=https://ossrdbms-aad.database.usgovcloudapi.net/.default"; private static final String DEFAULT_PASSWORDLESS_PROPERTIES_SUFFIX = ".spring.datasource.azure"; private MockEnvironment mockEnvironment; @@ -153,7 +156,7 @@ void shouldGetCloudTypeFromAzureUsGov() { DatabaseType.MYSQL, MYSQL_CONNECTION_STRING, MANAGED_IDENTITY_ENABLED_DEFAULT, - SCOPES_DEFAULT, + SCOPES_US_GOVERNMENT, MYSQL_USER_AGENT, US_AUTHORITY_HOST_STRING ); @@ -161,6 +164,31 @@ void shouldGetCloudTypeFromAzureUsGov() { assertEquals(expectedJdbcUrl, dataSourceProperties.getUrl()); } + @Test + void shouldGetCorrectScopeFromAzureChina() { + AzureProfileConfigurationProperties azureProfileConfigurationProperties = new AzureProfileConfigurationProperties(); + azureProfileConfigurationProperties.setCloudType(AzureProfileOptionsProvider.CloudType.AZURE_CHINA); + when(this.azureGlobalProperties.getProfile()).thenReturn(azureProfileConfigurationProperties); + + DataSourceProperties dataSourceProperties = new DataSourceProperties(); + dataSourceProperties.setUrl(POSTGRESQL_CONNECTION_STRING); + + this.mockEnvironment.setProperty("spring.datasource.azure.passwordless-enabled", "true"); + this.jdbcPropertiesBeanPostProcessor.postProcessBeforeInitialization(dataSourceProperties, "dataSourceProperties"); + + String expectedJdbcUrl = enhanceJdbcUrl( + DatabaseType.POSTGRESQL, + POSTGRESQL_CONNECTION_STRING, + MANAGED_IDENTITY_ENABLED_DEFAULT, + SCOPES_CHINA, + APPLICATION_NAME.getName() + "=" + AzureSpringIdentifier.AZURE_SPRING_POSTGRESQL_OAUTH, + POSTGRESQL_ASSUME_MIN_SERVER_VERSION, + CHINA_AUTHORITY_HOST_STRING + ); + + assertEquals(expectedJdbcUrl, dataSourceProperties.getUrl()); + } + @Test void mySqlUserAgentShouldConfigureIfConnectionAttributesIsEmpty() { DataSourceProperties dataSourceProperties = new DataSourceProperties(); diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/passwordless/MergeAzureCommonPropertiesTest.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/passwordless/MergeAzureCommonPropertiesTest.java index 56a9b5666059..edecabfbb183 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/passwordless/MergeAzureCommonPropertiesTest.java +++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/implementation/passwordless/MergeAzureCommonPropertiesTest.java @@ -5,11 +5,13 @@ import com.azure.spring.cloud.autoconfigure.implementation.context.properties.AzureGlobalProperties; import com.azure.spring.cloud.autoconfigure.implementation.jms.properties.AzureServiceBusJmsProperties; +import com.azure.spring.cloud.autoconfigure.implementation.passwordless.properties.AzureJdbcPasswordlessProperties; import com.azure.spring.cloud.core.implementation.util.AzurePasswordlessPropertiesUtils; import com.azure.spring.cloud.core.provider.AzureProfileOptionsProvider; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; class MergeAzureCommonPropertiesTest { @@ -116,4 +118,43 @@ void testGetPropertiesFromGlobalAndPasswordlessProperties() { assertEquals("sub", result.getProfile().getSubscriptionId()); assertEquals("global-tenant-id", result.getProfile().getTenantId()); } + + @Test + void testJdbcPropertiesGetCorrectScopeFromGlobalCloudType() { + AzureGlobalProperties globalProperties = new AzureGlobalProperties(); + globalProperties.getProfile().setCloudType(AzureProfileOptionsProvider.CloudType.AZURE_CHINA); + + AzureJdbcPasswordlessProperties jdbcProperties = new AzureJdbcPasswordlessProperties(); + // User has not explicitly set scopes + + AzureJdbcPasswordlessProperties result = new AzureJdbcPasswordlessProperties(); + AzurePasswordlessPropertiesUtils.mergeAzureCommonProperties(globalProperties, jdbcProperties, result); + + // scopes field should be null (not explicitly set) + assertNull(result.getScopes()); + // effective scopes should use the merged cloud type (AZURE_CHINA) + assertEquals("https://ossrdbms-aad.database.chinacloudapi.cn/.default", result.getEffectiveScopes()); + // toPasswordlessProperties should include the correct cloud-type-aware scope + assertEquals("https://ossrdbms-aad.database.chinacloudapi.cn/.default", + result.toPasswordlessProperties().getProperty("azure.scopes")); + assertEquals(AzureProfileOptionsProvider.CloudType.AZURE_CHINA, result.getProfile().getCloudType()); + } + + @Test + void testJdbcPropertiesExplicitScopesOverridesDefault() { + AzureGlobalProperties globalProperties = new AzureGlobalProperties(); + globalProperties.getProfile().setCloudType(AzureProfileOptionsProvider.CloudType.AZURE_CHINA); + + AzureJdbcPasswordlessProperties jdbcProperties = new AzureJdbcPasswordlessProperties(); + jdbcProperties.setScopes("https://custom-scope/.default"); + + AzureJdbcPasswordlessProperties result = new AzureJdbcPasswordlessProperties(); + AzurePasswordlessPropertiesUtils.mergeAzureCommonProperties(globalProperties, jdbcProperties, result); + + // Explicit scopes should be preserved + assertEquals("https://custom-scope/.default", result.getScopes()); + assertEquals("https://custom-scope/.default", result.getEffectiveScopes()); + assertEquals("https://custom-scope/.default", + result.toPasswordlessProperties().getProperty("azure.scopes")); + } } diff --git a/sdk/spring/spring-cloud-azure-core/CHANGELOG.md b/sdk/spring/spring-cloud-azure-core/CHANGELOG.md index 68068a92722e..8c79fe46f0d5 100644 --- a/sdk/spring/spring-cloud-azure-core/CHANGELOG.md +++ b/sdk/spring/spring-cloud-azure-core/CHANGELOG.md @@ -8,6 +8,8 @@ ### Bugs Fixed +- Fixed `copyAzureCommonPropertiesIgnoreNull` to respect "ignore null" semantics for `scopes` property, preventing incorrect scope overwriting during property merging. + ### Other Changes ## 7.1.0 (2026-03-11) 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 6af576a029d8..43896433dbce 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 @@ -51,7 +51,9 @@ public static void copyAzureCommonPropertiesI copyPropertiesIgnoreNull(source.getProfile().getEnvironment(), target.getProfile().getEnvironment()); copyPropertiesIgnoreNull(source.getCredential(), target.getCredential()); - target.setScopes(source.getScopes()); + if (source.getScopes() != null) { + target.setScopes(source.getScopes()); + } target.setPasswordlessEnabled(source.isPasswordlessEnabled()); } From f56b6ba434f1af5a8cb83a191fe0afaa6ae040fc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 3 Apr 2026 08:01:59 +0000 Subject: [PATCH 3/3] Fix issue number in CHANGELOG to #47096 Agent-Logs-Url: https://github.com/Azure/azure-sdk-for-java/sessions/2f82f4f9-ea2e-46a7-a2fc-062b403c19bd Co-authored-by: rujche <171773178+rujche@users.noreply.github.com> --- sdk/spring/spring-cloud-azure-autoconfigure/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/CHANGELOG.md b/sdk/spring/spring-cloud-azure-autoconfigure/CHANGELOG.md index e766e36a38f1..09f28728e3ea 100644 --- a/sdk/spring/spring-cloud-azure-autoconfigure/CHANGELOG.md +++ b/sdk/spring/spring-cloud-azure-autoconfigure/CHANGELOG.md @@ -8,7 +8,7 @@ ### Bugs Fixed -- Fixed `azure.scopes` using wrong default value for Azure China and Azure US Government when `spring.cloud.azure.profile.cloud-type` is set to `azure_china` or `azure_us_government`. The scopes are now correctly derived from the merged cloud type. ([#44945](https://github.com/Azure/azure-sdk-for-java/issues/44945)) +- Fixed `azure.scopes` using wrong default value for Azure China and Azure US Government when `spring.cloud.azure.profile.cloud-type` is set to `azure_china` or `azure_us_government`. The scopes are now correctly derived from the merged cloud type. ([#47096](https://github.com/Azure/azure-sdk-for-java/issues/47096)) ### Other Changes