Skip to content

Commit 644f835

Browse files
authored
Fix Passwordless feature does't set the cloud-type correctly and pipeline fail (Azure#31600)
Fix Passwordless feature does't set the cloud-type correctly and pipeline fail
1 parent d87a764 commit 644f835

File tree

23 files changed

+257
-74
lines changed

23 files changed

+257
-74
lines changed

eng/versioning/version_client.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,9 @@ com.azure.tools:azure-sdk-build-tool;1.0.0-beta.1;1.0.0-beta.2
395395
# In the pom, the version update tag after the version should name the unreleased package and the dependency version:
396396
# <!-- {x-version-update;unreleased_com.azure:azure-core;dependency} -->
397397
unreleased_com.azure:azure-identity;1.7.0-beta.2
398+
unreleased_com.azure:azure-identity-providers-core;1.0.0-beta.2
399+
unreleased_com.azure:azure-identity-providers-jdbc-mysql;1.0.0-beta.2
400+
unreleased_com.azure:azure-identity-providers-jdbc-postgresql;1.0.0-beta.2
398401

399402
# Released Beta dependencies: Copy the entry from above, prepend "beta_", remove the current
400403
# version and set the version to the released beta. Released beta dependencies are only valid

sdk/jdbc/azure-identity-providers-core/src/main/java/com/azure/identity/providers/jdbc/implementation/token/AccessTokenResolverOptions.java

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,26 @@
44
package com.azure.identity.providers.jdbc.implementation.token;
55

66

7+
import com.azure.core.util.logging.ClientLogger;
8+
import com.azure.identity.AzureAuthorityHosts;
79
import com.azure.identity.providers.jdbc.implementation.enums.AuthProperty;
8-
10+
import java.util.HashMap;
11+
import java.util.Map;
912
import java.util.Properties;
1013

