diff --git a/spring-cloud-context/src/main/java/org/springframework/cloud/autoconfigure/RefreshAutoConfiguration.java b/spring-cloud-context/src/main/java/org/springframework/cloud/autoconfigure/RefreshAutoConfiguration.java index c0222be5..c90a71b3 100644 --- a/spring-cloud-context/src/main/java/org/springframework/cloud/autoconfigure/RefreshAutoConfiguration.java +++ b/spring-cloud-context/src/main/java/org/springframework/cloud/autoconfigure/RefreshAutoConfiguration.java @@ -33,6 +33,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.condition.SearchStrategy; import org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration; import org.springframework.boot.context.properties.ConfigurationBeanFactoryMetaData; import org.springframework.boot.context.properties.ConfigurationProperties; @@ -135,7 +136,7 @@ public class RefreshAutoConfiguration { } @Bean - @ConditionalOnMissingBean + @ConditionalOnMissingBean(search=SearchStrategy.CURRENT) public ConfigurationPropertiesRebinder configurationPropertiesRebinder() { // Since this is a BeanPostProcessor we have to be super careful not to cause // a cascade of bean instantiation. Knowing the *name* of the beans we need is diff --git a/spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/BootstrapApplicationListener.java b/spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/BootstrapApplicationListener.java index 3b4aa2d7..7f8284d4 100644 --- a/spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/BootstrapApplicationListener.java +++ b/spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/BootstrapApplicationListener.java @@ -54,7 +54,7 @@ import org.springframework.util.StringUtils; */ public class BootstrapApplicationListener implements ApplicationListener, Ordered { - + public static final String BOOTSTRAP_PROPERTY_SOURCE_NAME = "bootstrap"; public static final int DEFAULT_ORDER = Ordered.HIGHEST_PRECEDENCE + 5; diff --git a/spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/config/PropertySourceBootstrapConfiguration.java b/spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/config/PropertySourceBootstrapConfiguration.java index d9e2c6b8..b85dad2d 100644 --- a/spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/config/PropertySourceBootstrapConfiguration.java +++ b/spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/config/PropertySourceBootstrapConfiguration.java @@ -26,6 +26,7 @@ import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.bind.PropertySourcesPropertyValues; import org.springframework.boot.bind.RelaxedDataBinder; +import org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.logging.LogFile; import org.springframework.boot.logging.LoggingSystem; @@ -64,6 +65,9 @@ public class PropertySourceBootstrapConfiguration implements @Autowired private PropertySourceBootstrapProperties properties; + @Autowired + private ConfigurationPropertiesBindingPostProcessor binder; + public void setPropertySourceLocators( Collection propertySourceLocators) { this.propertySourceLocators = new ArrayList<>(propertySourceLocators); diff --git a/spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/encrypt/EncryptionBootstrapConfiguration.java b/spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/encrypt/EncryptionBootstrapConfiguration.java index 9bfa1feb..77b251cc 100644 --- a/spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/encrypt/EncryptionBootstrapConfiguration.java +++ b/spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/encrypt/EncryptionBootstrapConfiguration.java @@ -21,9 +21,14 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; import org.springframework.boot.autoconfigure.condition.SpringBootCondition; +import org.springframework.boot.context.properties.ConfigurationBeanFactoryMetaData; +import org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor; +import org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessorRegistrar; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.bootstrap.encrypt.KeyProperties.KeyStore; import org.springframework.cloud.context.encrypt.EncryptorFactory; +import org.springframework.cloud.context.properties.ConfigurationPropertiesRebinder; +import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ConditionContext; import org.springframework.context.annotation.Conditional; @@ -89,6 +94,18 @@ public class EncryptionBootstrapConfiguration { } + @Bean + public ConfigurationPropertiesRebinder configurationPropertiesRebinder( + ApplicationContext context, ConfigurationPropertiesBindingPostProcessor binder) { + ConfigurationBeanFactoryMetaData metaData = context.getBean( + ConfigurationPropertiesBindingPostProcessorRegistrar.BINDER_BEAN_NAME + + ".store", ConfigurationBeanFactoryMetaData.class); + ConfigurationPropertiesRebinder rebinder = new ConfigurationPropertiesRebinder( + binder); + rebinder.setBeanMetaDataStore(metaData); + return rebinder; + } + @Bean public EnvironmentDecryptApplicationInitializer environmentDecryptApplicationListener() { if (encryptor == null) { @@ -96,7 +113,6 @@ public class EncryptionBootstrapConfiguration { } EnvironmentDecryptApplicationInitializer listener = new EnvironmentDecryptApplicationInitializer( encryptor); - listener.setFailOnError(key.isFailOnError()); return listener; } diff --git a/spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/encrypt/EnvironmentDecryptApplicationInitializer.java b/spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/encrypt/EnvironmentDecryptApplicationInitializer.java index 163e71d8..2048bc5f 100644 --- a/spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/encrypt/EnvironmentDecryptApplicationInitializer.java +++ b/spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/encrypt/EnvironmentDecryptApplicationInitializer.java @@ -20,6 +20,9 @@ import java.util.Map; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.cloud.bootstrap.BootstrapApplicationListener; +import org.springframework.cloud.context.environment.EnvironmentChangeEvent; +import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextInitializer; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.Ordered; @@ -27,29 +30,32 @@ import org.springframework.core.env.CompositePropertySource; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.EnumerablePropertySource; import org.springframework.core.env.MapPropertySource; +import org.springframework.core.env.MutablePropertySources; import org.springframework.core.env.PropertySource; +import org.springframework.core.env.PropertySources; import org.springframework.security.crypto.encrypt.TextEncryptor; /** + * Decrypt properties from the environment and insert them with high priority so they + * override the encrypted values. + * * @author Dave Syer * */ public class EnvironmentDecryptApplicationInitializer implements ApplicationContextInitializer, Ordered { - private static Log logger = LogFactory - .getLog(EnvironmentDecryptApplicationInitializer.class); + public static final String DECRYPTED_PROPERTY_SOURCE_NAME = "decrypted"; private int order = Ordered.HIGHEST_PRECEDENCE + 15; + private static Log logger = LogFactory + .getLog(EnvironmentDecryptApplicationInitializer.class); + private TextEncryptor encryptor; private boolean failOnError = true; - public EnvironmentDecryptApplicationInitializer(TextEncryptor encryptor) { - this.encryptor = encryptor; - } - /** * Strategy to determine how to handle exceptions during decryption. * @@ -59,6 +65,10 @@ public class EnvironmentDecryptApplicationInitializer implements this.failOnError = failOnError; } + public EnvironmentDecryptApplicationInitializer(TextEncryptor encryptor) { + this.encryptor = encryptor; + } + @Override public int getOrder() { return order; @@ -66,16 +76,46 @@ public class EnvironmentDecryptApplicationInitializer implements @Override public void initialize(ConfigurableApplicationContext applicationContext) { - ConfigurableEnvironment environment = applicationContext.getEnvironment(); + MapPropertySource decrypted = new MapPropertySource( + DECRYPTED_PROPERTY_SOURCE_NAME, decrypt(environment.getPropertySources())); + if (!decrypted.getSource().isEmpty()) { + // We have some decrypted properties + insert(environment.getPropertySources(), decrypted); + ApplicationContext parent = applicationContext.getParent(); + if (parent != null + && (parent.getEnvironment() instanceof ConfigurableEnvironment)) { + ConfigurableEnvironment mutable = (ConfigurableEnvironment) parent + .getEnvironment(); + // The parent is actually the bootstrap context, and it is fully + // initialized, so we can fire an EnvironmentChangeEvent there to rebind + // @ConfigurationProperties, in case they were encrypted. + insert(mutable.getPropertySources(), decrypted); + parent.publishEvent(new EnvironmentChangeEvent(decrypted.getSource() + .keySet())); + } + } + } + + private void insert(MutablePropertySources propertySources, + MapPropertySource propertySource) { + if (propertySources + .contains(BootstrapApplicationListener.BOOTSTRAP_PROPERTY_SOURCE_NAME)) { + propertySources.addAfter( + BootstrapApplicationListener.BOOTSTRAP_PROPERTY_SOURCE_NAME, + propertySource); + } + else { + propertySources.addFirst(propertySource); + } + } + + public Map decrypt(PropertySources propertySources) { Map overrides = new LinkedHashMap(); - for (PropertySource source : environment.getPropertySources()) { + for (PropertySource source : propertySources) { decrypt(source, overrides); } - if (!overrides.isEmpty()) { - environment.getPropertySources().addFirst( - new MapPropertySource("decrypted", overrides)); - } + return overrides; } private void decrypt(PropertySource source, Map overrides) { @@ -123,5 +163,5 @@ public class EnvironmentDecryptApplicationInitializer implements } } - + } diff --git a/spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/encrypt/KeyProperties.java b/spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/encrypt/KeyProperties.java index d3d3b11e..5b197c33 100644 --- a/spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/encrypt/KeyProperties.java +++ b/spring-cloud-context/src/main/java/org/springframework/cloud/bootstrap/encrypt/KeyProperties.java @@ -1,3 +1,18 @@ +/* + * Copyright 2013-2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ /* * Copyright 2013-2014 the original author or authors. *