Add decrypted property source to bootstrap environment

... and rebind to @ConfigurationProperties so that other listeners
can take advantage of the changes.

Fixes gh-32
This commit is contained in:
Dave Syer
2015-05-19 09:26:27 +01:00
parent 9d4584bb5c
commit e340855b32
6 changed files with 92 additions and 16 deletions

View File

@@ -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

View File

@@ -54,7 +54,7 @@ import org.springframework.util.StringUtils;
*/
public class BootstrapApplicationListener implements
ApplicationListener<ApplicationEnvironmentPreparedEvent>, Ordered {
public static final String BOOTSTRAP_PROPERTY_SOURCE_NAME = "bootstrap";
public static final int DEFAULT_ORDER = Ordered.HIGHEST_PRECEDENCE + 5;

View File

@@ -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<PropertySourceLocator> propertySourceLocators) {
this.propertySourceLocators = new ArrayList<>(propertySourceLocators);

View File

@@ -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;
}

View File

@@ -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<ConfigurableApplicationContext>, 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<String, Object> decrypt(PropertySources propertySources) {
Map<String, Object> overrides = new LinkedHashMap<String, Object>();
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<String, Object> overrides) {
@@ -123,5 +163,5 @@ public class EnvironmentDecryptApplicationInitializer implements
}
}
}

View File

@@ -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.
*