/*
* Copyright 2013-2017 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.
*/
package org.springframework.cloud.bootstrap.encrypt;
import java.util.Collections;
import java.util.Map;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import org.springframework.security.crypto.encrypt.Encryptors;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.springframework.boot.test.util.EnvironmentTestUtils.addEnvironment;
import static org.springframework.cloud.bootstrap.encrypt.EnvironmentDecryptApplicationInitializer.DECRYPTED_PROPERTY_SOURCE_NAME;
/**
* @author Dave Syer
* @author Biju Kunjummen
*/
public class EnvironmentDecryptApplicationInitializerTests {
private EnvironmentDecryptApplicationInitializer listener = new EnvironmentDecryptApplicationInitializer(
Encryptors.noOpText());
@Test
public void decryptCipherKey() {
ConfigurableApplicationContext context = new AnnotationConfigApplicationContext();
addEnvironment(context, "foo: {cipher}bar");
this.listener.initialize(context);
assertEquals("bar", context.getEnvironment().getProperty("foo"));
}
@Test
public void relaxedBinding() {
ConfigurableApplicationContext context = new AnnotationConfigApplicationContext();
addEnvironment(context, "FOO_TEXT: {cipher}bar");
this.listener.initialize(context);
assertEquals("bar", context.getEnvironment().getProperty("foo.text"));
}
@Test
public void propertySourcesOrderedCorrectly() {
ConfigurableApplicationContext context = new AnnotationConfigApplicationContext();
addEnvironment(context, "foo: {cipher}bar");
context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(
"test_override",
Collections.<String, Object> singletonMap("foo", "{cipher}spam")));
this.listener.initialize(context);
assertEquals("spam", context.getEnvironment().getProperty("foo"));
}
@Test(expected = IllegalStateException.class)
public void errorOnDecrypt() {
this.listener = new EnvironmentDecryptApplicationInitializer(
Encryptors.text("deadbeef", "AFFE37"));
ConfigurableApplicationContext context = new AnnotationConfigApplicationContext();
addEnvironment(context, "foo: {cipher}bar");
this.listener.initialize(context);
assertEquals("bar", context.getEnvironment().getProperty("foo"));
}
@Test
public void errorOnDecryptWithEmpty() {
this.listener = new EnvironmentDecryptApplicationInitializer(
Encryptors.text("deadbeef", "AFFE37"));
this.listener.setFailOnError(false);
ConfigurableApplicationContext context = new AnnotationConfigApplicationContext();
addEnvironment(context, "foo: {cipher}bar");
this.listener.initialize(context);
// Empty is safest fallback for undecryptable cipher
assertEquals("", context.getEnvironment().getProperty("foo"));
}
@Test
@SuppressWarnings("unchecked")
public void indexedPropertiesCopied() {
ConfigurableApplicationContext context = new AnnotationConfigApplicationContext();
// tests that collections in another property source don't get copied into "decrypted" property source
addEnvironment(context, "yours[0].someValue: yourFoo", "yours[1].someValue: yourBar");
// collection with some encrypted keys and some not encrypted
addEnvironment("combinedTest", context.getEnvironment(), "mine[0].someValue: Foo",
"mine[0].someKey: {cipher}Foo0", "mine[1].someValue: Bar",
"mine[1].someKey: {cipher}Bar1", "nonindexed: nonindexval");
this.listener.initialize(context);
assertEquals("Foo", context.getEnvironment().getProperty("mine[0].someValue"));
assertEquals("Foo0", context.getEnvironment().getProperty("mine[0].someKey"));
assertEquals("Bar", context.getEnvironment().getProperty("mine[1].someValue"));
assertEquals("Bar1", context.getEnvironment().getProperty("mine[1].someKey"));
assertEquals("yourFoo", context.getEnvironment().getProperty("yours[0].someValue"));
assertEquals("yourBar", context.getEnvironment().getProperty("yours[1].someValue"));
MutablePropertySources propertySources = context.getEnvironment().getPropertySources();
PropertySource<Map> decrypted = (PropertySource<Map>) propertySources.get(DECRYPTED_PROPERTY_SOURCE_NAME);
assertThat("decrypted property source had wrong size", decrypted.getSource().size(), is(4));
}
@Test
public void testDecryptNonStandardParent() {
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext();
EnvironmentDecryptApplicationInitializer initializer =
new EnvironmentDecryptApplicationInitializer(Encryptors.noOpText());
addEnvironment(ctx, "key:{cipher}value");
ApplicationContext ctxParent = mock(ApplicationContext.class);
when(ctxParent.getEnvironment()).thenReturn(mock(Environment.class));
ctx.setParent(ctxParent);
initializer.initialize(ctx);
assertEquals("value", ctx.getEnvironment().getProperty("key"));
}
}