Skip to content

Commit

Permalink
Add the ability for FailsafeTextEncryptor to have a delegate. (#1349)
Browse files Browse the repository at this point in the history
Related to spring-cloud/spring-cloud-config#2330

Co-authored-by: Ryan Baxter <[email protected]>
  • Loading branch information
Ryan Baxter and ryanjbaxter authored Apr 5, 2024
1 parent e120edb commit 77d221d
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -177,14 +177,40 @@ public static boolean isLegacyBootstrap(Environment environment) {
*/
public static class FailsafeTextEncryptor implements TextEncryptor {

private TextEncryptor delegate;

/**
* You can set a delegate that can be used to encrypt/decrypt values if later on
* after the initial initialization of the app we have the necessary values to
* create a proper {@link TextEncryptor}. Depending on where the encryption keys
* are set we might not have the right values to create a {@link TextEncryptor}
* (this can happen if the keys are in application.properties for example, but we
* create the text encryptor during Bootstrap). The delegate functionality allows
* us the option to set the delegate later on when we have the necessary values.
* @param delegate The TextEncryptor to use for encryption/decryption
*/
public void setDelegate(TextEncryptor delegate) {
this.delegate = delegate;
}

public TextEncryptor getDelegate() {
return this.delegate;
}

@Override
public String encrypt(String text) {
if (this.delegate != null) {
return this.delegate.encrypt(text);
}
throw new UnsupportedOperationException(
"No encryption for FailsafeTextEncryptor. Did you configure the keystore correctly?");
}

@Override
public String decrypt(String encryptedText) {
if (this.delegate != null) {
return this.delegate.decrypt(encryptedText);
}
throw new UnsupportedOperationException(
"No decryption for FailsafeTextEncryptor. Did you configure the keystore correctly?");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,28 @@ public void decryptAfterRefresh() {
TestConfigDataLocationResolver.config.clear();
}

@Test
public void failsafeTextEncryptor() {
ConfigurableApplicationContext context = new SpringApplicationBuilder(
EncryptionIntegrationTests.TestConfiguration.class).web(WebApplicationType.NONE).properties().run();
then(context.getBean(TextEncryptor.class)).isInstanceOf(TextEncryptorUtils.FailsafeTextEncryptor.class);
}

@Test
public void failsafeShouldHaveDelegate() {
TestConfigDataLocationResolver.config.put("foo.password",
"{cipher}bf29452295df354e6153c5b31b03ef23c70e55fba24299aa85c63438f1c43c95");
ConfigurableApplicationContext context = new SpringApplicationBuilder(TestAutoConfiguration.class)
.web(WebApplicationType.NONE)
.properties("spring.config.import=testdatasource:,classpath:application-failsafe.properties",
"createfailsafedelegate=true")
.run();
ConfigurableEnvironment env = context.getBean(ConfigurableEnvironment.class);
then(env.getProperty("foo.password")).isEqualTo("test");
context.close();
TestConfigDataLocationResolver.config.clear();
}

@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(PasswordProperties.class)
protected static class TestConfiguration {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,15 @@
import org.springframework.boot.context.config.ConfigDataLocationResolverContext;
import org.springframework.boot.context.config.ConfigDataResourceNotFoundException;
import org.springframework.boot.context.properties.bind.Bindable;
import org.springframework.cloud.bootstrap.encrypt.KeyProperties;
import org.springframework.cloud.bootstrap.encrypt.RsaProperties;
import org.springframework.cloud.bootstrap.encrypt.TextEncryptorUtils;
import org.springframework.core.Ordered;
import org.springframework.security.crypto.encrypt.TextEncryptor;

public class TestConfigDataLocationResolver implements ConfigDataLocationResolver<TestConfigDataResource> {
import static org.assertj.core.api.Assertions.assertThat;

public class TestConfigDataLocationResolver implements ConfigDataLocationResolver<TestConfigDataResource>, Ordered {

public static AtomicInteger count = new AtomicInteger(1);

Expand All @@ -52,6 +59,20 @@ public List<TestConfigDataResource> resolve(ConfigDataLocationResolverContext co
context.getBootstrapContext().registerIfAbsent(aClass, supplier);
}
String myplaceholder = context.getBinder().bind("myplaceholder", Bindable.of(String.class)).orElse("notfound");
boolean createFailsafeDelegate = context.getBinder().bind("createfailsafedelegate", Bindable.of(Boolean.class))
.orElse(Boolean.FALSE);
if (createFailsafeDelegate) {
assertThat(context.getBootstrapContext().isRegistered(TextEncryptor.class)).isTrue();
TextEncryptor textEncryptor = context.getBootstrapContext().get(TextEncryptor.class);
assertThat(textEncryptor).isInstanceOf(TextEncryptorUtils.FailsafeTextEncryptor.class);
KeyProperties keyProperties = context.getBinder().bindOrCreate(KeyProperties.PREFIX,
Bindable.of(KeyProperties.class));
assertThat(TextEncryptorUtils.keysConfigured(keyProperties)).isTrue();
RsaProperties rsaProperties = context.getBinder().bindOrCreate(RsaProperties.PREFIX,
Bindable.of(RsaProperties.class));
((TextEncryptorUtils.FailsafeTextEncryptor) textEncryptor)
.setDelegate(TextEncryptorUtils.createTextEncryptor(keyProperties, rsaProperties));
}
HashMap<String, Object> props = new HashMap<>(config);
props.put(TestEnvPostProcessor.EPP_VALUE, count.get());
if (count.get() == 99 && myplaceholder.contains("${vcap")) {
Expand All @@ -61,4 +82,9 @@ public List<TestConfigDataResource> resolve(ConfigDataLocationResolverContext co
return Collections.singletonList(new TestConfigDataResource(props));
}

@Override
public int getOrder() {
return -1;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
encrypt.key=pie

0 comments on commit 77d221d

Please sign in to comment.