Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bridge azure properties to spring starter to authenticate #5

Closed
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.spring;

import com.azure.core.credential.TokenCredential;
import com.azure.spring.autoconfigure.unity.CredentialProperties;
import com.azure.spring.autoconfigure.unity.EnvironmentProperties;
import com.azure.spring.identity.CredentialPropertiesProvider;

/**
* An interface to provide all credential related properties based on the properties subclass.
*/
public interface MappingCredentialPropertiesProvider extends CredentialPropertiesProvider {

void mapAzureProperties(CredentialProperties credentialProperties, EnvironmentProperties environment);

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

package com.azure.spring;

import com.azure.core.credential.TokenCredential;
import com.azure.spring.autoconfigure.unity.AzureProperties;
import com.azure.spring.autoconfigure.unity.CredentialProperties;
import com.azure.spring.autoconfigure.unity.EnvironmentProperties;
import com.azure.spring.identity.SpringEnvironmentCredentialBuilder;

import java.util.Optional;

/**
* An implementation to provide all credential related properties based on properties subclass.
*/
public class SpringMappingCredentialPropertiesProvider implements MappingCredentialPropertiesProvider {

CredentialProperties credentialProperties;
EnvironmentProperties environment;

public SpringMappingCredentialPropertiesProvider(AzureProperties azureProperties) {
if (azureProperties != null) {
mapAzureProperties(azureProperties.getCredential(), azureProperties.getEnvironment());
}
}

@Override
public void mapAzureProperties(CredentialProperties credentialProperties,
EnvironmentProperties environment) {
this.credentialProperties = credentialProperties;
this.environment = environment;
}


@Override
public TokenCredential mappingTokenCredential() {
SpringEnvironmentCredentialBuilder mapEnvironmentCredentialBuilder =
new SpringEnvironmentCredentialBuilder().credentialPropertiesProvider(this);
return mapEnvironmentCredentialBuilder.build();
}

@Override
public String getTenantId() {
return Optional.ofNullable(credentialProperties).map(CredentialProperties::getTenantId).orElse(null);
}

@Override
public String getClientId() {
return Optional.ofNullable(credentialProperties).map(CredentialProperties::getClientId).orElse(null);
}

@Override
public String getClientSecret() {
return Optional.ofNullable(credentialProperties).map(CredentialProperties::getClientSecret).orElse(null);
}

@Override
public String getClientCertificatePath() {
return Optional.ofNullable(credentialProperties).map(CredentialProperties::getCertificatePath).orElse(null);
}

@Override
public String getCertificatePassword() {
return Optional.ofNullable(credentialProperties).map(CredentialProperties::getCertificatePassword).orElse(null);
}

@Override
public String getUsername() {
return null;
}

@Override
public String getPassword() {
return null;
}

@Override
public String getAuthorityHost() {
return Optional.ofNullable(environment).map(EnvironmentProperties::getAuthorityHost).orElse(null);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,34 @@
package com.azure.spring.autoconfigure.cosmos;

import com.azure.core.credential.AzureKeyCredential;
import com.azure.core.credential.TokenCredential;
import com.azure.cosmos.ConnectionMode;
import com.azure.cosmos.CosmosAsyncClient;
import com.azure.cosmos.CosmosClientBuilder;
import com.azure.spring.autoconfigure.unity.AzureProperties;
import com.azure.identity.ChainedTokenCredential;
import com.azure.identity.ChainedTokenCredentialBuilder;
import com.azure.spring.SpringMappingCredentialPropertiesProvider;
import com.azure.spring.autoconfigure.unity.identity.AzureDefaultTokenCredentialAutoConfiguration;
import com.azure.spring.data.cosmos.config.AbstractCosmosConfiguration;
import com.azure.spring.data.cosmos.config.CosmosConfig;
import com.azure.spring.data.cosmos.core.CosmosTemplate;
import com.azure.spring.identity.AzureKeyCredentialClientBuilderCustomizer;
import com.azure.spring.identity.TokenCredentialClientBuilderCustomizer;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnResource;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.StringUtils;

import static com.azure.spring.autoconfigure.unity.AzureProperties.AZURE_PROPERTY_BEAN_NAME;
import java.util.Optional;

import static com.azure.spring.autoconfigure.unity.identity.AzureDefaultTokenCredentialAutoConfiguration.DEFAULT_CHAINED_TOKEN_CREDENTIAL_BEAN_NAME;

/**
* Auto Configure Cosmos properties and connection policy.
Expand All @@ -27,44 +40,96 @@
@ConditionalOnClass({ CosmosAsyncClient.class, CosmosTemplate.class })
@ConditionalOnResource(resources = "classpath:cosmos.enable.config")
@EnableConfigurationProperties(CosmosProperties.class)
@AutoConfigureAfter(AzureDefaultTokenCredentialAutoConfiguration.class)
public class CosmosAutoConfiguration extends AbstractCosmosConfiguration {
private final CosmosProperties cosmosProperties;
private final AzureProperties azureProperties;

private final CosmosProperties properties;
private static final String COSMOS_CHAINED_TOKEN_CREDENTIAL_BEAN_NAME = "cosmosChainedTokenCredential";
private static final String COSMOS_AZURE_KEY_CREDENTIAL_BEAN_NAME = "cosmosAzureKeyCredential";

public CosmosAutoConfiguration(CosmosProperties cosmosProperties,
@Qualifier(AZURE_PROPERTY_BEAN_NAME) AzureProperties azureProperties) {
this.cosmosProperties = cosmosProperties;
this.azureProperties = azureProperties;
public CosmosAutoConfiguration(CosmosProperties properties) {
this.properties = properties;
}

@Override
protected String getDatabaseName() {
return cosmosProperties.getDatabase();
return properties.getDatabase();
}

@Bean(COSMOS_AZURE_KEY_CREDENTIAL_BEAN_NAME)
@ConditionalOnMissingBean(name = COSMOS_AZURE_KEY_CREDENTIAL_BEAN_NAME)
public AzureKeyCredential cosmosAzureKeyCredential() {
return Optional.ofNullable(properties.getKey())
.filter(StringUtils::hasText)
.map(AzureKeyCredential::new)
.orElse(null);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

null?

}

@Bean(COSMOS_CHAINED_TOKEN_CREDENTIAL_BEAN_NAME)
@ConditionalOnMissingBean(name = COSMOS_CHAINED_TOKEN_CREDENTIAL_BEAN_NAME)
public ChainedTokenCredential cosmosChainedTokenCredential(
@Qualifier(DEFAULT_CHAINED_TOKEN_CREDENTIAL_BEAN_NAME) TokenCredential defaultTokenCredential) {
SpringMappingCredentialPropertiesProvider provider = new SpringMappingCredentialPropertiesProvider(properties);
final ChainedTokenCredentialBuilder chainedTokenCredentialBuilder = new ChainedTokenCredentialBuilder();
chainedTokenCredentialBuilder.addLast(provider.mappingTokenCredential())
.addLast(defaultTokenCredential);
return chainedTokenCredentialBuilder.build();
}

@Bean
@ConditionalOnMissingBean
public AzureKeyCredentialClientBuilderCustomizer<CosmosClientBuilder> azureKeyCredentialCustomizer(
@Autowired(required = false) @Qualifier(COSMOS_AZURE_KEY_CREDENTIAL_BEAN_NAME) AzureKeyCredential cosmosAzureKeyCredential) {
if (cosmosAzureKeyCredential != null) {
return builder -> builder.credential(cosmosAzureKeyCredential);
}
return null;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should discuss this design, I'm really not sure if it is the right way to do this

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

btw, you can use ObjectProvider to get rid of this @Autowired(required=false)

}

@Bean
@ConditionalOnMissingBean
public TokenCredentialClientBuilderCustomizer<CosmosClientBuilder> tokenCredentialCustomizer(
@Qualifier(COSMOS_CHAINED_TOKEN_CREDENTIAL_BEAN_NAME) ChainedTokenCredential cosmosChainedTokenCredential) {
return builder -> builder.credential(cosmosChainedTokenCredential);
}

/**
* Cosmos client builder configurer
* @param azureKeyCredentialCustomizers Customize key credential
* @param tokenCredentialCustomizers Customize token credential.
* @return Cosmos client builder configurer
*/
@Bean
public AzureKeyCredential azureKeyCredential() {
return new AzureKeyCredential(cosmosProperties.getKey());
public CosmosClientBuilderConfigurer cosmosClientBuilderConfigurer(
ObjectProvider<AzureKeyCredentialClientBuilderCustomizer<CosmosClientBuilder>> azureKeyCredentialCustomizers,
ObjectProvider<TokenCredentialClientBuilderCustomizer<CosmosClientBuilder>> tokenCredentialCustomizers) {
CosmosClientBuilderConfigurer configurer = new CosmosClientBuilderConfigurer();
configurer.setAzureKeyCredentialCustomizer(azureKeyCredentialCustomizers.orderedStream().findFirst().orElse(null));
configurer.setTokenCredentialCustomizer(tokenCredentialCustomizers.orderedStream().findFirst().orElse(null));
return configurer;
}

/**
* Create default CosmosClientBuilder
* @param cosmosClientBuilderConfigurer Cosmos client builder configurer bean.
* @return Default CosmosClientBuilder
*/
@Bean
public CosmosClientBuilder cosmosClientBuilder(AzureKeyCredential azureKeyCredential) {
CosmosClientBuilder cosmosClientBuilder = new CosmosClientBuilder();
cosmosClientBuilder.credential(azureKeyCredential)
.consistencyLevel(cosmosProperties.getConsistencyLevel())
.endpoint(cosmosProperties.getUri());
if (ConnectionMode.GATEWAY == cosmosProperties.getConnectionMode()) {
cosmosClientBuilder.gatewayMode();
@ConditionalOnMissingBean
public CosmosClientBuilder cosmosClientBuilderCustomizer(CosmosClientBuilderConfigurer cosmosClientBuilderConfigurer) {
CosmosClientBuilder builder = new CosmosClientBuilder();
builder.consistencyLevel(properties.getConsistencyLevel())
.endpoint(properties.getUri());
if (ConnectionMode.GATEWAY == properties.getConnectionMode()) {
builder.gatewayMode();
}
return cosmosClientBuilder;
return cosmosClientBuilderConfigurer.configure(builder);
}

@Override
public CosmosConfig cosmosConfig() {
return CosmosConfig.builder()
.enableQueryMetrics(cosmosProperties.isPopulateQueryMetrics())
.responseDiagnosticsProcessor(cosmosProperties.getResponseDiagnosticsProcessor())
.enableQueryMetrics(properties.isPopulateQueryMetrics())
.responseDiagnosticsProcessor(properties.getResponseDiagnosticsProcessor())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.spring.autoconfigure.cosmos;

import com.azure.cosmos.CosmosClientBuilder;
import com.azure.spring.identity.AbstractClientBuilderConfigurer;
import com.azure.spring.identity.AzureKeyCredentialClientBuilderCustomizer;

/**
* Configurer for extending Azure Cosmos service client builder configuration.
*/
public class CosmosClientBuilderConfigurer extends AbstractClientBuilderConfigurer<CosmosClientBuilder> {

private AzureKeyCredentialClientBuilderCustomizer<CosmosClientBuilder> azureKeyCredentialCustomizer;

public void setAzureKeyCredentialCustomizer(AzureKeyCredentialClientBuilderCustomizer<CosmosClientBuilder> azureKeyCredentialCustomizer) {
this.azureKeyCredentialCustomizer = azureKeyCredentialCustomizer;
}

@Override
public CosmosClientBuilder configure(CosmosClientBuilder builder) {
super.configure(builder);
if (azureKeyCredentialCustomizer != null) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about omitting the DefaultSkipCredentialCallback and setting the credential by precedence, first setting the lower precedence and higher precedence.

azureKeyCredentialCustomizer.keyCredential(builder);
}
return builder;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ public class CosmosProperties extends AzureProperties {
/**
* Document DB key.
*/
@NotEmpty
private String key;

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,16 @@
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

import static com.azure.spring.autoconfigure.cosmos.CosmosProperties.PREFIX;

/**
* Import {@link CosmosRepositoriesAutoConfigureRegistrar} class as a Bean in Spring.
*/
@Configuration
@ConditionalOnClass({ CosmosRepository.class })
@ConditionalOnMissingBean({ CosmosRepositoryFactoryBean.class,
CosmosRepositoryConfigurationExtension.class })
@ConditionalOnProperty(prefix = "azure.cosmos.repositories",
@ConditionalOnProperty(prefix = PREFIX + ".repositories",
name = "enabled",
havingValue = "true",
matchIfMissing = true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ public class AzureServiceBusJMSProperties extends AzureProperties {

private String pricingTier;

public AzureServiceBusJMSProperties() {
super();
this.pricingTier = null;
}

public String getConnectionString() {
return connectionString;
}
Expand Down
Loading