-
Notifications
You must be signed in to change notification settings - Fork 0
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
Changes from 3 commits
94353ba
6ec0e05
0e5ec4c
92f0f85
6fdd070
d45335a
79749ea
548b429
c332fd2
d8e972e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
package com.azure.spring; | ||
|
||
import com.azure.spring.autoconfigure.unity.CredentialProperties; | ||
import com.azure.spring.autoconfigure.unity.EnvironmentProperties; | ||
|
||
import java.util.Optional; | ||
|
||
/** | ||
* An abstract implementation to provide all credential related properties based on properties subclass. | ||
*/ | ||
public abstract class AbstractMappingCredentialPropertiesProvider implements MappingCredentialPropertiesProvider { | ||
|
||
CredentialProperties credentialProperties; | ||
EnvironmentProperties environment; | ||
|
||
@Override | ||
public void mapAzureProperties( | ||
CredentialProperties credentialProperties, | ||
EnvironmentProperties environment) { | ||
this.credentialProperties = credentialProperties; | ||
this.environment = environment; | ||
} | ||
|
||
@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::getClientId).orElse(null); | ||
} | ||
|
||
@Override | ||
public String getClientCertificatePath() { | ||
return Optional.ofNullable(credentialProperties).map(CredentialProperties::getClientId).orElse(null); | ||
} | ||
|
||
@Override | ||
public String getUsername() { | ||
return Optional.ofNullable(credentialProperties).map(CredentialProperties::getClientId).orElse(null); | ||
} | ||
|
||
@Override | ||
public String getPassword() { | ||
return Optional.ofNullable(credentialProperties).map(CredentialProperties::getClientId).orElse(null); | ||
} | ||
|
||
@Override | ||
public String getAuthorityHost() { | ||
return Optional.ofNullable(environment) | ||
.map(EnvironmentProperties::getAuthorityHost).orElse(null); | ||
} | ||
} |
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 |
---|---|---|
|
@@ -4,21 +4,26 @@ | |
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.spring.MappingCredentialPropertiesProvider; | ||
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 org.springframework.beans.factory.annotation.Qualifier; | ||
import org.springframework.beans.factory.ObjectProvider; | ||
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; | ||
|
||
/** | ||
* Auto Configure Cosmos properties and connection policy. | ||
|
@@ -27,15 +32,12 @@ | |
@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; | ||
|
||
|
||
public CosmosAutoConfiguration(CosmosProperties cosmosProperties, | ||
@Qualifier(AZURE_PROPERTY_BEAN_NAME) AzureProperties azureProperties) { | ||
public CosmosAutoConfiguration(CosmosProperties cosmosProperties) { | ||
this.cosmosProperties = cosmosProperties; | ||
this.azureProperties = azureProperties; | ||
} | ||
|
||
@Override | ||
|
@@ -44,20 +46,44 @@ protected String getDatabaseName() { | |
} | ||
|
||
@Bean | ||
@ConditionalOnMissingBean | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we really need this to be a bean? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I mean the source of this key could only be the CosmosProperties. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For @saragluna, we need to check the key rotation logic here. |
||
public AzureKeyCredential azureKeyCredential() { | ||
return new AzureKeyCredential(cosmosProperties.getKey()); | ||
return Optional.ofNullable(cosmosProperties.getKey()) | ||
.filter(StringUtils::hasText) | ||
.map(AzureKeyCredential::new) | ||
.orElse(null); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. null? |
||
} | ||
|
||
@Bean | ||
public CosmosClientBuilder cosmosClientBuilder(AzureKeyCredential azureKeyCredential) { | ||
CosmosClientBuilder cosmosClientBuilder = new CosmosClientBuilder(); | ||
cosmosClientBuilder.credential(azureKeyCredential) | ||
.consistencyLevel(cosmosProperties.getConsistencyLevel()) | ||
.endpoint(cosmosProperties.getUri()); | ||
@ConditionalOnMissingBean | ||
public CosmosClientBuilder cosmosClientBuilder( | ||
ObjectProvider<AzureKeyCredential> azureKeyCredentials, | ||
ObjectProvider<MappingCredentialPropertiesProvider> mappingPropertiesProviders, | ||
ObjectProvider<TokenCredential> defaultTokenCredentials) { | ||
CosmosClientBuilder cosmosClientBuilder = new CosmosClientBuilder() | ||
.consistencyLevel(cosmosProperties.getConsistencyLevel()) | ||
.endpoint(cosmosProperties.getUri()); | ||
if (ConnectionMode.GATEWAY == cosmosProperties.getConnectionMode()) { | ||
cosmosClientBuilder.gatewayMode(); | ||
} | ||
return cosmosClientBuilder; | ||
AzureKeyCredential azureKeyCredential = azureKeyCredentials.getIfAvailable(); | ||
if (azureKeyCredential != null) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What will happen if we set both AzureKeyCredential and TokenCredential in an Azure SDK builder? |
||
return cosmosClientBuilder.credential(azureKeyCredential); | ||
} | ||
|
||
MappingCredentialPropertiesProvider propertiesProvider = mappingPropertiesProviders.orderedStream() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about using this EnvironemtCredential and the Azure Default Token Credential to construct a chained token credential? |
||
.findFirst() | ||
.orElse(null); | ||
if (propertiesProvider != null) { | ||
return cosmosClientBuilder.credential(propertiesProvider.mappingTokenCredential()); | ||
} | ||
|
||
TokenCredential defaultTokenCredential = defaultTokenCredentials.orderedStream().findFirst().orElse(null); | ||
if (defaultTokenCredential != null) { | ||
return cosmosClientBuilder.credential(defaultTokenCredential); | ||
} | ||
|
||
throw new IllegalStateException("Not found any credential properties configured."); | ||
} | ||
|
||
@Override | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,7 @@ | |
import com.azure.cosmos.ConnectionMode; | ||
import com.azure.cosmos.ConsistencyLevel; | ||
import com.azure.spring.autoconfigure.unity.AzureProperties; | ||
import com.azure.spring.autoconfigure.unity.SpringAzureProperties; | ||
import com.azure.spring.data.cosmos.core.ResponseDiagnosticsProcessor; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
@@ -20,7 +21,7 @@ | |
*/ | ||
@Validated | ||
@ConfigurationProperties(CosmosProperties.PREFIX) | ||
public class CosmosProperties extends AzureProperties { | ||
public class CosmosProperties extends AzureProperties implements SpringAzureProperties { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No need of SpringAzureProperties. |
||
|
||
private static final Logger LOGGER = LoggerFactory.getLogger(CosmosProperties.class); | ||
public static final String PREFIX = "spring.cloud.azure.cosmos"; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,13 +3,18 @@ | |
|
||
package com.azure.spring.autoconfigure.storage; | ||
|
||
import com.azure.core.credential.TokenCredential; | ||
import com.azure.core.http.policy.HttpLogOptions; | ||
import com.azure.spring.MappingCredentialPropertiesProvider; | ||
import com.azure.spring.autoconfigure.storage.resource.AzureStorageProtocolResolver; | ||
import com.azure.spring.autoconfigure.unity.AzureProperties; | ||
import com.azure.spring.autoconfigure.unity.identity.AzureDefaultTokenCredentialAutoConfiguration; | ||
import com.azure.spring.identity.SpringEnvironmentCredential; | ||
import com.azure.spring.identity.SpringEnvironmentCredentialBuilder; | ||
import com.azure.storage.blob.BlobServiceClientBuilder; | ||
import com.azure.storage.common.StorageSharedKeyCredential; | ||
import com.azure.storage.file.share.ShareServiceClientBuilder; | ||
import org.springframework.beans.factory.annotation.Qualifier; | ||
import org.springframework.beans.factory.ObjectProvider; | ||
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.ConditionalOnProperty; | ||
|
@@ -18,8 +23,8 @@ | |
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.context.annotation.Import; | ||
import org.springframework.util.StringUtils; | ||
|
||
import static com.azure.spring.autoconfigure.unity.AzureProperties.AZURE_PROPERTY_BEAN_NAME; | ||
import static com.azure.spring.core.ApplicationId.AZURE_SPRING_STORAGE_BLOB; | ||
import static com.azure.spring.core.ApplicationId.AZURE_SPRING_STORAGE_FILES; | ||
import static com.azure.spring.core.ApplicationId.VERSION; | ||
|
@@ -33,27 +38,54 @@ | |
@ConditionalOnClass({ BlobServiceClientBuilder.class, ShareServiceClientBuilder.class }) | ||
@ConditionalOnResource(resources = "classpath:storage.enable.config") | ||
@EnableConfigurationProperties(StorageProperties.class) | ||
@AutoConfigureAfter(AzureDefaultTokenCredentialAutoConfiguration.class) | ||
public class StorageAutoConfiguration { | ||
|
||
private final SpringEnvironmentCredentialBuilder environmentCredentialBuilder; | ||
|
||
private final TokenCredential defaultTokenCredential; | ||
|
||
public StorageAutoConfiguration(SpringEnvironmentCredentialBuilder environmentCredentialBuilder, | ||
TokenCredential defaultTokenCredential) { | ||
this.environmentCredentialBuilder = environmentCredentialBuilder; | ||
this.defaultTokenCredential = defaultTokenCredential; | ||
} | ||
@Bean | ||
@ConditionalOnMissingBean | ||
@ConditionalOnProperty("azure.storage.blob-endpoint") | ||
public BlobServiceClientBuilder blobServiceClientBuilder(StorageProperties storageProperties, @Qualifier( | ||
AZURE_PROPERTY_BEAN_NAME) AzureProperties azureProperties) { | ||
public BlobServiceClientBuilder blobServiceClientBuilder( | ||
StorageProperties storageProperties, | ||
ObjectProvider<MappingCredentialPropertiesProvider> mappingPropertiesProviders, | ||
ObjectProvider<TokenCredential> defaultTokenCredentials) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why is this called defaultTokenCredentials? Will there be multiple default token crdentials? |
||
final String accountName = storageProperties.getAccountName(); | ||
final String accountKey = storageProperties.getAccountKey(); | ||
|
||
return new BlobServiceClientBuilder() | ||
BlobServiceClientBuilder serviceClientBuilder = new BlobServiceClientBuilder() | ||
.endpoint(storageProperties.getBlobEndpoint()) | ||
.credential(new StorageSharedKeyCredential(accountName, accountKey)) | ||
.httpLogOptions(new HttpLogOptions().setApplicationId(AZURE_SPRING_STORAGE_BLOB + VERSION)); | ||
if (StringUtils.hasText(accountName) | ||
&& StringUtils.hasText(accountKey)) { | ||
return serviceClientBuilder.credential(new StorageSharedKeyCredential(accountName, accountKey)); | ||
} | ||
|
||
MappingCredentialPropertiesProvider propertiesProvider = mappingPropertiesProviders.orderedStream() | ||
.findFirst() | ||
.orElse(null); | ||
if (propertiesProvider != null) { | ||
return serviceClientBuilder.credential(propertiesProvider.mappingTokenCredential()); | ||
} | ||
|
||
TokenCredential defaultTokenCredential = defaultTokenCredentials.orderedStream().findFirst().orElse(null); | ||
if (defaultTokenCredential != null) { | ||
return serviceClientBuilder.credential(defaultTokenCredential); | ||
} | ||
|
||
throw new IllegalStateException("Not found any credential properties configured."); | ||
} | ||
|
||
@Bean | ||
@ConditionalOnMissingBean | ||
@ConditionalOnProperty("azure.storage.file-endpoint") | ||
public ShareServiceClientBuilder shareServiceClientBuilder(StorageProperties storageProperties, @Qualifier( | ||
AZURE_PROPERTY_BEAN_NAME) AzureProperties azureProperties) { | ||
public ShareServiceClientBuilder shareServiceClientBuilder(StorageProperties storageProperties) { | ||
final String accountName = storageProperties.getAccountName(); | ||
final String accountKey = storageProperties.getAccountKey(); | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
package com.azure.spring.autoconfigure.unity; | ||
|
||
public interface SpringAzureProperties { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why this empty interface? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Per discussion, maybe we could remove this now. |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,15 +5,19 @@ | |
|
||
import com.azure.core.credential.TokenCredential; | ||
import com.azure.identity.ChainedTokenCredentialBuilder; | ||
import com.azure.spring.MappingCredentialPropertiesProvider; | ||
import com.azure.spring.autoconfigure.unity.AzureProperties; | ||
import com.azure.spring.autoconfigure.unity.SpringAzureProperties; | ||
import com.azure.spring.identity.SpringAzureCliCredentialBuilder; | ||
import com.azure.spring.identity.SpringAzurePowerShellCredentialBuilder; | ||
import com.azure.spring.identity.SpringCredentialBuilderBase; | ||
import com.azure.spring.identity.SpringEnvironmentCredentialBuilder; | ||
import com.azure.spring.identity.SpringIntelliJCredentialBuilder; | ||
import com.azure.spring.identity.SpringManagedIdentityCredentialBuilder; | ||
import com.azure.spring.identity.SpringVisualStudioCodeCredentialBuilder; | ||
import org.springframework.beans.factory.ObjectProvider; | ||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; | ||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.core.annotation.Order; | ||
|
@@ -24,6 +28,7 @@ | |
* Auto-configuration for Azure Spring default token credential. | ||
*/ | ||
@Configuration | ||
@ConditionalOnProperty(name = AzureProperties.PREFIX) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we really need to depend on this property bean? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, I will remove it |
||
public class AzureDefaultTokenCredentialAutoConfiguration { | ||
|
||
public static final int SPRING_ENV_CREDENTIAL_ORDER = 0; | ||
|
@@ -87,6 +92,12 @@ public TokenCredential azureTokenCredential(List<SpringCredentialBuilderBase> cr | |
return chainedTokenCredentialBuilder.build(); | ||
} | ||
|
||
@Bean | ||
public MappingCredentialPropertiesProvider cosmosCredentialPropertiesProvider( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What if service bus and cosmos work together? |
||
ObjectProvider<SpringAzureProperties> springProperties) { | ||
return new SpringMappingCredentialPropertiesProvider((AzureProperties) springProperties.getIfAvailable()); | ||
} | ||
|
||
/*@Bean | ||
public CredentialBuilderPostProcessor credentialBuilderPostProcessor() { | ||
return new CredentialBuilderPostProcessor(); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
package com.azure.spring.autoconfigure.unity.identity; | ||
|
||
import com.azure.core.credential.TokenCredential; | ||
import com.azure.spring.AbstractMappingCredentialPropertiesProvider; | ||
import com.azure.spring.autoconfigure.unity.AzureProperties; | ||
import com.azure.spring.identity.SpringEnvironmentCredentialBuilder; | ||
import org.springframework.core.Ordered; | ||
|
||
public class SpringMappingCredentialPropertiesProvider extends AbstractMappingCredentialPropertiesProvider implements Ordered { | ||
|
||
public SpringMappingCredentialPropertiesProvider(AzureProperties azureProperties) { | ||
if (azureProperties != null) { | ||
mapAzureProperties(azureProperties.getCredential(), azureProperties.getEnvironment()); | ||
} | ||
} | ||
|
||
@Override | ||
public int getOrder() { | ||
return Ordered.LOWEST_PRECEDENCE; | ||
} | ||
|
||
@Override | ||
public TokenCredential mappingTokenCredential() { | ||
SpringEnvironmentCredentialBuilder mapEnvironmentCredentialBuilder = | ||
new SpringEnvironmentCredentialBuilder().credentialPropertiesProvider(this); | ||
return mapEnvironmentCredentialBuilder.build(); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally I don't like this code style.