11-
1214
/**
1315
* Contains details of a request to get a token.
1416
*/
1517
public class AccessTokenResolverOptions {
16-
18+
private static final ClientLogger LOGGER = new ClientLogger(AccessTokenResolverOptions.class);
19+
private static final Map<String, String> OSS_RDBMS_SCOPE_MAP = new HashMap<String, String>() {
20+
{
21+
put(AzureAuthorityHosts.AZURE_PUBLIC_CLOUD, "https://ossrdbms-aad.database.windows.net/.default");
22+
put(AzureAuthorityHosts.AZURE_CHINA, "https://ossrdbms-aad.database.chinacloudapi.cn/.default");
23+
put(AzureAuthorityHosts.AZURE_GERMANY, "https://ossrdbms-aad.database.cloudapi.de/.default");
24+
put(AzureAuthorityHosts.AZURE_GOVERNMENT, "https://ossrdbms-aad.database.usgovcloudapi.net/.default");
25+
}
26+
};
1727
private String claims;
1828
private String tenantId;
1929
private String[] scopes;
@@ -27,7 +37,26 @@ public AccessTokenResolverOptions(Properties properties) {
2737
this.claims = AuthProperty.CLAIMS.get(properties);
2838

2939
String scopeProperty = AuthProperty.SCOPES.get(properties);
30-
this.scopes = scopeProperty == null ? new String[0] : scopeProperty.split(",");
40+
if (scopeProperty == null) {
41+
scopeProperty = getDefaultScope(properties);
42+
}
43+
this.scopes = scopeProperty.split(",");
44+
}
45+
46+
private String getDefaultScope(Properties properties) {
47+
String ossrdbmsScope = OSS_RDBMS_SCOPE_MAP.get(AzureAuthorityHosts.AZURE_PUBLIC_CLOUD);
48+
String authorityHost = AuthProperty.AUTHORITY_HOST.get(properties);
49+
if (AzureAuthorityHosts.AZURE_PUBLIC_CLOUD.startsWith(authorityHost)) {
50+
ossrdbmsScope = OSS_RDBMS_SCOPE_MAP.get(AzureAuthorityHosts.AZURE_PUBLIC_CLOUD);
51+
} else if (AzureAuthorityHosts.AZURE_CHINA.startsWith(authorityHost)) {
52+
ossrdbmsScope = OSS_RDBMS_SCOPE_MAP.get(AzureAuthorityHosts.AZURE_CHINA);
53+
} else if (AzureAuthorityHosts.AZURE_GERMANY.startsWith(authorityHost)) {
54+
ossrdbmsScope = OSS_RDBMS_SCOPE_MAP.get(AzureAuthorityHosts.AZURE_GERMANY);
55+
} else if (AzureAuthorityHosts.AZURE_GOVERNMENT.startsWith(authorityHost)) {
56+
ossrdbmsScope = OSS_RDBMS_SCOPE_MAP.get(AzureAuthorityHosts.AZURE_GOVERNMENT);
57+
}
58+
LOGGER.info("Ossrdbms scope set to {}.", ossrdbmsScope);
59+
return ossrdbmsScope;
3160
}
3261

3362
public String getClaims() {
@@ -47,7 +76,7 @@ public void setTenantId(String tenantId) {
4776
}
4877

4978
public String[] getScopes() {
50-
return scopes.clone();
79+
return scopes == null ? null : scopes.clone();
5180
}
5281

5382
public void setScopes(String[] scopes) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
package com.azure.identity.providers.jdbc.implementation.token;
5+
6+
import com.azure.identity.AzureAuthorityHosts;
7+
import com.azure.identity.providers.jdbc.implementation.enums.AuthProperty;
8+
import org.junit.jupiter.api.Test;
9+
import org.junit.jupiter.params.ParameterizedTest;
10+
import org.junit.jupiter.params.provider.Arguments;
11+
import org.junit.jupiter.params.provider.MethodSource;
12+
13+
import java.util.Properties;
14+
import java.util.stream.Stream;
15+
16+
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
17+
import static org.junit.jupiter.api.Assertions.assertEquals;
18+
import static org.junit.jupiter.api.Assertions.assertNull;
19+
20+
class AccessTokenResolverOptionsTest {
21+
22+
@Test
23+
void testDefaultConstructor() {
24+
AccessTokenResolverOptions accessTokenResolverOptions = new AccessTokenResolverOptions();
25+
assertNull(accessTokenResolverOptions.getClaims());
26+
assertNull(accessTokenResolverOptions.getScopes());
27+
assertNull(accessTokenResolverOptions.getTenantId());
28+
}
29+
30+
@Test
31+
void testConstructorWithProperties() {
32+
Properties properties = new Properties();
33+
properties.setProperty(AuthProperty.TENANT_ID.getPropertyKey(), "fake-tenant-id");
34+
properties.setProperty(AuthProperty.CLAIMS.getPropertyKey(), "fake-claims");
35+
properties.setProperty(AuthProperty.SCOPES.getPropertyKey(), "fake-scopes");
36+
37+
AccessTokenResolverOptions accessTokenResolverOptions = new AccessTokenResolverOptions(properties);
38+
assertEquals("fake-claims", accessTokenResolverOptions.getClaims());
39+
assertArrayEquals(new String[]{"fake-scopes"}, accessTokenResolverOptions.getScopes());
40+
assertEquals("fake-tenant-id", accessTokenResolverOptions.getTenantId());
41+
}
42+
43+
@ParameterizedTest
44+
@MethodSource("provideAuthorityHostScopeMap")
45+
void testDifferentAuthorties(String authorityHost, String scope) {
46+
Properties properties = new Properties();
47+
AuthProperty.AUTHORITY_HOST.setProperty(properties, authorityHost);
48+
AccessTokenResolverOptions accessTokenResolverOptions = new AccessTokenResolverOptions(properties);
49+
assertArrayEquals(new String[]{scope}, accessTokenResolverOptions.getScopes());
50+
}
51+
52+
private static Stream<Arguments> provideAuthorityHostScopeMap() {
53+
return Stream.of(
54+
Arguments.of(null, "https://ossrdbms-aad.database.windows.net/.default"),
55+
Arguments.of("", "https://ossrdbms-aad.database.windows.net/.default"),
56+
Arguments.of(AzureAuthorityHosts.AZURE_PUBLIC_CLOUD, "https://ossrdbms-aad.database.windows.net/.default"),
57+
Arguments.of(AzureAuthorityHosts.AZURE_CHINA, "https://ossrdbms-aad.database.chinacloudapi.cn/.default"),
58+
Arguments.of(AzureAuthorityHosts.AZURE_GERMANY, "https://ossrdbms-aad.database.cloudapi.de/.default"),
59+
Arguments.of(AzureAuthorityHosts.AZURE_GOVERNMENT, "https://ossrdbms-aad.database.usgovcloudapi.net/.default")
60+
);
61+
}
62+
}

sdk/jdbc/azure-identity-providers-jdbc-mysql/src/main/java/com/azure/identity/providers/mysql/AzureIdentityMysqlAuthenticationPlugin.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
package com.azure.identity.providers.mysql;
55

66
import com.azure.core.util.logging.ClientLogger;
7-
import com.azure.identity.providers.jdbc.implementation.enums.AuthProperty;
87
import com.azure.identity.providers.jdbc.implementation.template.AzureAuthenticationTemplate;
98
import com.mysql.cj.callback.MysqlCallbackHandler;
109
import com.mysql.cj.protocol.AuthenticationPlugin;
@@ -20,7 +19,6 @@
2019
*/
2120
public class AzureIdentityMysqlAuthenticationPlugin implements AuthenticationPlugin<NativePacketPayload> {
2221
private static final ClientLogger LOGGER = new ClientLogger(AzureIdentityMysqlAuthenticationPlugin.class);
23-
private static final String OSSRDBMS_SCOPE = "https://ossrdbms-aad.database.windows.net/.default";
2422
private static final String PLUGIN_NAME = "mysql_clear_password";
2523

2624
private final AzureAuthenticationTemplate azureAuthenticationTemplate;
@@ -60,7 +58,6 @@ public String getProtocolPluginName() {
6058
public void init(Protocol<NativePacketPayload> protocol) {
6159
this.protocol = protocol;
6260
Properties properties = protocol.getPropertySet().exposeAsProperties();
63-
AuthProperty.SCOPES.setProperty(properties, OSSRDBMS_SCOPE);
6461
azureAuthenticationTemplate.init(properties);
6562
}
6663

sdk/jdbc/azure-identity-providers-jdbc-mysql/src/test/java/com/azure/identity/providers/mysql/AzureIdentityMysqlAuthenticationPluginTest.java

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
package com.azure.identity.providers.mysql;
55

6-
import com.azure.identity.providers.jdbc.implementation.enums.AuthProperty;
76
import com.azure.identity.providers.jdbc.implementation.template.AzureAuthenticationTemplate;
87
import com.mysql.cj.conf.PropertySet;
98
import com.mysql.cj.protocol.Protocol;
@@ -24,7 +23,6 @@
2423
import static org.mockito.Mockito.when;
2524

2625
class AzureIdentityMysqlAuthenticationPluginTest {
27-
protected static final String OSSRDBMS_SCOPES = "https://ossrdbms-aad.database.windows.net/.default";
2826
private static final String CLEAR_PASSWORD = "mysql_clear_password";
2927

3028
Protocol<NativePacketPayload> protocol;
@@ -52,14 +50,6 @@ void testPluginName() {
5250
assertEquals(CLEAR_PASSWORD, protocolPluginName);
5351
}
5452

55-
@Test
56-
void tokenAudienceShouldConfig() {
57-
AzureAuthenticationTemplate template = new AzureAuthenticationTemplate();
58-
AzureIdentityMysqlAuthenticationPlugin plugin = new AzureIdentityMysqlAuthenticationPlugin(template);
59-
plugin.init(protocol);
60-
assertEquals(OSSRDBMS_SCOPES, properties.getProperty(AuthProperty.SCOPES.getPropertyKey()));
61-
}
62-
6353
@Test
6454
void testRequiresConfidentiality() {
6555
AzureIdentityMysqlAuthenticationPlugin plugin = new AzureIdentityMysqlAuthenticationPlugin();

sdk/jdbc/azure-identity-providers-jdbc-postgresql/src/main/java/com/azure/identity/providers/postgresql/AzureIdentityPostgresqlAuthenticationPlugin.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
package com.azure.identity.providers.postgresql;
55

6-
import com.azure.identity.providers.jdbc.implementation.enums.AuthProperty;
76
import com.azure.identity.providers.jdbc.implementation.template.AzureAuthenticationTemplate;
87
import org.postgresql.plugin.AuthenticationPlugin;
98
import org.postgresql.plugin.AuthenticationRequestType;
@@ -18,8 +17,6 @@
1817
*/
1918
public class AzureIdentityPostgresqlAuthenticationPlugin implements AuthenticationPlugin {
2019

21-
private static final String OSSRDBMS_SCOPE = "https://ossrdbms-aad.database.windows.net/.default";
22-
2320
private final AzureAuthenticationTemplate azureAuthenticationTemplate;
2421

2522
/**
@@ -29,13 +26,11 @@ public class AzureIdentityPostgresqlAuthenticationPlugin implements Authenticati
2926
*/
3027
public AzureIdentityPostgresqlAuthenticationPlugin(Properties properties) {
3128
this.azureAuthenticationTemplate = new AzureAuthenticationTemplate();
32-
AuthProperty.SCOPES.setProperty(properties, OSSRDBMS_SCOPE);
3329
azureAuthenticationTemplate.init(properties);
3430
}
3531

3632
AzureIdentityPostgresqlAuthenticationPlugin(AzureAuthenticationTemplate azureAuthenticationTemplate, Properties properties) {
3733
this.azureAuthenticationTemplate = azureAuthenticationTemplate;
38-
AuthProperty.SCOPES.setProperty(properties, OSSRDBMS_SCOPE);
3934
this.azureAuthenticationTemplate.init(properties);
4035
}
4136

sdk/jdbc/azure-identity-providers-jdbc-postgresql/src/test/java/com/azure/identity/providers/postgresql/AzureIdentityPostgresqlAuthenticationPluginTest.java

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
package com.azure.identity.providers.postgresql;
55

6-
import com.azure.identity.providers.jdbc.implementation.enums.AuthProperty;
76
import com.azure.identity.providers.jdbc.implementation.template.AzureAuthenticationTemplate;
87
import org.junit.jupiter.api.Test;
98
import org.postgresql.plugin.AuthenticationRequestType;
@@ -19,7 +18,6 @@
1918

2019

2120
class AzureIdentityPostgresqlAuthenticationPluginTest {
22-
private static final String OSSRDBMS_SCOPES = "https://ossrdbms-aad.database.windows.net/.default";
2321

2422
@Test
2523
void testTokenCredentialProvider() {
@@ -28,13 +26,6 @@ void testTokenCredentialProvider() {
2826
assertNotNull(plugin.getAzureAuthenticationTemplate());
2927
}
3028

31-
@Test
32-
protected void tokenAudienceShouldConfig() {
33-
Properties properties = new Properties();
34-
new AzureIdentityPostgresqlAuthenticationPlugin(properties);
35-
assertEquals(OSSRDBMS_SCOPES, properties.getProperty(AuthProperty.SCOPES.getPropertyKey()));
36-
}
37-
3829
@Test
3930
void shouldThrowPSQLException() {
4031
Properties properties = new Properties();

sdk/spring/spring-cloud-azure-autoconfigure/pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -281,14 +281,14 @@
281281
<dependency>
282282
<groupId>com.azure</groupId>
283283
<artifactId>azure-identity-providers-jdbc-mysql</artifactId>
284-
<version>1.0.0-beta.1</version> <!-- {x-version-update;com.azure:azure-identity-providers-jdbc-mysql;dependency} -->
284+
<version>1.0.0-beta.2</version> <!-- {x-version-update;unreleased_com.azure:azure-identity-providers-jdbc-mysql;dependency} -->
285285
<optional>true</optional>
286286
</dependency>
287287

288288
<dependency>
289289
<groupId>com.azure</groupId>
290290
<artifactId>azure-identity-providers-jdbc-postgresql</artifactId>
291-
<version>1.0.0-beta.1</version> <!-- {x-version-update;com.azure:azure-identity-providers-jdbc-postgresql;dependency} -->
291+
<version>1.0.0-beta.2</version> <!-- {x-version-update;unreleased_com.azure:azure-identity-providers-jdbc-postgresql;dependency} -->
292292
<optional>true</optional>
293293
</dependency>
294294

sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/jdbc/JdbcPropertiesBeanPostProcessor.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import com.azure.core.credential.TokenCredential;
66
import com.azure.identity.providers.jdbc.implementation.enums.AuthProperty;
7+
import com.azure.spring.cloud.autoconfigure.context.AzureGlobalProperties;
78
import com.azure.spring.cloud.autoconfigure.implementation.jdbc.DatabaseType;
89
import com.azure.spring.cloud.autoconfigure.implementation.jdbc.JdbcConnectionString;
910
import com.azure.spring.cloud.autoconfigure.implementation.jdbc.JdbcConnectionStringEnhancer;
@@ -32,6 +33,7 @@
3233
import static com.azure.spring.cloud.autoconfigure.implementation.jdbc.JdbcPropertyConstants.MYSQL_PROPERTY_CONNECTION_ATTRIBUTES_KV_DELIMITER;
3334
import static com.azure.spring.cloud.autoconfigure.implementation.jdbc.JdbcPropertyConstants.MYSQL_PROPERTY_NAME_CONNECTION_ATTRIBUTES;
3435
import static com.azure.spring.cloud.autoconfigure.implementation.jdbc.JdbcPropertyConstants.POSTGRESQL_PROPERTY_NAME_APPLICATION_NAME;
36+
import static com.azure.spring.cloud.core.implementation.util.AzurePropertiesUtils.copyPropertiesIgnoreTargetNonNull;
3537
import static com.azure.spring.cloud.service.implementation.identity.credential.provider.SpringTokenCredentialProvider.PASSWORDLESS_TOKEN_CREDENTIAL_BEAN_NAME;
3638

3739

@@ -51,9 +53,8 @@ class JdbcPropertiesBeanPostProcessor implements BeanPostProcessor, EnvironmentA
5153
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
5254
if (bean instanceof DataSourceProperties) {
5355
DataSourceProperties dataSourceProperties = (DataSourceProperties) bean;
56+
AzurePasswordlessProperties properties = buildAzureProperties();
5457

55-
AzurePasswordlessProperties properties = Binder.get(environment)
56-
.bindOrCreate(SPRING_CLOUD_AZURE_DATASOURCE_PREFIX, AzurePasswordlessProperties.class);
5758
if (!properties.isPasswordlessEnabled()) {
5859
LOGGER.debug("Feature passwordless authentication is not enabled, skip enhancing jdbc url.");
5960
return bean;
@@ -129,6 +130,7 @@ private Map<String, String> buildEnhancedProperties(DatabaseType databaseType, A
129130
}
130131

131132
AuthProperty.TOKEN_CREDENTIAL_PROVIDER_CLASS_NAME.setProperty(result, SPRING_TOKEN_CREDENTIAL_PROVIDER_CLASS_NAME);
133+
AuthProperty.AUTHORITY_HOST.setProperty(result, properties.getProfile().getEnvironment().getActiveDirectoryEndpoint());
132134

133135
databaseType.setDefaultEnhancedProperties(result);
134136

@@ -144,4 +146,13 @@ public void setEnvironment(Environment environment) {
144146
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
145147
this.applicationContext = (GenericApplicationContext) applicationContext;
146148
}
149+
150+
private AzurePasswordlessProperties buildAzureProperties() {
151+
AzureGlobalProperties azureGlobalProperties = applicationContext.getBean(AzureGlobalProperties.class);
152+
AzurePasswordlessProperties azurePasswordlessProperties = Binder.get(environment)
153+
.bindOrCreate(SPRING_CLOUD_AZURE_DATASOURCE_PREFIX, AzurePasswordlessProperties.class);
154+
copyPropertiesIgnoreTargetNonNull(azureGlobalProperties.getProfile(), azurePasswordlessProperties.getProfile());
155+
copyPropertiesIgnoreTargetNonNull(azureGlobalProperties.getCredential(), azurePasswordlessProperties.getCredential());
156+
return azurePasswordlessProperties;
157+
}
147158
}

sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/jdbc/AbstractAzureJdbcAutoConfigurationTest.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33

44
package com.azure.spring.cloud.autoconfigure.jdbc;
55

6+
import com.azure.identity.providers.jdbc.implementation.enums.AuthProperty;
67
import com.azure.identity.providers.jdbc.implementation.template.AzureAuthenticationTemplate;
7-
import com.azure.spring.cloud.autoconfigure.context.AzureGlobalProperties;
8+
import com.azure.spring.cloud.autoconfigure.context.AzureGlobalPropertiesAutoConfiguration;
89
import com.azure.spring.cloud.autoconfigure.context.AzureTokenCredentialAutoConfiguration;
910
import com.azure.spring.cloud.autoconfigure.implementation.jdbc.SpringTokenCredentialProviderContextProvider;
1011
import org.junit.jupiter.api.Test;
@@ -19,6 +20,8 @@
1920

2021
abstract class AbstractAzureJdbcAutoConfigurationTest {
2122

23+
public static final String PUBLIC_AUTHORITY_HOST_STRING = AuthProperty.AUTHORITY_HOST.getPropertyKey() + "=" + "https://login.microsoftonline.com/";
24+
2225
abstract void pluginNotOnClassPath();
2326
abstract void wrongJdbcUrl();
2427
abstract void enhanceUrlWithDefaultCredential();
@@ -27,8 +30,8 @@ abstract class AbstractAzureJdbcAutoConfigurationTest {
2730
protected final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
2831
.withConfiguration(AutoConfigurations.of(AzureJdbcAutoConfiguration.class,
2932
AzureTokenCredentialAutoConfiguration.class,
30-
DataSourceAutoConfiguration.class,
31-
AzureGlobalProperties.class));
33+
AzureGlobalPropertiesAutoConfiguration.class,
34+
DataSourceAutoConfiguration.class));
3235

3336
@Test
3437
void testEnhanceUrlDefaultCredential() {

0 commit comments

Comments
 (0)