diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/keyvault/secrets/properties/AzureKeyVaultPropertySourceProperties.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/keyvault/secrets/properties/AzureKeyVaultPropertySourceProperties.java
index a7cb499bd2082..310ebb228c56b 100644
--- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/keyvault/secrets/properties/AzureKeyVaultPropertySourceProperties.java
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/implementation/keyvault/secrets/properties/AzureKeyVaultPropertySourceProperties.java
@@ -9,8 +9,6 @@
import java.time.Duration;
import java.util.List;
-import static com.azure.spring.cloud.autoconfigure.keyvault.environment.KeyVaultPropertySource.DEFAULT_AZURE_KEYVAULT_PROPERTYSOURCE_NAME;
-
/**
* Configurations to set when Azure Key Vault is used as an external property source.
*
@@ -31,7 +29,7 @@ public class AzureKeyVaultPropertySourceProperties extends AbstractAzureHttpConf
/**
* Name of this property source.
*/
- private String name = DEFAULT_AZURE_KEYVAULT_PROPERTYSOURCE_NAME;
+ private String name;
/**
* Defines the constant for the property that enables/disables case-sensitive keys.
*/
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/keyvault/environment/KeyVaultEnvironmentPostProcessor.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/keyvault/environment/KeyVaultEnvironmentPostProcessor.java
index d17803830fdb7..4172c67927194 100644
--- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/keyvault/environment/KeyVaultEnvironmentPostProcessor.java
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/keyvault/environment/KeyVaultEnvironmentPostProcessor.java
@@ -4,16 +4,14 @@
package com.azure.spring.cloud.autoconfigure.keyvault.environment;
import com.azure.security.keyvault.secrets.SecretClient;
+import com.azure.spring.cloud.autoconfigure.context.AzureGlobalProperties;
import com.azure.spring.cloud.autoconfigure.implementation.keyvault.secrets.properties.AzureKeyVaultPropertySourceProperties;
import com.azure.spring.cloud.autoconfigure.implementation.keyvault.secrets.properties.AzureKeyVaultSecretProperties;
-import com.azure.spring.cloud.autoconfigure.context.AzureGlobalProperties;
-import com.azure.spring.cloud.autoconfigure.implementation.properties.utils.AzureGlobalPropertiesUtils;
import com.azure.spring.cloud.core.implementation.util.AzurePropertiesUtils;
import com.azure.spring.cloud.service.implementation.keyvault.secrets.SecretClientBuilderFactory;
import org.apache.commons.logging.Log;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.context.config.ConfigDataEnvironmentPostProcessor;
-import org.springframework.boot.context.properties.PropertyMapper;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.env.EnvironmentPostProcessor;
@@ -21,17 +19,17 @@
import org.springframework.core.Ordered;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources;
-import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
-import java.util.Collections;
+import java.util.ArrayList;
import java.util.List;
import static org.springframework.core.env.StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME;
/**
- * Leverage {@link EnvironmentPostProcessor} to add Key Vault secrets as a property source.
+ * Leverage {@link EnvironmentPostProcessor} to insert {@link KeyVaultPropertySource}s into {@link ConfigurableEnvironment}.
+ * {@link KeyVaultPropertySource}s are constructed according to {@link AzureKeyVaultSecretProperties},
*
* @since 4.0.0
*/
@@ -51,115 +49,100 @@ public KeyVaultEnvironmentPostProcessor(Log logger) {
}
/**
- * Construct a {@link KeyVaultEnvironmentPostProcessor} instance with default value.
+ * Construct a {@link KeyVaultEnvironmentPostProcessor} instance with a new {@link DeferredLog}.
*/
public KeyVaultEnvironmentPostProcessor() {
this.logger = new DeferredLog();
}
/**
- * Post-process the environment.
- *
- *
- * Here we are going to process any key vault(s) and make them as available PropertySource(s). Note this supports
- * both the singular key vault setup, as well as the multiple key vault setup.
- *
+ * Construct {@link KeyVaultPropertySource}s according to {@link AzureKeyVaultSecretProperties},
+ * then insert these {@link KeyVaultPropertySource}s into {@link ConfigurableEnvironment}.
*
* @param environment the environment.
* @param application the application.
*/
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
- if (!isKeyVaultClientAvailable()) {
- logger.info("Key Vault client is not present, skip the Key Vault property source");
+ if (!isKeyVaultClientOnClasspath()) {
+ logger.debug("Skip configuring Key Vault PropertySource. "
+ + "Because com.azure:azure-security-keyvault-secrets doesn't exist in classpath.");
return;
}
- final AzureKeyVaultSecretProperties keyVaultSecretProperties = loadProperties(Binder.get(environment));
-
- // In propertySources list, smaller index has higher priority.
- final List propertySources = keyVaultSecretProperties.getPropertySources();
- Collections.reverse(propertySources);
-
- if (propertySources.isEmpty() && StringUtils.hasText(keyVaultSecretProperties.getEndpoint())) {
- propertySources.add(new AzureKeyVaultPropertySourceProperties());
+ final AzureKeyVaultSecretProperties secretProperties = loadProperties(environment);
+ if (!secretProperties.isPropertySourceEnabled()) {
+ logger.debug("Skip configuring Key Vault PropertySource. "
+ + "Because spring.cloud.azure.keyvault.secret.property-source-enabled=false");
+ return;
+ }
+ if (secretProperties.getPropertySources().isEmpty()) {
+ logger.debug("Skip configuring Key Vault PropertySource. "
+ + "Because spring.cloud.azure.keyvault.secret.property-sources is empty.");
+ return;
}
- if (isKeyVaultPropertySourceEnabled(keyVaultSecretProperties)) {
- for (AzureKeyVaultPropertySourceProperties propertySource : propertySources) {
- final AzureKeyVaultPropertySourceProperties properties = getMergeProperties(keyVaultSecretProperties,
- propertySource);
- if (properties.isEnabled()) {
- addKeyVaultPropertySource(environment, properties);
- }
+ final List propertiesList = secretProperties.getPropertySources();
+ List keyVaultPropertySources = buildKeyVaultPropertySourceList(propertiesList);
+ final MutablePropertySources propertySources = environment.getPropertySources();
+ // reverse iterate order making sure smaller index has higher priority.
+ for (int i = keyVaultPropertySources.size() - 1; i >= 0; i--) {
+ KeyVaultPropertySource propertySource = keyVaultPropertySources.get(i);
+ logger.debug("Inserting Key Vault PropertySource. name = " + propertySource.getName());
+ if (propertySources.contains(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME)) {
+ propertySources.addAfter(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, propertySource);
+ } else {
+ propertySources.addFirst(propertySource);
}
- } else {
- logger.debug("Key Vault 'propertySourceEnabled' or 'enabled' is not enabled");
}
}
- // TODO (xiada) better way to implement this
- private AzureKeyVaultPropertySourceProperties getMergeProperties(AzureKeyVaultSecretProperties secretProperties,
- AzureKeyVaultPropertySourceProperties propertySource) {
- AzureKeyVaultPropertySourceProperties mergedResult = new AzureKeyVaultPropertySourceProperties();
- AzurePropertiesUtils.mergeAzureCommonProperties(secretProperties, propertySource, mergedResult);
-
- mergedResult.setEndpoint(secretProperties.getEndpoint());
- mergedResult.setServiceVersion(secretProperties.getServiceVersion());
- mergedResult.setEnabled(propertySource.isEnabled());
- mergedResult.setName(propertySource.getName());
- mergedResult.setCaseSensitive(propertySource.isCaseSensitive());
- mergedResult.setSecretKeys(propertySource.getSecretKeys());
- mergedResult.setRefreshInterval(propertySource.getRefreshInterval());
-
- PropertyMapper propertyMapper = PropertyMapper.get().alwaysApplyingWhenNonNull();
- propertyMapper.from(propertySource.getEndpoint()).to(mergedResult::setEndpoint);
- propertyMapper.from(propertySource.getServiceVersion()).to(mergedResult::setServiceVersion);
-
- return mergedResult;
+ private List buildKeyVaultPropertySourceList(
+ List propertiesList) {
+ List propertySources = new ArrayList<>();
+ for (int i = 0; i < propertiesList.size(); i++) {
+ AzureKeyVaultPropertySourceProperties properties = propertiesList.get(i);
+ if (!properties.isEnabled()) {
+ logger.debug("Skip configuring Key Vault PropertySource. "
+ + "Because spring.cloud.azure.keyvault.secret.property-sources[" + i + "].enabled = false.");
+ continue;
+ }
+ if (!StringUtils.hasText(properties.getEndpoint())) {
+ logger.debug("Skip configuring Key Vault PropertySource. "
+ + "Because spring.cloud.azure.keyvault.secret.property-sources[" + i + "].endpoint is empty.");
+ continue;
+ }
+ propertySources.add(buildKeyVaultPropertySource(properties));
+ }
+ return propertySources;
}
+ private KeyVaultPropertySource buildKeyVaultPropertySource(
+ AzureKeyVaultPropertySourceProperties properties) {
+ try {
+ final KeyVaultOperation keyVaultOperation = new KeyVaultOperation(
+ buildSecretClient(properties),
+ properties.getRefreshInterval(),
+ properties.getSecretKeys(),
+ properties.isCaseSensitive());
+ return new KeyVaultPropertySource(properties.getName(), keyVaultOperation);
+ } catch (final Exception exception) {
+ throw new IllegalStateException("Failed to configure KeyVault property source", exception);
+ }
+ }
- /**
- * Add a Key Vault property source.
- *
- *
- * The normalizedName is used to target a specific key vault (note if the name is the empty string it works as
- * before with only one key vault present). The normalized name is the name of the specific key vault plus a
- * trailing "." at the end.
- *
- *
- * @param environment The Spring environment.
- * @param propertySource The property source properties.
- * @throws IllegalStateException If KeyVaultOperations fails to initialize.
- */
- private void addKeyVaultPropertySource(ConfigurableEnvironment environment,
- AzureKeyVaultPropertySourceProperties propertySource) {
- Assert.notNull(propertySource.getEndpoint(), "endpoint must not be null!");
+ private SecretClient buildSecretClient(AzureKeyVaultPropertySourceProperties propertySourceProperties) {
+ AzureKeyVaultSecretProperties secretProperties = toAzureKeyVaultSecretProperties(propertySourceProperties);
+ return buildSecretClient(secretProperties);
+ }
+ private AzureKeyVaultSecretProperties toAzureKeyVaultSecretProperties(
+ AzureKeyVaultPropertySourceProperties propertySourceProperties) {
AzureKeyVaultSecretProperties secretProperties = new AzureKeyVaultSecretProperties();
- AzurePropertiesUtils.copyAzureCommonProperties(propertySource, secretProperties);
- secretProperties.setServiceVersion(propertySource.getServiceVersion());
- secretProperties.setEndpoint(propertySource.getEndpoint());
- try {
- final MutablePropertySources sources = environment.getPropertySources();
- final SecretClient secretClient = buildSecretClient(secretProperties);
- final KeyVaultOperation keyVaultOperation = new KeyVaultOperation(secretClient,
- propertySource.getRefreshInterval(),
- propertySource.getSecretKeys(),
- propertySource.isCaseSensitive());
- KeyVaultPropertySource keyVaultPropertySource = new KeyVaultPropertySource(propertySource.getName(),
- keyVaultOperation);
- if (sources.contains(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME)) {
- sources.addAfter(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, keyVaultPropertySource);
- } else {
- // TODO (xiada): confirm the order
- sources.addFirst(keyVaultPropertySource);
- }
-
- } catch (final Exception ex) {
- throw new IllegalStateException("Failed to configure KeyVault property source", ex);
- }
+ AzurePropertiesUtils.copyAzureCommonProperties(propertySourceProperties, secretProperties);
+ secretProperties.setEndpoint(propertySourceProperties.getEndpoint());
+ secretProperties.setServiceVersion(propertySourceProperties.getServiceVersion());
+ return secretProperties;
}
/**
@@ -171,38 +154,60 @@ SecretClient buildSecretClient(AzureKeyVaultSecretProperties secretProperties) {
return new SecretClientBuilderFactory(secretProperties).build().buildClient();
}
- private AzureKeyVaultSecretProperties loadProperties(Binder binder) {
- AzureGlobalProperties azureProperties = binder
+ AzureKeyVaultSecretProperties loadProperties(ConfigurableEnvironment environment) {
+ Binder binder = Binder.get(environment);
+ AzureGlobalProperties globalProperties = binder
.bind(AzureGlobalProperties.PREFIX, Bindable.of(AzureGlobalProperties.class))
.orElseGet(AzureGlobalProperties::new);
- AzureKeyVaultSecretProperties existingValue = new AzureKeyVaultSecretProperties();
- AzureGlobalPropertiesUtils.loadProperties(azureProperties, existingValue);
+ AzureKeyVaultSecretProperties secretProperties = binder
+ .bind(AzureKeyVaultSecretProperties.PREFIX, Bindable.of(AzureKeyVaultSecretProperties.class))
+ .orElseGet(AzureKeyVaultSecretProperties::new);
+
+ List list = secretProperties.getPropertySources();
+
+ // Load properties from global properties.
+ for (int i = 0; i < list.size(); i++) {
+ list.set(i, buildMergedProperties(globalProperties, list.get(i)));
+ }
- return binder
- .bind(AzureKeyVaultSecretProperties.PREFIX,
- Bindable.of(AzureKeyVaultSecretProperties.class).withExistingValue(existingValue))
- .orElseGet(AzureKeyVaultSecretProperties::new);
+ // Name must be unique for each property source.
+ // Because MutablePropertySources#add will remove property source with existing name.
+ for (int i = 0; i < list.size(); i++) {
+ AzureKeyVaultPropertySourceProperties propertySourceProperties = list.get(i);
+ if (!StringUtils.hasText(propertySourceProperties.getName())) {
+ propertySourceProperties.setName(buildPropertySourceName(i));
+ }
+ }
+ return secretProperties;
}
- /**
- * Is the Key Vault property source enabled.
- *
- * @param properties The Azure Key Vault Secret properties.
- * @return true if the key vault is enabled, false otherwise.
- */
- private boolean isKeyVaultPropertySourceEnabled(AzureKeyVaultSecretProperties properties) {
- return properties.isEnabled()
- && (properties.isPropertySourceEnabled() && !properties.getPropertySources().isEmpty());
+ private AzureKeyVaultPropertySourceProperties buildMergedProperties(
+ AzureGlobalProperties globalProperties,
+ AzureKeyVaultPropertySourceProperties propertySourceProperties) {
+ AzureKeyVaultPropertySourceProperties mergedProperties = new AzureKeyVaultPropertySourceProperties();
+ AzurePropertiesUtils.mergeAzureCommonProperties(globalProperties, propertySourceProperties, mergedProperties);
+ mergedProperties.setEnabled(propertySourceProperties.isEnabled());
+ mergedProperties.setName(propertySourceProperties.getName());
+ mergedProperties.setEndpoint(propertySourceProperties.getEndpoint());
+ mergedProperties.setServiceVersion(propertySourceProperties.getServiceVersion());
+ mergedProperties.setCaseSensitive(propertySourceProperties.isCaseSensitive());
+ mergedProperties.setSecretKeys(propertySourceProperties.getSecretKeys());
+ mergedProperties.setRefreshInterval(propertySourceProperties.getRefreshInterval());
+ return mergedProperties;
+ }
+
+ String buildPropertySourceName(int index) {
+ return "azure-key-vault-secret-property-source-" + index;
}
- private boolean isKeyVaultClientAvailable() {
+ private boolean isKeyVaultClientOnClasspath() {
return ClassUtils.isPresent("com.azure.security.keyvault.secrets.SecretClient",
KeyVaultEnvironmentPostProcessor.class.getClassLoader());
}
/**
- *
+ * Get the order value of this object.
* @return The order value.
*/
@Override
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/keyvault/environment/KeyVaultPropertySource.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/keyvault/environment/KeyVaultPropertySource.java
index 9ad7ba1e3a1f8..897eb313899fc 100644
--- a/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/keyvault/environment/KeyVaultPropertySource.java
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/main/java/com/azure/spring/cloud/autoconfigure/keyvault/environment/KeyVaultPropertySource.java
@@ -14,26 +14,14 @@
public class KeyVaultPropertySource extends EnumerablePropertySource {
private final KeyVaultOperation operations;
- public static final String DEFAULT_AZURE_KEYVAULT_PROPERTYSOURCE_NAME = "azurekv";
/**
- * Creates a new instance of {@link KeyVaultPropertySource}.
- *
- * @param keyVaultName the KeyVault name
- * @param operation the KeyVault operation
+ * Create a new {@code KeyVaultPropertySource} with the given name and {@link KeyVaultOperation}.
+ * @param name the associated name
+ * @param operation the {@link KeyVaultOperation}
*/
- public KeyVaultPropertySource(String keyVaultName, KeyVaultOperation operation) {
- super(keyVaultName, operation);
- this.operations = operation;
- }
-
- /**
- * Creates a new instance of {@link KeyVaultPropertySource}.
- *
- * @param operation the KeyVault operation
- */
- public KeyVaultPropertySource(KeyVaultOperation operation) {
- super(DEFAULT_AZURE_KEYVAULT_PROPERTYSOURCE_NAME, operation);
+ public KeyVaultPropertySource(String name, KeyVaultOperation operation) {
+ super(name, operation);
this.operations = operation;
}
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/keyvault/environment/KeyVaultEnvironmentPostProcessorTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/keyvault/environment/KeyVaultEnvironmentPostProcessorTests.java
index b0d9db9361423..ae078843ba3b9 100644
--- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/keyvault/environment/KeyVaultEnvironmentPostProcessorTests.java
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/keyvault/environment/KeyVaultEnvironmentPostProcessorTests.java
@@ -4,8 +4,10 @@
package com.azure.spring.cloud.autoconfigure.keyvault.environment;
import com.azure.security.keyvault.secrets.SecretClient;
+import com.azure.spring.cloud.autoconfigure.implementation.keyvault.secrets.properties.AzureKeyVaultPropertySourceProperties;
import com.azure.spring.cloud.autoconfigure.implementation.keyvault.secrets.properties.AzureKeyVaultSecretProperties;
import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.springframework.boot.SpringApplication;
@@ -19,9 +21,9 @@
import java.util.Collections;
import java.util.Iterator;
-import static com.azure.spring.cloud.autoconfigure.keyvault.environment.KeyVaultPropertySource.DEFAULT_AZURE_KEYVAULT_PROPERTYSOURCE_NAME;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
@@ -32,143 +34,256 @@
class KeyVaultEnvironmentPostProcessorTests {
+ private static final String NAME_0 = "name_0";
+ private static final String NAME_1 = "name_1";
+ private static final String ENDPOINT_0 = "https://test0.vault.azure.net/";
+ private static final String ENDPOINT_1 = "https://test1.vault.azure.net/";
+
private final SpringApplication application = new SpringApplication();
private KeyVaultEnvironmentPostProcessor processor;
private MockEnvironment environment;
private MutablePropertySources propertySources;
@BeforeEach
- void setup() {
+ void beforeEach() {
processor = spy(new KeyVaultEnvironmentPostProcessor(new DeferredLog()));
environment = new MockEnvironment();
propertySources = environment.getPropertySources();
+ SecretClient secretClient = mock(SecretClient.class);
+ doReturn(secretClient).when(processor).buildSecretClient(any(AzureKeyVaultSecretProperties.class));
}
@Test
void postProcessorHasConfiguredOrder() {
- final KeyVaultEnvironmentPostProcessor processor = new KeyVaultEnvironmentPostProcessor(new DeferredLog());
+ final KeyVaultEnvironmentPostProcessor processor = new KeyVaultEnvironmentPostProcessor();
assertEquals(processor.getOrder(), KeyVaultEnvironmentPostProcessor.ORDER);
}
@Test
- void keyVaultClientIsNotAvailable() {
+ void insertSinglePropertySourceTest() {
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-source-enabled", "true");
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].enabled", "true");
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].name", NAME_0);
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].endpoint", ENDPOINT_0);
+ processor.postProcessEnvironment(environment, application);
+ assertTrue(propertySources.contains(NAME_0));
+ }
+
+ @Test
+ void insertMultiplePropertySourceTest() {
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-source-enabled", "true");
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].enabled", "true");
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].name", NAME_0);
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].endpoint", ENDPOINT_0);
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[1].enabled", "true");
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[1].name", NAME_1);
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[1].endpoint", ENDPOINT_1);
+ processor.postProcessEnvironment(environment, application);
+ assertTrue(propertySources.contains(NAME_0));
+ assertTrue(propertySources.contains(NAME_1));
+ }
+
+ @Test
+ void keyVaultClientNotExistInClassPathTest() {
try (MockedStatic classUtils = mockStatic(ClassUtils.class)) {
- classUtils.when(() ->
- ClassUtils.isPresent("com.azure.security.keyvault.secrets.SecretClient",
- this.getClass().getClassLoader()))
- .thenReturn(false);
- processor.postProcessEnvironment(this.environment, this.application);
- final MutablePropertySources sources = this.environment.getPropertySources();
- assertFalse(sources.contains(DEFAULT_AZURE_KEYVAULT_PROPERTYSOURCE_NAME));
+ classUtils.when(() -> ClassUtils.isPresent("com.azure.security.keyvault.secrets.SecretClient", getClass().getClassLoader()))
+ .thenReturn(false);
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-source-enabled", "true");
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].enabled", "true");
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].name", NAME_0);
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].endpoint", ENDPOINT_0);
+ processor.postProcessEnvironment(environment, application);
+ assertFalse(propertySources.contains(NAME_0));
}
}
@Test
- void sourcesNotExistsWhenConfigureEnabledFalse() {
- environment.setProperty("spring.cloud.azure.keyvault.secret.enabled", "false");
- processor.postProcessEnvironment(this.environment, this.application);
- final MutablePropertySources sources = this.environment.getPropertySources();
- assertFalse(sources.contains(DEFAULT_AZURE_KEYVAULT_PROPERTYSOURCE_NAME));
+ void disableAllPropertySourceTest() {
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-source-enabled", "false");
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].enabled", "true");
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].name", NAME_0);
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].endpoint", ENDPOINT_0);
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[1].enabled", "true");
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[1].name", NAME_1);
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[1].endpoint", ENDPOINT_1);
+ processor.postProcessEnvironment(environment, application);
+ assertFalse(propertySources.contains(NAME_0));
+ assertFalse(propertySources.contains(NAME_1));
}
@Test
- void sourcesNotExistsWhenConfigurePropertySourceEnabledFalseAndPropertySourcesEmpty() {
- environment.setProperty("spring.cloud.azure.keyvault.secret.propertySourceEnabled", "false");
- processor.postProcessEnvironment(this.environment, this.application);
- final MutablePropertySources sources = this.environment.getPropertySources();
- assertFalse(sources.contains(DEFAULT_AZURE_KEYVAULT_PROPERTYSOURCE_NAME));
+ void emptyPropertySourceListTest() {
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-source-enabled", "true");
+ processor.postProcessEnvironment(environment, application);
+ assertEquals(1, propertySources.size());
}
@Test
- void defaultPropertySourcesAdded() {
- environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].endpoint",
- "https://test.vault.azure.net/");
- SecretClient secretClient = mock(SecretClient.class);
- doReturn(secretClient).when(processor).buildSecretClient(any(AzureKeyVaultSecretProperties.class));
- processor.postProcessEnvironment(this.environment, this.application);
- final MutablePropertySources sources = this.environment.getPropertySources();
- assertTrue(sources.contains(DEFAULT_AZURE_KEYVAULT_PROPERTYSOURCE_NAME));
- }
-
- @Test
- void configuredPropertySourcesAddedWhenEnabled() {
- String propertySourcesOne = "testkeyOne";
- String propertySourcesTwo = "testkeyTwo";
- environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].name", propertySourcesOne);
- environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].endpoint",
- "https://test.vault.azure.net/");
- environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[1].enabled", "false");
- environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[1].name", propertySourcesTwo);
- environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[1].endpoint",
- "https://test2.vault.azure.net/");
- SecretClient secretClient = mock(SecretClient.class);
- doReturn(secretClient).when(processor).buildSecretClient(any(AzureKeyVaultSecretProperties.class));
- processor.postProcessEnvironment(this.environment, this.application);
- final MutablePropertySources sources = this.environment.getPropertySources();
- assertTrue(sources.contains(propertySourcesOne));
- assertFalse(sources.contains(propertySourcesTwo));
+ void disableSpecificOnePropertySourceTest() {
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-source-enabled", "true");
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].enabled", "false");
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].name", NAME_0);
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].endpoint", ENDPOINT_0);
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[1].enabled", "true");
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[1].name", NAME_1);
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[1].endpoint", ENDPOINT_1);
+ processor.postProcessEnvironment(environment, application);
+ assertFalse(propertySources.contains(NAME_0));
+ assertTrue(propertySources.contains(NAME_1));
}
@Test
- void configuredPropertySourcesIsFirst() {
- String sourceName = "testkey";
- environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].name", sourceName);
- environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].endpoint",
- "https://test.vault.azure.net/");
- SecretClient secretClient = mock(SecretClient.class);
- doReturn(secretClient).when(processor).buildSecretClient(any(AzureKeyVaultSecretProperties.class));
- processor.postProcessEnvironment(this.environment, this.application);
- final MutablePropertySources sources = this.environment.getPropertySources();
- Iterator> iterator = sources.iterator();
- assertTrue(iterator.next().getName().equals(sourceName));
+ void enableByDefaultTest() {
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].name", NAME_0);
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].endpoint", ENDPOINT_0);
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[1].name", NAME_1);
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[1].endpoint", ENDPOINT_1);
+ processor.postProcessEnvironment(environment, application);
+ assertTrue(propertySources.contains(NAME_0));
+ assertTrue(propertySources.contains(NAME_1));
}
@Test
- void configuredPropertySourcesWithEndpoint() {
- String mockEndPoint = "https://mockendpoint.vault.azure.net";
- environment.setProperty("spring.cloud.azure.keyvault.secret.endpoint", mockEndPoint);
- SecretClient secretClient = mock(SecretClient.class);
- doReturn(secretClient).when(processor).buildSecretClient(any(AzureKeyVaultSecretProperties.class));
- processor.postProcessEnvironment(this.environment, this.application);
- final MutablePropertySources sources = this.environment.getPropertySources();
- Iterator> iterator = sources.iterator();
- assertTrue(DEFAULT_AZURE_KEYVAULT_PROPERTYSOURCE_NAME.equals(iterator.next().getName()));
+ void endPointNotConfiguredTest() {
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-source-enabled", "true");
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].enabled", "true");
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].name", NAME_0);
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[1].enabled", "true");
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[1].name", NAME_1);
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[1].endpoint", ENDPOINT_1);
+ processor.postProcessEnvironment(environment, application);
+ assertFalse(propertySources.contains(NAME_0));
+ assertTrue(propertySources.contains(NAME_1));
}
@Test
- void configuredPropertySourcesIgnoreEndPoint() {
- String mockEndPoint = "https://mockendpoint.vault.azure.net";
- environment.setProperty("spring.cloud.azure.keyvault.secret.endpoint", mockEndPoint);
+ void defaultPropertySourceNameTest() {
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-source-enabled", "true");
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].enabled", "true");
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].endpoint", ENDPOINT_0);
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[1].enabled", "true");
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[1].endpoint", ENDPOINT_1);
+ processor.postProcessEnvironment(environment, application);
+ assertTrue(propertySources.contains(processor.buildPropertySourceName(0)));
+ assertTrue(propertySources.contains(processor.buildPropertySourceName(1)));
+ }
- String sourceName = "testkey";
- environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].name", sourceName);
- environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].endpoint",
- "https://test.vault.azure.net/");
- SecretClient secretClient = mock(SecretClient.class);
- doReturn(secretClient).when(processor).buildSecretClient(any(AzureKeyVaultSecretProperties.class));
- processor.postProcessEnvironment(this.environment, this.application);
- final MutablePropertySources sources = this.environment.getPropertySources();
- Iterator> iterator = sources.iterator();
- assertTrue(iterator.next().getName().equals(sourceName));
+ @Test
+ void keyVaultPropertySourceHasHighestPriorityIfEnvironmentPropertySourceNotExistTest() {
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-source-enabled", "true");
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].enabled", "true");
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].name", NAME_0);
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].endpoint", ENDPOINT_0);
+ processor.postProcessEnvironment(environment, application);
+ Iterator> iterator = propertySources.iterator();
+ assertEquals(NAME_0, iterator.next().getName());
+ assertTrue(iterator.hasNext());
}
@Test
- void configuredPropertySourcesLocationIsAfterSystemEnvironmentPropertySources() {
- environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].endpoint",
- "https://test.vault.azure.net/");
- SecretClient secretClient = mock(SecretClient.class);
- doReturn(secretClient).when(processor).buildSecretClient(any(AzureKeyVaultSecretProperties.class));
- propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, Collections.emptyMap()));
- processor.postProcessEnvironment(this.environment, this.application);
- final MutablePropertySources sources = this.environment.getPropertySources();
- Iterator> it = sources.iterator();
- while (it.hasNext()) {
- PropertySource> propertySource = it.next();
+ void keyVaultPropertySourceHasLowerPriorityThanEnvironmentPropertySourceTest() {
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-source-enabled", "true");
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].enabled", "true");
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].name", NAME_0);
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].endpoint", ENDPOINT_0);
+ propertySources.addFirst(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, Collections.emptyMap()));
+ processor.postProcessEnvironment(environment, application);
+ Iterator> iterator = propertySources.iterator();
+ while (iterator.hasNext()) {
+ PropertySource> propertySource = iterator.next();
if (SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME.equals(propertySource.getName())) {
break;
}
}
- assertTrue(DEFAULT_AZURE_KEYVAULT_PROPERTYSOURCE_NAME.equals(it.next().getName()));
+ assertEquals(NAME_0, iterator.next().getName());
+ assertTrue(iterator.hasNext());
+ }
+
+ @Test
+ void keyVaultPropertySourceOrderTest() {
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-source-enabled", "true");
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].enabled", "true");
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].name", NAME_0);
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].endpoint", ENDPOINT_0);
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[1].enabled", "true");
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[1].name", NAME_1);
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[1].endpoint", ENDPOINT_1);
+ processor.postProcessEnvironment(environment, application);
+ Iterator> iterator = propertySources.iterator();
+ assertEquals(NAME_0, iterator.next().getName());
+ assertEquals(NAME_1, iterator.next().getName());
+ assertTrue(iterator.hasNext());
+ }
+
+ @Test
+ void globalPropertiesTakeEffectIfSpecificPropertiesNotSetTest() {
+ final String globalHostname = "globalHostname";
+ final String globalApplicationId = "globalApplicationId";
+ final String globalTenantId = "globalTenantId";
+ final String globalUsername = "globalUsername";
+ final int globalMaxRetries = 1;
+ environment.setProperty("spring.cloud.azure.client.application-id", globalApplicationId);
+ environment.setProperty("spring.cloud.azure.credential.username", globalUsername);
+ environment.setProperty("spring.cloud.azure.profile.tenant-id", globalTenantId);
+ environment.setProperty("spring.cloud.azure.proxy.hostname", globalHostname);
+ environment.setProperty("spring.cloud.azure.retry.fixed.max-retries", "" + globalMaxRetries);
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-source-enabled", "true");
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].enabled", "true");
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].name", NAME_0);
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].endpoint", ENDPOINT_0);
+ AzureKeyVaultSecretProperties secretProperties = processor.loadProperties(environment);
+ AzureKeyVaultPropertySourceProperties properties = secretProperties.getPropertySources().get(0);
+ assertEquals(globalUsername, properties.getCredential().getUsername());
+ assertEquals(globalApplicationId, properties.getClient().getApplicationId());
+ assertEquals(globalTenantId, properties.getProfile().getTenantId());
+ assertEquals(globalHostname, properties.getProxy().getHostname());
+ assertEquals(globalMaxRetries, properties.getRetry().getFixed().getMaxRetries());
+ }
+
+ @Test
+ void specificPropertiesHasHigherPriorityThanGlobalPropertiesTest() {
+ final String globalHostname = "globalHostname";
+ final String globalApplicationId = "globalApplicationId";
+ final String globalTenantId = "globalTenantId";
+ final String globalUsername = "globalUsername";
+ final int globalMaxRetries = 1;
+ final String specificHostname = "specificHostname";
+ final String specificApplicationId = "specificApplicationId";
+ final String specificTenantId = "specificTenantId";
+ final String specificUsername = "specificUsername";
+ final int specificMaxRetries = 2;
+ environment.setProperty("spring.cloud.azure.client.application-id", globalApplicationId);
+ environment.setProperty("spring.cloud.azure.credential.username", globalUsername);
+ environment.setProperty("spring.cloud.azure.profile.tenant-id", globalTenantId);
+ environment.setProperty("spring.cloud.azure.proxy.hostname", globalHostname);
+ environment.setProperty("spring.cloud.azure.retry.fixed.max-retries", "" + globalMaxRetries);
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-source-enabled", "true");
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].enabled", "true");
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].name", NAME_0);
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].endpoint", ENDPOINT_0);
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].client.application-id", specificApplicationId);
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].credential.username", specificUsername);
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].profile.tenant-id", specificTenantId);
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].proxy.hostname", specificHostname);
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].retry.fixed.max-retries", "" + specificMaxRetries);
+ AzureKeyVaultSecretProperties secretProperties = processor.loadProperties(environment);
+ AzureKeyVaultPropertySourceProperties properties = secretProperties.getPropertySources().get(0);
+ assertEquals(specificUsername, properties.getCredential().getUsername());
+ assertEquals(specificApplicationId, properties.getClient().getApplicationId());
+ assertEquals(specificTenantId, properties.getProfile().getTenantId());
+ assertEquals(specificHostname, properties.getProxy().getHostname());
+ assertEquals(specificMaxRetries, properties.getRetry().getFixed().getMaxRetries());
+ }
+
+ @Disabled("Disable it to unblock Azure Dev Ops pipeline: https://dev.azure.com/azure-sdk/public/_build/results?buildId=1434354&view=logs&j=c1fb1ddd-7688-52ac-4c5f-1467e51181f3")
+ @Test
+ void buildKeyVaultPropertySourceWithExceptionTest() {
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-source-enabled", "true");
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].enabled", "true");
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].name", NAME_0);
+ environment.setProperty("spring.cloud.azure.keyvault.secret.property-sources[0].endpoint", ENDPOINT_0);
+ assertThrows(IllegalStateException.class,
+ () -> new KeyVaultEnvironmentPostProcessor().postProcessEnvironment(environment, application));
}
}
diff --git a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/keyvault/environment/KeyVaultPropertySourceTests.java b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/keyvault/environment/KeyVaultPropertySourceTests.java
index 9acc9c4cc7b1c..19eee73e47cde 100644
--- a/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/keyvault/environment/KeyVaultPropertySourceTests.java
+++ b/sdk/spring/spring-cloud-azure-autoconfigure/src/test/java/com/azure/spring/cloud/autoconfigure/keyvault/environment/KeyVaultPropertySourceTests.java
@@ -35,7 +35,7 @@ public void setup() {
when(keyVaultOperation.getProperty(anyString())).thenReturn(TEST_PROPERTY_NAME_1);
when(keyVaultOperation.getPropertyNames()).thenReturn(propertyNameList);
- keyVaultPropertySource = new KeyVaultPropertySource(keyVaultOperation);
+ keyVaultPropertySource = new KeyVaultPropertySource("azure-key-vault-secret-property-source", keyVaultOperation);
}
@AfterEach
diff --git a/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/implementation/util/AzurePropertiesUtils.java b/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/implementation/util/AzurePropertiesUtils.java
index c3b94eb4b70cd..9b6ddc09d00b0 100644
--- a/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/implementation/util/AzurePropertiesUtils.java
+++ b/sdk/spring/spring-cloud-azure-core/src/main/java/com/azure/spring/cloud/core/implementation/util/AzurePropertiesUtils.java
@@ -69,7 +69,11 @@ public static void copyAzureCommonPropertiesIgnoreNu
copyPropertiesIgnoreNull(source.getCredential(), target.getCredential());
if (source instanceof RetryOptionsProvider && target instanceof RetryOptionsProvider) {
- copyPropertiesIgnoreNull(((RetryOptionsProvider) source).getRetry(), ((RetryOptionsProvider) target).getRetry());
+ RetryOptionsProvider.RetryOptions sourceRetry = ((RetryOptionsProvider) source).getRetry();
+ RetryOptionsProvider.RetryOptions targetRetry = ((RetryOptionsProvider) target).getRetry();
+ copyPropertiesIgnoreNull(sourceRetry, targetRetry);
+ copyPropertiesIgnoreNull(sourceRetry.getExponential(), targetRetry.getExponential());
+ copyPropertiesIgnoreNull(sourceRetry.getFixed(), targetRetry.getFixed());
}
}