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:
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user