Merge pull request #872 from spring-cloud/rebinder
Creates TextEncryptorBindHandler for Binder decryption. It is registered in TextEncryptorConfigBootstrapper for later use in other ConfigData implementations. Creates DecryptEnvironmentPostProcessor. This is used if bootstrap and legacy processing are not enabled. EnvironmentDecryptApplicationInitializer is only is if bootstrap and legacy processing are enabled. Fixes gh-815 @spencergibb
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2013-2020 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.bootstrap;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.boot.context.properties.bind.AbstractBindHandler;
|
||||
import org.springframework.boot.context.properties.bind.BindContext;
|
||||
import org.springframework.boot.context.properties.bind.Bindable;
|
||||
import org.springframework.boot.context.properties.source.ConfigurationPropertyName;
|
||||
import org.springframework.cloud.bootstrap.encrypt.KeyProperties;
|
||||
import org.springframework.security.crypto.encrypt.TextEncryptor;
|
||||
|
||||
/**
|
||||
* BindHandler that uses a TextEncryptor to decrypt text if properly prefixed with
|
||||
* {cipher}.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 3.0.0
|
||||
*/
|
||||
class TextEncryptorBindHandler extends AbstractBindHandler {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(TextEncryptorBindHandler.class);
|
||||
|
||||
/**
|
||||
* Prefix indicating an encrypted value.
|
||||
*/
|
||||
protected static final String ENCRYPTED_PROPERTY_PREFIX = "{cipher}";
|
||||
|
||||
private final TextEncryptor textEncryptor;
|
||||
|
||||
private final KeyProperties keyProperties;
|
||||
|
||||
TextEncryptorBindHandler(TextEncryptor textEncryptor, KeyProperties keyProperties) {
|
||||
this.textEncryptor = textEncryptor;
|
||||
this.keyProperties = keyProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object onSuccess(ConfigurationPropertyName name, Bindable<?> target, BindContext context, Object result) {
|
||||
if (result instanceof String && ((String) result).startsWith(ENCRYPTED_PROPERTY_PREFIX)) {
|
||||
return decrypt(name.toString(), (String) result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private String decrypt(String key, String original) {
|
||||
String value = original.substring(ENCRYPTED_PROPERTY_PREFIX.length());
|
||||
try {
|
||||
value = this.textEncryptor.decrypt(value);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Decrypted: key=" + key);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
catch (Exception e) {
|
||||
String message = "Cannot decrypt: key=" + key;
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.warn(message, e);
|
||||
}
|
||||
else {
|
||||
logger.warn(message);
|
||||
}
|
||||
if (this.keyProperties.isFailOnError()) {
|
||||
throw new IllegalStateException(message, e);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright 2012-2020 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.bootstrap;
|
||||
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.boot.BootstrapContext;
|
||||
import org.springframework.boot.BootstrapRegistry;
|
||||
import org.springframework.boot.Bootstrapper;
|
||||
import org.springframework.boot.context.properties.bind.BindHandler;
|
||||
import org.springframework.boot.context.properties.bind.Binder;
|
||||
import org.springframework.cloud.bootstrap.encrypt.KeyProperties;
|
||||
import org.springframework.cloud.bootstrap.encrypt.RsaProperties;
|
||||
import org.springframework.cloud.context.encrypt.EncryptorFactory;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.security.crypto.encrypt.TextEncryptor;
|
||||
import org.springframework.security.rsa.crypto.KeyStoreKeyFactory;
|
||||
import org.springframework.security.rsa.crypto.RsaSecretEncryptor;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* Bootstrapper.
|
||||
*
|
||||
* @author Marcin Grzejszczak
|
||||
* @since 3.0.0
|
||||
*/
|
||||
public class TextEncryptorConfigBootstrapper implements Bootstrapper {
|
||||
|
||||
private static final boolean RSA_IS_PRESENT = ClassUtils
|
||||
.isPresent("org.springframework.security.rsa.crypto.RsaSecretEncryptor", null);
|
||||
|
||||
@Override
|
||||
public void intitialize(BootstrapRegistry registry) {
|
||||
if (!ClassUtils.isPresent("org.springframework.security.crypto.encrypt.TextEncryptor", null)) {
|
||||
return;
|
||||
}
|
||||
|
||||
registry.registerIfAbsent(KeyProperties.class, context -> context.get(Binder.class)
|
||||
.bind(KeyProperties.PREFIX, KeyProperties.class).orElseGet(KeyProperties::new));
|
||||
if (RSA_IS_PRESENT) {
|
||||
registry.registerIfAbsent(RsaProperties.class, context -> context.get(Binder.class)
|
||||
.bind(RsaProperties.PREFIX, RsaProperties.class).orElseGet(RsaProperties::new));
|
||||
}
|
||||
registry.registerIfAbsent(TextEncryptor.class, context -> {
|
||||
KeyProperties keyProperties = context.get(KeyProperties.class);
|
||||
if (keysConfigured(keyProperties)) {
|
||||
if (RSA_IS_PRESENT) {
|
||||
RsaProperties rsaProperties = context.get(RsaProperties.class);
|
||||
return rsaTextEncryptor(keyProperties, rsaProperties);
|
||||
}
|
||||
return new EncryptorFactory(keyProperties.getSalt()).create(keyProperties.getKey());
|
||||
}
|
||||
// no keys configured
|
||||
return new FailsafeTextEncryptor();
|
||||
});
|
||||
registry.registerIfAbsent(BindHandler.class, context -> {
|
||||
TextEncryptor textEncryptor = context.get(TextEncryptor.class);
|
||||
if (textEncryptor != null) {
|
||||
KeyProperties keyProperties = context.get(KeyProperties.class);
|
||||
return new TextEncryptorBindHandler(textEncryptor, keyProperties);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
// promote beans to context
|
||||
registry.addCloseListener(event -> {
|
||||
if (isLegacyBootstrap(event.getApplicationContext().getEnvironment())) {
|
||||
return;
|
||||
}
|
||||
BootstrapContext bootstrapContext = event.getBootstrapContext();
|
||||
KeyProperties keyProperties = bootstrapContext.get(KeyProperties.class);
|
||||
ConfigurableListableBeanFactory beanFactory = event.getApplicationContext().getBeanFactory();
|
||||
if (keyProperties != null) {
|
||||
beanFactory.registerSingleton("keyProperties", keyProperties);
|
||||
}
|
||||
if (RSA_IS_PRESENT) {
|
||||
RsaProperties rsaProperties = bootstrapContext.get(RsaProperties.class);
|
||||
if (rsaProperties != null) {
|
||||
beanFactory.registerSingleton("rsaProperties", rsaProperties);
|
||||
}
|
||||
}
|
||||
TextEncryptor textEncryptor = bootstrapContext.get(TextEncryptor.class);
|
||||
if (textEncryptor != null) {
|
||||
beanFactory.registerSingleton("textEncryptor", textEncryptor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static TextEncryptor rsaTextEncryptor(KeyProperties keyProperties, RsaProperties rsaProperties) {
|
||||
KeyProperties.KeyStore keyStore = keyProperties.getKeyStore();
|
||||
if (keyStore.getLocation() != null) {
|
||||
if (keyStore.getLocation().exists()) {
|
||||
return new RsaSecretEncryptor(
|
||||
new KeyStoreKeyFactory(keyStore.getLocation(), keyStore.getPassword().toCharArray())
|
||||
.getKeyPair(keyStore.getAlias(), keyStore.getSecret().toCharArray()),
|
||||
rsaProperties.getAlgorithm(), rsaProperties.getSalt(), rsaProperties.isStrong());
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Invalid keystore location");
|
||||
}
|
||||
|
||||
return new EncryptorFactory(keyProperties.getSalt()).create(keyProperties.getKey());
|
||||
}
|
||||
|
||||
public static boolean keysConfigured(KeyProperties properties) {
|
||||
if (hasProperty(properties.getKeyStore().getLocation())) {
|
||||
if (hasProperty(properties.getKeyStore().getPassword())) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else if (hasProperty(properties.getKey())) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static boolean hasProperty(Object value) {
|
||||
if (value instanceof String) {
|
||||
return StringUtils.hasText((String) value);
|
||||
}
|
||||
return value != null;
|
||||
}
|
||||
|
||||
static boolean isLegacyBootstrap(Environment environment) {
|
||||
boolean isLegacy = environment.getProperty("spring.config.use-legacy-processing", Boolean.class, false);
|
||||
boolean isBootstrapEnabled = environment.getProperty("spring.cloud.bootstrap.enabled", Boolean.class, false);
|
||||
return isLegacy || isBootstrapEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* TextEncryptor that just fails, so that users don't get a false sense of security
|
||||
* adding ciphers to config files and not getting them decrypted.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public static class FailsafeTextEncryptor implements TextEncryptor {
|
||||
|
||||
@Override
|
||||
public String encrypt(String text) {
|
||||
throw new UnsupportedOperationException(
|
||||
"No encryption for FailsafeTextEncryptor. Did you configure the keystore correctly?");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String decrypt(String encryptedText) {
|
||||
throw new UnsupportedOperationException(
|
||||
"No decryption for FailsafeTextEncryptor. Did you configure the keystore correctly?");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* Copyright 2013-2020 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.bootstrap.encrypt;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.core.env.CompositePropertySource;
|
||||
import org.springframework.core.env.EnumerablePropertySource;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.core.env.PropertySources;
|
||||
import org.springframework.security.crypto.encrypt.TextEncryptor;
|
||||
|
||||
/**
|
||||
* Abstract class that handles decrypting and merging of PropertySources.
|
||||
*/
|
||||
public class AbstractEnvironmentDecrypt {
|
||||
|
||||
private static final Pattern COLLECTION_PROPERTY = Pattern.compile("(\\S+)?\\[(\\d+)\\](\\.\\S+)?");
|
||||
|
||||
/**
|
||||
* Name of the decrypted property source.
|
||||
*/
|
||||
public static final String DECRYPTED_PROPERTY_SOURCE_NAME = "decrypted";
|
||||
|
||||
/**
|
||||
* Prefix indicating an encrypted value.
|
||||
*/
|
||||
public static final String ENCRYPTED_PROPERTY_PREFIX = "{cipher}";
|
||||
|
||||
protected Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private boolean failOnError = true;
|
||||
|
||||
/**
|
||||
* Strategy to determine how to handle exceptions during decryption.
|
||||
* @param failOnError the flag value (default true)
|
||||
*/
|
||||
public void setFailOnError(boolean failOnError) {
|
||||
this.failOnError = failOnError;
|
||||
}
|
||||
|
||||
public boolean isFailOnError() {
|
||||
return this.failOnError;
|
||||
}
|
||||
|
||||
protected Map<String, Object> decrypt(TextEncryptor encryptor, PropertySources propertySources) {
|
||||
Map<String, Object> properties = merge(propertySources);
|
||||
decrypt(encryptor, properties);
|
||||
return properties;
|
||||
}
|
||||
|
||||
protected Map<String, Object> merge(PropertySources propertySources) {
|
||||
Map<String, Object> properties = new LinkedHashMap<>();
|
||||
List<PropertySource<?>> sources = new ArrayList<>();
|
||||
for (PropertySource<?> source : propertySources) {
|
||||
sources.add(0, source);
|
||||
}
|
||||
for (PropertySource<?> source : sources) {
|
||||
merge(source, properties);
|
||||
}
|
||||
return properties;
|
||||
}
|
||||
|
||||
protected void merge(PropertySource<?> source, Map<String, Object> properties) {
|
||||
if (source instanceof CompositePropertySource) {
|
||||
|
||||
List<PropertySource<?>> sources = new ArrayList<>(((CompositePropertySource) source).getPropertySources());
|
||||
Collections.reverse(sources);
|
||||
|
||||
for (PropertySource<?> nested : sources) {
|
||||
merge(nested, properties);
|
||||
}
|
||||
|
||||
}
|
||||
else if (source instanceof EnumerablePropertySource) {
|
||||
Map<String, Object> otherCollectionProperties = new LinkedHashMap<>();
|
||||
boolean sourceHasDecryptedCollection = false;
|
||||
|
||||
EnumerablePropertySource<?> enumerable = (EnumerablePropertySource<?>) source;
|
||||
for (String key : enumerable.getPropertyNames()) {
|
||||
Object property = source.getProperty(key);
|
||||
if (property != null) {
|
||||
String value = property.toString();
|
||||
if (value.startsWith(ENCRYPTED_PROPERTY_PREFIX)) {
|
||||
properties.put(key, value);
|
||||
if (COLLECTION_PROPERTY.matcher(key).matches()) {
|
||||
sourceHasDecryptedCollection = true;
|
||||
}
|
||||
}
|
||||
else if (COLLECTION_PROPERTY.matcher(key).matches()) {
|
||||
// put non-encrypted properties so merging of index properties
|
||||
// happens correctly
|
||||
otherCollectionProperties.put(key, value);
|
||||
}
|
||||
else {
|
||||
// override previously encrypted with non-encrypted property
|
||||
properties.remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
// copy all indexed properties even if not encrypted
|
||||
if (sourceHasDecryptedCollection && !otherCollectionProperties.isEmpty()) {
|
||||
properties.putAll(otherCollectionProperties);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
protected void decrypt(TextEncryptor encryptor, Map<String, Object> properties) {
|
||||
properties.replaceAll((key, value) -> {
|
||||
String valueString = value.toString();
|
||||
if (!valueString.startsWith(ENCRYPTED_PROPERTY_PREFIX)) {
|
||||
return value;
|
||||
}
|
||||
return decrypt(encryptor, key, valueString);
|
||||
});
|
||||
}
|
||||
|
||||
protected String decrypt(TextEncryptor encryptor, String key, String original) {
|
||||
String value = original.substring(ENCRYPTED_PROPERTY_PREFIX.length());
|
||||
try {
|
||||
value = encryptor.decrypt(value);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Decrypted: key=" + key);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
catch (Exception e) {
|
||||
String message = "Cannot decrypt: key=" + key;
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.warn(message, e);
|
||||
}
|
||||
else {
|
||||
logger.warn(message);
|
||||
}
|
||||
if (this.failOnError) {
|
||||
throw new IllegalStateException(message, e);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 2013-2020 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
|
||||
*
|
||||
* https://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.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.bootstrap.encrypt;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.context.properties.bind.Binder;
|
||||
import org.springframework.boot.env.EnvironmentPostProcessor;
|
||||
import org.springframework.cloud.bootstrap.TextEncryptorConfigBootstrapper;
|
||||
import org.springframework.cloud.bootstrap.TextEncryptorConfigBootstrapper.FailsafeTextEncryptor;
|
||||
import org.springframework.cloud.context.encrypt.EncryptorFactory;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.MutablePropertySources;
|
||||
import org.springframework.core.env.SystemEnvironmentPropertySource;
|
||||
import org.springframework.security.crypto.encrypt.TextEncryptor;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import static org.springframework.cloud.bootstrap.TextEncryptorConfigBootstrapper.keysConfigured;
|
||||
import static org.springframework.cloud.util.PropertyUtils.bootstrapEnabled;
|
||||
import static org.springframework.cloud.util.PropertyUtils.useLegacyProcessing;
|
||||
|
||||
/**
|
||||
* Decrypt properties from the environment and insert them with high priority so they
|
||||
* override the encrypted values.
|
||||
*
|
||||
* @author Dave Syer
|
||||
* @author Tim Ysewyn
|
||||
*/
|
||||
public class DecryptEnvironmentPostProcessor extends AbstractEnvironmentDecrypt
|
||||
implements EnvironmentPostProcessor, Ordered {
|
||||
|
||||
private int order = Ordered.LOWEST_PRECEDENCE;
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return this.order;
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
|
||||
if (bootstrapEnabled(environment) || useLegacyProcessing(environment)) {
|
||||
return;
|
||||
}
|
||||
|
||||
MutablePropertySources propertySources = environment.getPropertySources();
|
||||
|
||||
environment.getPropertySources().remove(DECRYPTED_PROPERTY_SOURCE_NAME);
|
||||
|
||||
TextEncryptor encryptor = getTextEncryptor(environment);
|
||||
|
||||
Map<String, Object> map = decrypt(encryptor, propertySources);
|
||||
if (!map.isEmpty()) {
|
||||
// We have some decrypted properties
|
||||
propertySources.addFirst(new SystemEnvironmentPropertySource(DECRYPTED_PROPERTY_SOURCE_NAME, map));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected TextEncryptor getTextEncryptor(ConfigurableEnvironment environment) {
|
||||
Binder binder = Binder.get(environment);
|
||||
KeyProperties keyProperties = binder.bind(KeyProperties.PREFIX, KeyProperties.class)
|
||||
.orElseGet(KeyProperties::new);
|
||||
if (keysConfigured(keyProperties)) {
|
||||
|
||||
if (ClassUtils.isPresent("org.springframework.security.rsa.crypto.RsaSecretEncryptor", null)) {
|
||||
RsaProperties rsaProperties = binder.bind(RsaProperties.PREFIX, RsaProperties.class)
|
||||
.orElseGet(RsaProperties::new);
|
||||
return TextEncryptorConfigBootstrapper.rsaTextEncryptor(keyProperties, rsaProperties);
|
||||
}
|
||||
return new EncryptorFactory(keyProperties.getSalt()).create(keyProperties.getKey());
|
||||
}
|
||||
// no keys configured
|
||||
return new FailsafeTextEncryptor();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.cloud.bootstrap.encrypt;
|
||||
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
@@ -25,6 +26,7 @@ import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
|
||||
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.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ConditionContext;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
@@ -42,55 +44,58 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass({ TextEncryptor.class })
|
||||
@EnableConfigurationProperties({ KeyProperties.class })
|
||||
@EnableConfigurationProperties
|
||||
public class EncryptionBootstrapConfiguration {
|
||||
|
||||
@Autowired(required = false)
|
||||
private TextEncryptor encryptor;
|
||||
|
||||
@Autowired
|
||||
private KeyProperties key;
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public KeyProperties keyProperties() {
|
||||
return new KeyProperties();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public EnvironmentDecryptApplicationInitializer environmentDecryptApplicationListener() {
|
||||
if (this.encryptor == null) {
|
||||
this.encryptor = new FailsafeTextEncryptor();
|
||||
public EnvironmentDecryptApplicationInitializer environmentDecryptApplicationListener(
|
||||
ConfigurableApplicationContext context, KeyProperties keyProperties) {
|
||||
TextEncryptor encryptor;
|
||||
try {
|
||||
encryptor = context.getBean(TextEncryptor.class);
|
||||
}
|
||||
EnvironmentDecryptApplicationInitializer listener = new EnvironmentDecryptApplicationInitializer(
|
||||
this.encryptor);
|
||||
listener.setFailOnError(this.key.isFailOnError());
|
||||
catch (NoSuchBeanDefinitionException e) {
|
||||
encryptor = new FailsafeTextEncryptor();
|
||||
}
|
||||
EnvironmentDecryptApplicationInitializer listener = new EnvironmentDecryptApplicationInitializer(encryptor);
|
||||
listener.setFailOnError(keyProperties.isFailOnError());
|
||||
return listener;
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Conditional(KeyCondition.class)
|
||||
@ConditionalOnClass(RsaSecretEncryptor.class)
|
||||
@EnableConfigurationProperties({ RsaProperties.class })
|
||||
@EnableConfigurationProperties
|
||||
protected static class RsaEncryptionConfiguration {
|
||||
|
||||
@Autowired
|
||||
private KeyProperties key;
|
||||
|
||||
@Autowired
|
||||
private RsaProperties rsaProperties;
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public RsaProperties rsaProperties() {
|
||||
return new RsaProperties();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(TextEncryptor.class)
|
||||
public TextEncryptor textEncryptor() {
|
||||
KeyStore keyStore = this.key.getKeyStore();
|
||||
public TextEncryptor textEncryptor(RsaProperties rsaProperties, KeyProperties keyProperties) {
|
||||
KeyStore keyStore = keyProperties.getKeyStore();
|
||||
if (keyStore.getLocation() != null) {
|
||||
if (keyStore.getLocation().exists()) {
|
||||
return new RsaSecretEncryptor(
|
||||
new KeyStoreKeyFactory(keyStore.getLocation(), keyStore.getPassword().toCharArray())
|
||||
.getKeyPair(keyStore.getAlias(), keyStore.getSecret().toCharArray()),
|
||||
this.rsaProperties.getAlgorithm(), this.rsaProperties.getSalt(),
|
||||
this.rsaProperties.isStrong());
|
||||
rsaProperties.getAlgorithm(), rsaProperties.getSalt(), rsaProperties.isStrong());
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Invalid keystore location");
|
||||
}
|
||||
|
||||
return new EncryptorFactory(this.key.getSalt()).create(this.key.getKey());
|
||||
return new EncryptorFactory(keyProperties.getSalt()).create(keyProperties.getKey());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,17 +16,10 @@
|
||||
|
||||
package org.springframework.cloud.bootstrap.encrypt;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
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;
|
||||
@@ -34,15 +27,15 @@ import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextInitializer;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.env.CompositePropertySource;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.EnumerablePropertySource;
|
||||
import org.springframework.core.env.MutablePropertySources;
|
||||
import org.springframework.core.env.PropertySource;
|
||||
import org.springframework.core.env.PropertySources;
|
||||
import org.springframework.core.env.SystemEnvironmentPropertySource;
|
||||
import org.springframework.security.crypto.encrypt.TextEncryptor;
|
||||
|
||||
import static org.springframework.cloud.util.PropertyUtils.bootstrapEnabled;
|
||||
import static org.springframework.cloud.util.PropertyUtils.useLegacyProcessing;
|
||||
|
||||
/**
|
||||
* Decrypt properties from the environment and insert them with high priority so they
|
||||
* override the encrypted values.
|
||||
@@ -50,42 +43,18 @@ import org.springframework.security.crypto.encrypt.TextEncryptor;
|
||||
* @author Dave Syer
|
||||
* @author Tim Ysewyn
|
||||
*/
|
||||
public class EnvironmentDecryptApplicationInitializer
|
||||
public class EnvironmentDecryptApplicationInitializer extends AbstractEnvironmentDecrypt
|
||||
implements ApplicationContextInitializer<ConfigurableApplicationContext>, Ordered {
|
||||
|
||||
/**
|
||||
* Name of the decrypted property source.
|
||||
*/
|
||||
public static final String DECRYPTED_PROPERTY_SOURCE_NAME = "decrypted";
|
||||
|
||||
/**
|
||||
* Name of the decrypted bootstrap property source.
|
||||
*/
|
||||
public static final String DECRYPTED_BOOTSTRAP_PROPERTY_SOURCE_NAME = "decryptedBootstrap";
|
||||
|
||||
/**
|
||||
* Prefix indicating an encrypted value.
|
||||
*/
|
||||
public static final String ENCRYPTED_PROPERTY_PREFIX = "{cipher}";
|
||||
|
||||
private static final Pattern COLLECTION_PROPERTY = Pattern.compile("(\\S+)?\\[(\\d+)\\](\\.\\S+)?");
|
||||
|
||||
private static Log logger = LogFactory.getLog(EnvironmentDecryptApplicationInitializer.class);
|
||||
|
||||
private int order = Ordered.HIGHEST_PRECEDENCE + 15;
|
||||
|
||||
private TextEncryptor encryptor;
|
||||
|
||||
private boolean failOnError = true;
|
||||
|
||||
/**
|
||||
* Strategy to determine how to handle exceptions during decryption.
|
||||
* @param failOnError the flag value (default true)
|
||||
*/
|
||||
public void setFailOnError(boolean failOnError) {
|
||||
this.failOnError = failOnError;
|
||||
}
|
||||
|
||||
public EnvironmentDecryptApplicationInitializer(TextEncryptor encryptor) {
|
||||
this.encryptor = encryptor;
|
||||
}
|
||||
@@ -102,6 +71,10 @@ public class EnvironmentDecryptApplicationInitializer
|
||||
@Override
|
||||
public void initialize(ConfigurableApplicationContext applicationContext) {
|
||||
ConfigurableEnvironment environment = applicationContext.getEnvironment();
|
||||
if (!bootstrapEnabled(environment) && !useLegacyProcessing(environment)) {
|
||||
return;
|
||||
}
|
||||
|
||||
MutablePropertySources propertySources = environment.getPropertySources();
|
||||
|
||||
Set<String> found = new LinkedHashSet<>();
|
||||
@@ -119,7 +92,7 @@ public class EnvironmentDecryptApplicationInitializer
|
||||
}
|
||||
}
|
||||
removeDecryptedProperties(applicationContext);
|
||||
Map<String, Object> map = decrypt(propertySources);
|
||||
Map<String, Object> map = decrypt(this.encryptor, propertySources);
|
||||
if (!map.isEmpty()) {
|
||||
// We have some decrypted properties
|
||||
found.addAll(map.keySet());
|
||||
@@ -173,27 +146,9 @@ public class EnvironmentDecryptApplicationInitializer
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, Object> decrypt(PropertySources propertySources) {
|
||||
Map<String, Object> properties = merge(propertySources);
|
||||
decrypt(properties);
|
||||
return properties;
|
||||
}
|
||||
|
||||
private Map<String, Object> decrypt(PropertySource<?> source) {
|
||||
Map<String, Object> properties = merge(source);
|
||||
decrypt(properties);
|
||||
return properties;
|
||||
}
|
||||
|
||||
private Map<String, Object> merge(PropertySources propertySources) {
|
||||
Map<String, Object> properties = new LinkedHashMap<>();
|
||||
List<PropertySource<?>> sources = new ArrayList<>();
|
||||
for (PropertySource<?> source : propertySources) {
|
||||
sources.add(0, source);
|
||||
}
|
||||
for (PropertySource<?> source : sources) {
|
||||
merge(source, properties);
|
||||
}
|
||||
decrypt(this.encryptor, properties);
|
||||
return properties;
|
||||
}
|
||||
|
||||
@@ -203,83 +158,4 @@ public class EnvironmentDecryptApplicationInitializer
|
||||
return properties;
|
||||
}
|
||||
|
||||
private void merge(PropertySource<?> source, Map<String, Object> properties) {
|
||||
if (source instanceof CompositePropertySource) {
|
||||
|
||||
List<PropertySource<?>> sources = new ArrayList<>(((CompositePropertySource) source).getPropertySources());
|
||||
Collections.reverse(sources);
|
||||
|
||||
for (PropertySource<?> nested : sources) {
|
||||
merge(nested, properties);
|
||||
}
|
||||
|
||||
}
|
||||
else if (source instanceof EnumerablePropertySource) {
|
||||
Map<String, Object> otherCollectionProperties = new LinkedHashMap<>();
|
||||
boolean sourceHasDecryptedCollection = false;
|
||||
|
||||
EnumerablePropertySource<?> enumerable = (EnumerablePropertySource<?>) source;
|
||||
for (String key : enumerable.getPropertyNames()) {
|
||||
Object property = source.getProperty(key);
|
||||
if (property != null) {
|
||||
String value = property.toString();
|
||||
if (value.startsWith(ENCRYPTED_PROPERTY_PREFIX)) {
|
||||
properties.put(key, value);
|
||||
if (COLLECTION_PROPERTY.matcher(key).matches()) {
|
||||
sourceHasDecryptedCollection = true;
|
||||
}
|
||||
}
|
||||
else if (COLLECTION_PROPERTY.matcher(key).matches()) {
|
||||
// put non-encrypted properties so merging of index properties
|
||||
// happens correctly
|
||||
otherCollectionProperties.put(key, value);
|
||||
}
|
||||
else {
|
||||
// override previously encrypted with non-encrypted property
|
||||
properties.remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
// copy all indexed properties even if not encrypted
|
||||
if (sourceHasDecryptedCollection && !otherCollectionProperties.isEmpty()) {
|
||||
properties.putAll(otherCollectionProperties);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void decrypt(Map<String, Object> properties) {
|
||||
properties.replaceAll((key, value) -> {
|
||||
String valueString = value.toString();
|
||||
if (!valueString.startsWith(ENCRYPTED_PROPERTY_PREFIX)) {
|
||||
return value;
|
||||
}
|
||||
return decrypt(key, valueString);
|
||||
});
|
||||
}
|
||||
|
||||
private String decrypt(String key, String original) {
|
||||
String value = original.substring(ENCRYPTED_PROPERTY_PREFIX.length());
|
||||
try {
|
||||
value = this.encryptor.decrypt(value);
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Decrypted: key=" + key);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
catch (Exception e) {
|
||||
String message = "Cannot decrypt: key=" + key;
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.warn(message, e);
|
||||
}
|
||||
else {
|
||||
logger.warn(message);
|
||||
}
|
||||
if (this.failOnError) {
|
||||
throw new IllegalStateException(message, e);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,9 +24,14 @@ import org.springframework.core.io.Resource;
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@ConfigurationProperties("encrypt")
|
||||
@ConfigurationProperties(KeyProperties.PREFIX)
|
||||
public class KeyProperties {
|
||||
|
||||
/**
|
||||
* ConfigurationProperties prefix for KeyProperties.
|
||||
*/
|
||||
public static final String PREFIX = "encrypt";
|
||||
|
||||
/**
|
||||
* A symmetric key. As a stronger alternative, consider using a keystore.
|
||||
*/
|
||||
|
||||
@@ -24,9 +24,14 @@ import org.springframework.security.rsa.crypto.RsaAlgorithm;
|
||||
* @author Ryan Baxter
|
||||
*/
|
||||
@ConditionalOnClass(RsaAlgorithm.class)
|
||||
@ConfigurationProperties("encrypt.rsa")
|
||||
@ConfigurationProperties(RsaProperties.PREFIX)
|
||||
public class RsaProperties {
|
||||
|
||||
/**
|
||||
* ConfigurationProperties prefix for RsaProperties.
|
||||
*/
|
||||
public static final String PREFIX = "encrypt.rsa";
|
||||
|
||||
/**
|
||||
* The RSA algorithm to use (DEFAULT or OEAP). Once it is set, do not change it (or
|
||||
* existing ciphers will not be decryptable).
|
||||
|
||||
@@ -10,10 +10,16 @@ org.springframework.context.ApplicationListener=\
|
||||
org.springframework.cloud.bootstrap.BootstrapApplicationListener,\
|
||||
org.springframework.cloud.bootstrap.LoggingSystemShutdownListener,\
|
||||
org.springframework.cloud.context.restart.RestartListener
|
||||
# Bootstrap components
|
||||
# Spring Cloud Bootstrap components
|
||||
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
|
||||
org.springframework.cloud.bootstrap.config.PropertySourceBootstrapConfiguration,\
|
||||
org.springframework.cloud.bootstrap.encrypt.EncryptionBootstrapConfiguration,\
|
||||
org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
|
||||
org.springframework.cloud.util.random.CachedRandomPropertySourceAutoConfiguration
|
||||
# Spring Boot Bootstrappers
|
||||
org.springframework.boot.Bootstrapper=\
|
||||
org.springframework.cloud.bootstrap.TextEncryptorConfigBootstrapper
|
||||
# Environment Post Processors
|
||||
org.springframework.boot.env.EnvironmentPostProcessor=\
|
||||
org.springframework.cloud.bootstrap.encrypt.DecryptEnvironmentPostProcessor
|
||||
@@ -87,7 +87,7 @@ public class EncryptionBootstrapConfigurationTests {
|
||||
then(false).as("Should not create an application context with invalid keystore location").isTrue();
|
||||
}
|
||||
catch (Exception e) {
|
||||
then(e).hasRootCauseInstanceOf(IllegalStateException.class);
|
||||
then(e).isInstanceOf(IllegalStateException.class);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.springframework.cloud.bootstrap.encrypt;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.WebApplicationType;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
@@ -30,7 +31,7 @@ import static org.assertj.core.api.BDDAssertions.then;
|
||||
public class EncryptionIntegrationTests {
|
||||
|
||||
@Test
|
||||
public void symmetricPropertyValues() {
|
||||
public void legacySymmetricPropertyValues() {
|
||||
ConfigurableApplicationContext context = new SpringApplicationBuilder(TestConfiguration.class)
|
||||
.web(WebApplicationType.NONE).properties("spring.config.use-legacy-processing=true", "encrypt.key:pie",
|
||||
"foo.password:{cipher}bf29452295df354e6153c5b31b03ef23c70e55fba24299aa85c63438f1c43c95")
|
||||
@@ -39,7 +40,7 @@ public class EncryptionIntegrationTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void symmetricConfigurationProperties() {
|
||||
public void legacySymmetricConfigurationProperties() {
|
||||
ConfigurableApplicationContext context = new SpringApplicationBuilder(TestConfiguration.class)
|
||||
.web(WebApplicationType.NONE).properties("spring.config.use-legacy-processing=true", "encrypt.key:pie",
|
||||
"foo.password:{cipher}bf29452295df354e6153c5b31b03ef23c70e55fba24299aa85c63438f1c43c95")
|
||||
@@ -47,12 +48,55 @@ public class EncryptionIntegrationTests {
|
||||
then(context.getBean(PasswordProperties.class).getPassword()).isEqualTo("test");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void propSymmetricPropertyValues() {
|
||||
ConfigurableApplicationContext context = new SpringApplicationBuilder(TestConfiguration.class)
|
||||
.web(WebApplicationType.NONE).properties("spring.cloud.bootstrap.enabled=true", "encrypt.key:pie",
|
||||
"foo.password:{cipher}bf29452295df354e6153c5b31b03ef23c70e55fba24299aa85c63438f1c43c95")
|
||||
.run();
|
||||
then(context.getEnvironment().getProperty("foo.password")).isEqualTo("test");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void propSymmetricConfigurationProperties() {
|
||||
ConfigurableApplicationContext context = new SpringApplicationBuilder(TestConfiguration.class)
|
||||
.web(WebApplicationType.NONE).properties("spring.cloud.bootstrap.enabled=true", "encrypt.key:pie",
|
||||
"foo.password:{cipher}bf29452295df354e6153c5b31b03ef23c70e55fba24299aa85c63438f1c43c95")
|
||||
.run();
|
||||
then(context.getBean(PasswordProperties.class).getPassword()).isEqualTo("test");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void symmetricPropertyValues() {
|
||||
ConfigurableApplicationContext context = new SpringApplicationBuilder(TestAutoConfiguration.class)
|
||||
.web(WebApplicationType.NONE).properties("spring.config.use-legacy-processing=false", "encrypt.key:pie",
|
||||
"foo.password:{cipher}bf29452295df354e6153c5b31b03ef23c70e55fba24299aa85c63438f1c43c95")
|
||||
.run();
|
||||
then(context.getEnvironment().getProperty("foo.password")).isEqualTo("test");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void symmetricConfigurationProperties() {
|
||||
ConfigurableApplicationContext context = new SpringApplicationBuilder(TestAutoConfiguration.class)
|
||||
.web(WebApplicationType.NONE).properties("spring.config.use-legacy-processing=false", "encrypt.key:pie",
|
||||
"foo.password:{cipher}bf29452295df354e6153c5b31b03ef23c70e55fba24299aa85c63438f1c43c95")
|
||||
.run();
|
||||
then(context.getBean(PasswordProperties.class).getPassword()).isEqualTo("test");
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableConfigurationProperties(PasswordProperties.class)
|
||||
protected static class TestConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableAutoConfiguration
|
||||
@EnableConfigurationProperties(PasswordProperties.class)
|
||||
protected static class TestAutoConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@ConfigurationProperties("foo")
|
||||
protected static class PasswordProperties {
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ public class EnvironmentDecryptApplicationInitializerTests {
|
||||
@Test
|
||||
public void decryptCipherKey() {
|
||||
ConfigurableApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
TestPropertyValues.of("foo: {cipher}bar").applyTo(context);
|
||||
TestPropertyValues.of("spring.cloud.bootstrap.enabled=true", "foo: {cipher}bar").applyTo(context);
|
||||
this.listener.initialize(context);
|
||||
then(context.getEnvironment().getProperty("foo")).isEqualTo("bar");
|
||||
}
|
||||
@@ -72,8 +72,8 @@ public class EnvironmentDecryptApplicationInitializerTests {
|
||||
@Test
|
||||
public void relaxedBinding() {
|
||||
ConfigurableApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
TestPropertyValues.of("FOO_TEXT: {cipher}bar").applyTo(context.getEnvironment(),
|
||||
TestPropertyValues.Type.SYSTEM_ENVIRONMENT);
|
||||
TestPropertyValues.of("spring.cloud.bootstrap.enabled=true", "FOO_TEXT: {cipher}bar")
|
||||
.applyTo(context.getEnvironment(), TestPropertyValues.Type.SYSTEM_ENVIRONMENT);
|
||||
this.listener.initialize(context);
|
||||
then(context.getEnvironment().getProperty("foo.text")).isEqualTo("bar");
|
||||
}
|
||||
@@ -81,7 +81,7 @@ public class EnvironmentDecryptApplicationInitializerTests {
|
||||
@Test
|
||||
public void propertySourcesOrderedCorrectly() {
|
||||
ConfigurableApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
TestPropertyValues.of("foo: {cipher}bar").applyTo(context);
|
||||
TestPropertyValues.of("spring.cloud.bootstrap.enabled=true", "foo: {cipher}bar").applyTo(context);
|
||||
context.getEnvironment().getPropertySources()
|
||||
.addFirst(new MapPropertySource("test_override", Collections.singletonMap("foo", "{cipher}spam")));
|
||||
this.listener.initialize(context);
|
||||
@@ -92,7 +92,7 @@ public class EnvironmentDecryptApplicationInitializerTests {
|
||||
public void errorOnDecrypt() {
|
||||
this.listener = new EnvironmentDecryptApplicationInitializer(Encryptors.text("deadbeef", "AFFE37"));
|
||||
ConfigurableApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
TestPropertyValues.of("foo: {cipher}bar").applyTo(context);
|
||||
TestPropertyValues.of("spring.cloud.bootstrap.enabled=true", "foo: {cipher}bar").applyTo(context);
|
||||
// catch IllegalStateException and verify
|
||||
try {
|
||||
this.listener.initialize(context);
|
||||
@@ -110,7 +110,7 @@ public class EnvironmentDecryptApplicationInitializerTests {
|
||||
this.listener = new EnvironmentDecryptApplicationInitializer(Encryptors.text("deadbeef", "AFFE37"));
|
||||
this.listener.setFailOnError(false);
|
||||
ConfigurableApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
TestPropertyValues.of("foo: {cipher}bar").applyTo(context);
|
||||
TestPropertyValues.of("spring.cloud.bootstrap.enabled=true", "foo: {cipher}bar").applyTo(context);
|
||||
this.listener.initialize(context);
|
||||
// Assert logs contain warning
|
||||
String sysOutput = this.outputCapture.toString();
|
||||
@@ -125,7 +125,9 @@ public class EnvironmentDecryptApplicationInitializerTests {
|
||||
ConfigurableApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
// tests that collections in another property source don't get copied into
|
||||
// "decrypted" property source
|
||||
TestPropertyValues.of("yours[0].someValue: yourFoo", "yours[1].someValue: yourBar").applyTo(context);
|
||||
TestPropertyValues
|
||||
.of("spring.cloud.bootstrap.enabled=true", "yours[0].someValue: yourFoo", "yours[1].someValue: yourBar")
|
||||
.applyTo(context);
|
||||
|
||||
// collection with some encrypted keys and some not encrypted
|
||||
TestPropertyValues
|
||||
@@ -153,7 +155,7 @@ public class EnvironmentDecryptApplicationInitializerTests {
|
||||
EnvironmentDecryptApplicationInitializer initializer = new EnvironmentDecryptApplicationInitializer(
|
||||
Encryptors.noOpText());
|
||||
|
||||
TestPropertyValues.of("key:{cipher}value").applyTo(ctx);
|
||||
TestPropertyValues.of("spring.cloud.bootstrap.enabled=true", "key:{cipher}value").applyTo(ctx);
|
||||
|
||||
ApplicationContext ctxParent = mock(ApplicationContext.class);
|
||||
when(ctxParent.getEnvironment()).thenReturn(mock(Environment.class));
|
||||
@@ -168,6 +170,7 @@ public class EnvironmentDecryptApplicationInitializerTests {
|
||||
@Test
|
||||
public void testDecryptCompositePropertySource() {
|
||||
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext();
|
||||
TestPropertyValues.of("spring.cloud.bootstrap.enabled=true").applyTo(ctx);
|
||||
EnvironmentDecryptApplicationInitializer initializer = new EnvironmentDecryptApplicationInitializer(
|
||||
Encryptors.noOpText());
|
||||
|
||||
@@ -190,7 +193,7 @@ public class EnvironmentDecryptApplicationInitializerTests {
|
||||
@Test
|
||||
public void propertySourcesOrderedCorrectlyWithUnencryptedOverrides() {
|
||||
ConfigurableApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
TestPropertyValues.of("foo: {cipher}bar").applyTo(context);
|
||||
TestPropertyValues.of("spring.cloud.bootstrap.enabled=true", "foo: {cipher}bar").applyTo(context);
|
||||
context.getEnvironment().getPropertySources()
|
||||
.addFirst(new MapPropertySource("test_override", Collections.singletonMap("foo", "spam")));
|
||||
this.listener.initialize(context);
|
||||
@@ -208,6 +211,7 @@ public class EnvironmentDecryptApplicationInitializerTests {
|
||||
EnvironmentDecryptApplicationInitializer initializer = new EnvironmentDecryptApplicationInitializer(encryptor);
|
||||
|
||||
ConfigurableApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
TestPropertyValues.of("spring.cloud.bootstrap.enabled=true").applyTo(context);
|
||||
CompositePropertySource bootstrap = new CompositePropertySource(BOOTSTRAP_PROPERTY_SOURCE_NAME);
|
||||
bootstrap.addPropertySource(
|
||||
new MapPropertySource("configService", Collections.singletonMap("foo", "{cipher}bar")));
|
||||
@@ -248,7 +252,8 @@ public class EnvironmentDecryptApplicationInitializerTests {
|
||||
TextEncryptor encryptor = mock(TextEncryptor.class);
|
||||
when(encryptor.decrypt("bar2")).thenReturn("bar2");
|
||||
EnvironmentDecryptApplicationInitializer initializer = new EnvironmentDecryptApplicationInitializer(encryptor);
|
||||
TestPropertyValues.of("foo: {cipher}bar", "foo2: {cipher}bar2").applyTo(context);
|
||||
TestPropertyValues.of("spring.cloud.bootstrap.enabled=true", "foo: {cipher}bar", "foo2: {cipher}bar2")
|
||||
.applyTo(context);
|
||||
context.getEnvironment().getPropertySources()
|
||||
.addFirst(new MapPropertySource("test_override", Collections.singletonMap("foo", "spam")));
|
||||
initializer.initialize(context);
|
||||
|
||||
Reference in New Issue
Block a user