-
Notifications
You must be signed in to change notification settings - Fork 2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Make the credential resolver configurable in Spring Cloud Azure (#26792)
* make the credential resolver configurable * get rid of the credential provider * stop using Assertions.assertInstanceOf to make sure tests can work in Spring Boot 2.5.x
- Loading branch information
Showing
69 changed files
with
1,577 additions
and
658 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
97 changes: 0 additions & 97 deletions
97
...zure/spring/cloud/autoconfigure/context/AzureDefaultTokenCredentialAutoConfiguration.java
This file was deleted.
Oops, something went wrong.
224 changes: 224 additions & 0 deletions
224
...a/com/azure/spring/cloud/autoconfigure/context/AzureTokenCredentialAutoConfiguration.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,224 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
package com.azure.spring.cloud.autoconfigure.context; | ||
|
||
import com.azure.core.credential.TokenCredential; | ||
import com.azure.identity.ClientCertificateCredentialBuilder; | ||
import com.azure.identity.ClientSecretCredentialBuilder; | ||
import com.azure.identity.DefaultAzureCredentialBuilder; | ||
import com.azure.identity.ManagedIdentityCredentialBuilder; | ||
import com.azure.identity.UsernamePasswordCredentialBuilder; | ||
import com.azure.spring.cloud.autoconfigure.AzureServiceConfigurationBase; | ||
import com.azure.spring.cloud.autoconfigure.properties.AzureGlobalProperties; | ||
import com.azure.spring.cloud.autoconfigure.properties.core.AbstractAzureHttpConfigurationProperties; | ||
import com.azure.spring.core.aware.authentication.TokenCredentialAware; | ||
import com.azure.spring.core.customizer.AzureServiceClientBuilderCustomizer; | ||
import com.azure.spring.core.factory.AbstractAzureServiceClientBuilderFactory; | ||
import com.azure.spring.core.implementation.credential.resolver.AzureTokenCredentialResolver; | ||
import com.azure.spring.core.implementation.factory.credential.AbstractAzureCredentialBuilderFactory; | ||
import com.azure.spring.core.implementation.factory.credential.ClientCertificateCredentialBuilderFactory; | ||
import com.azure.spring.core.implementation.factory.credential.ClientSecretCredentialBuilderFactory; | ||
import com.azure.spring.core.implementation.factory.credential.DefaultAzureCredentialBuilderFactory; | ||
import com.azure.spring.core.implementation.factory.credential.ManagedIdentityCredentialBuilderFactory; | ||
import com.azure.spring.core.implementation.factory.credential.UsernamePasswordCredentialBuilderFactory; | ||
import org.springframework.beans.BeansException; | ||
import org.springframework.beans.factory.BeanFactory; | ||
import org.springframework.beans.factory.BeanFactoryAware; | ||
import org.springframework.beans.factory.ObjectProvider; | ||
import org.springframework.beans.factory.config.BeanPostProcessor; | ||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.core.annotation.Order; | ||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; | ||
import org.springframework.util.StringUtils; | ||
|
||
import static com.azure.spring.cloud.autoconfigure.context.AzureContextUtils.DEFAULT_TOKEN_CREDENTIAL_BEAN_NAME; | ||
|
||
/** | ||
* Auto-configuration for Azure Spring default token credential. | ||
*/ | ||
@Configuration(proxyBeanMethods = false) | ||
public class AzureTokenCredentialAutoConfiguration extends AzureServiceConfigurationBase { | ||
|
||
private final IdentityClientProperties identityClientProperties; | ||
|
||
public AzureTokenCredentialAutoConfiguration(AzureGlobalProperties azureGlobalProperties) { | ||
super(azureGlobalProperties); | ||
this.identityClientProperties = loadProperties(azureGlobalProperties, new IdentityClientProperties()); | ||
} | ||
|
||
@ConditionalOnMissingBean(name = DEFAULT_TOKEN_CREDENTIAL_BEAN_NAME) | ||
@Bean(name = DEFAULT_TOKEN_CREDENTIAL_BEAN_NAME) | ||
@Order | ||
TokenCredential tokenCredential(DefaultAzureCredentialBuilderFactory factory) { | ||
return factory.build().build(); | ||
} | ||
|
||
@Bean | ||
@ConditionalOnMissingBean | ||
DefaultAzureCredentialBuilderFactory azureCredentialBuilderFactory( | ||
ObjectProvider<AzureServiceClientBuilderCustomizer<DefaultAzureCredentialBuilder>> customizers, | ||
ObjectProvider<ThreadPoolTaskExecutor> threadPoolTaskExecutors) { | ||
DefaultAzureCredentialBuilderFactory factory = new DefaultAzureCredentialBuilderFactory(identityClientProperties); | ||
|
||
threadPoolTaskExecutors.ifAvailable(tpe -> factory.setExecutorService(tpe.getThreadPoolExecutor())); | ||
customizers.orderedStream().forEach(factory::addBuilderCustomizer); | ||
|
||
return factory; | ||
} | ||
|
||
@Bean | ||
@ConditionalOnMissingBean | ||
AzureTokenCredentialResolver azureTokenCredentialResolver( | ||
ClientSecretCredentialBuilderFactory clientSecretCredentialBuilderFactory, | ||
ClientCertificateCredentialBuilderFactory clientCertificateCredentialBuilderFactory, | ||
UsernamePasswordCredentialBuilderFactory usernamePasswordCredentialBuilderFactory, | ||
ManagedIdentityCredentialBuilderFactory managedIdentityCredentialBuilderFactory) { | ||
|
||
return new AzureTokenCredentialResolver(azureProperties -> { | ||
|
||
if (azureProperties.getCredential() == null) { | ||
return null; | ||
} | ||
|
||
final TokenCredentialAware.TokenCredential properties = azureProperties.getCredential(); | ||
final String tenantId = azureProperties.getProfile().getTenantId(); | ||
final String clientId = properties.getClientId(); | ||
|
||
if (StringUtils.hasText(tenantId)) { | ||
|
||
if (StringUtils.hasText(clientId) && StringUtils.hasText(properties.getClientSecret())) { | ||
return clientSecretCredentialBuilderFactory.build() | ||
.clientId(clientId) | ||
.clientSecret(properties.getClientSecret()) | ||
.tenantId(tenantId) | ||
.build(); | ||
} | ||
|
||
String clientCertificatePath = properties.getClientCertificatePath(); | ||
if (StringUtils.hasText(clientCertificatePath)) { | ||
ClientCertificateCredentialBuilder builder = clientCertificateCredentialBuilderFactory | ||
.build() | ||
.tenantId(tenantId) | ||
.clientId(clientId); | ||
|
||
if (StringUtils.hasText(properties.getClientCertificatePassword())) { | ||
builder.pfxCertificate(clientCertificatePath, properties.getClientCertificatePassword()); | ||
} else { | ||
builder.pemCertificate(clientCertificatePath); | ||
} | ||
|
||
return builder.build(); | ||
} | ||
} | ||
|
||
if (StringUtils.hasText(clientId) && StringUtils.hasText(properties.getUsername()) | ||
&& StringUtils.hasText(properties.getPassword())) { | ||
return usernamePasswordCredentialBuilderFactory.build() | ||
.username(properties.getUsername()) | ||
.password(properties.getPassword()) | ||
.clientId(clientId) | ||
.tenantId(tenantId) | ||
.build(); | ||
} | ||
|
||
if (StringUtils.hasText(properties.getManagedIdentityClientId())) { | ||
return managedIdentityCredentialBuilderFactory.build() | ||
.clientId(properties.getManagedIdentityClientId()) | ||
.build(); | ||
} | ||
return null; | ||
}); | ||
} | ||
|
||
@Bean | ||
@ConditionalOnMissingBean | ||
ClientSecretCredentialBuilderFactory clientSecretCredentialBuilderFactory( | ||
ObjectProvider<ThreadPoolTaskExecutor> threadPoolTaskExecutors, | ||
ObjectProvider<AzureServiceClientBuilderCustomizer<ClientSecretCredentialBuilder>> customizers) { | ||
|
||
ClientSecretCredentialBuilderFactory factory = new ClientSecretCredentialBuilderFactory(identityClientProperties); | ||
|
||
threadPoolTaskExecutors.ifAvailable(tpe -> factory.setExecutorService(tpe.getThreadPoolExecutor())); | ||
customizers.orderedStream().forEach(factory::addBuilderCustomizer); | ||
|
||
return factory; | ||
} | ||
|
||
@Bean | ||
@ConditionalOnMissingBean | ||
ClientCertificateCredentialBuilderFactory clientCertificateCredentialBuilderFactory( | ||
ObjectProvider<ThreadPoolTaskExecutor> threadPoolTaskExecutors, | ||
ObjectProvider<AzureServiceClientBuilderCustomizer<ClientCertificateCredentialBuilder>> customizers) { | ||
|
||
ClientCertificateCredentialBuilderFactory factory = new ClientCertificateCredentialBuilderFactory(identityClientProperties); | ||
|
||
threadPoolTaskExecutors.ifAvailable(tpe -> factory.setExecutorService(tpe.getThreadPoolExecutor())); | ||
customizers.orderedStream().forEach(factory::addBuilderCustomizer); | ||
|
||
return factory; | ||
} | ||
|
||
@Bean | ||
@ConditionalOnMissingBean | ||
ManagedIdentityCredentialBuilderFactory managedIdentityCredentialBuilderFactory( | ||
ObjectProvider<AzureServiceClientBuilderCustomizer<ManagedIdentityCredentialBuilder>> customizers) { | ||
|
||
ManagedIdentityCredentialBuilderFactory factory = new ManagedIdentityCredentialBuilderFactory(identityClientProperties); | ||
|
||
customizers.orderedStream().forEach(factory::addBuilderCustomizer); | ||
|
||
return factory; | ||
} | ||
|
||
@Bean | ||
@ConditionalOnMissingBean | ||
UsernamePasswordCredentialBuilderFactory usernamePasswordCredentialBuilderFactory( | ||
ObjectProvider<AzureServiceClientBuilderCustomizer<UsernamePasswordCredentialBuilder>> customizers) { | ||
|
||
UsernamePasswordCredentialBuilderFactory factory = new UsernamePasswordCredentialBuilderFactory(identityClientProperties); | ||
|
||
customizers.orderedStream().forEach(factory::addBuilderCustomizer); | ||
|
||
return factory; | ||
} | ||
|
||
@Bean | ||
public static AzureServiceClientBuilderFactoryPostProcessor builderFactoryBeanPostProcessor() { | ||
return new AzureServiceClientBuilderFactoryPostProcessor(); | ||
} | ||
|
||
/** | ||
* Apply the default token credential to service client builder factory. | ||
*/ | ||
static class AzureServiceClientBuilderFactoryPostProcessor implements BeanPostProcessor, BeanFactoryAware { | ||
|
||
private BeanFactory beanFactory; | ||
|
||
@Override | ||
@SuppressWarnings("rawtypes") | ||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { | ||
if (bean instanceof AbstractAzureCredentialBuilderFactory) { | ||
return bean; | ||
} | ||
|
||
if (bean instanceof AbstractAzureServiceClientBuilderFactory | ||
&& beanFactory.containsBean(DEFAULT_TOKEN_CREDENTIAL_BEAN_NAME)) { | ||
((AbstractAzureServiceClientBuilderFactory) bean).setDefaultTokenCredential( | ||
(TokenCredential) beanFactory.getBean(DEFAULT_TOKEN_CREDENTIAL_BEAN_NAME)); | ||
} | ||
return bean; | ||
} | ||
|
||
@Override | ||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException { | ||
this.beanFactory = beanFactory; | ||
} | ||
} | ||
|
||
static class IdentityClientProperties extends AbstractAzureHttpConfigurationProperties { | ||
|
||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.