From 70a63018b2cb4e411ca2f27f89ca123180c7aca5 Mon Sep 17 00:00:00 2001 From: spencergibb Date: Sun, 20 Dec 2020 11:49:47 -0500 Subject: [PATCH] Uses BindHandler if registered in bootstrap. Spring Cloud Commons now creates a TextEncryptorBindHandler that can decrypt properties when using a Binder. This allows configuration read in ConfigData methods to be decrypted. Fixes gh-688 --- .../ConsulConfigDataLocationResolver.java | 26 ++++++++++++----- ...nfigDataCustomizationIntegrationTests.java | 29 +++++++++++++++++++ ...ConsulConfigDataLocationResolverTests.java | 8 ++++- 3 files changed, 54 insertions(+), 9 deletions(-) diff --git a/spring-cloud-consul-config/src/main/java/org/springframework/cloud/consul/config/ConsulConfigDataLocationResolver.java b/spring-cloud-consul-config/src/main/java/org/springframework/cloud/consul/config/ConsulConfigDataLocationResolver.java index d17d1b98..3637b52f 100644 --- a/spring-cloud-consul-config/src/main/java/org/springframework/cloud/consul/config/ConsulConfigDataLocationResolver.java +++ b/spring-cloud-consul-config/src/main/java/org/springframework/cloud/consul/config/ConsulConfigDataLocationResolver.java @@ -34,6 +34,7 @@ import org.springframework.boot.context.config.ConfigDataLocationNotFoundExcepti import org.springframework.boot.context.config.ConfigDataLocationResolver; import org.springframework.boot.context.config.ConfigDataLocationResolverContext; import org.springframework.boot.context.config.Profiles; +import org.springframework.boot.context.properties.bind.BindHandler; import org.springframework.boot.context.properties.bind.Bindable; import org.springframework.boot.context.properties.bind.Binder; import org.springframework.cloud.consul.ConsulAutoConfiguration; @@ -89,12 +90,12 @@ public class ConsulConfigDataLocationResolver implements ConfigDataLocationResol UriComponents locationUri = parseLocation(resolverContext, location); // create consul client - registerBean(resolverContext, ConsulProperties.class, loadProperties(resolverContext.getBinder(), locationUri)); + registerBean(resolverContext, ConsulProperties.class, loadProperties(resolverContext, locationUri)); registerAndPromoteBean(resolverContext, ConsulClient.class, this::createConsulClient); // create locations - ConsulConfigProperties properties = loadConfigProperties(resolverContext.getBinder()); + ConsulConfigProperties properties = loadConfigProperties(resolverContext); ConsulPropertySources consulPropertySources = new ConsulPropertySources(properties, log); @@ -111,6 +112,10 @@ public class ConsulConfigDataLocationResolver implements ConfigDataLocationResol location.isOptional(), properties, consulPropertySources)).collect(Collectors.toList()); } + private BindHandler getBindHandler(ConfigDataLocationResolverContext context) { + return context.getBootstrapContext().getOrElse(BindHandler.class, null); + } + private List getCustomContexts(UriComponents uriComponents, ConsulConfigProperties properties) { if (StringUtils.isEmpty(uriComponents.getPath())) { return Collections.emptyList(); @@ -178,9 +183,12 @@ public class ConsulConfigDataLocationResolver implements ConfigDataLocationResol return ConsulAutoConfiguration.createConsulClient(properties); } - protected ConsulProperties loadProperties(Binder binder, UriComponents location) { - ConsulProperties consulProperties = binder.bind(ConsulProperties.PREFIX, Bindable.of(ConsulProperties.class)) - .orElse(new ConsulProperties()); + protected ConsulProperties loadProperties(ConfigDataLocationResolverContext resolverContext, + UriComponents location) { + Binder binder = resolverContext.getBinder(); + ConsulProperties consulProperties = binder + .bind(ConsulProperties.PREFIX, Bindable.of(ConsulProperties.class), getBindHandler(resolverContext)) + .orElseGet(ConsulProperties::new); if (location != null) { if (StringUtils.hasText(location.getHost())) { @@ -194,10 +202,12 @@ public class ConsulConfigDataLocationResolver implements ConfigDataLocationResol return consulProperties; } - protected ConsulConfigProperties loadConfigProperties(Binder binder) { + protected ConsulConfigProperties loadConfigProperties(ConfigDataLocationResolverContext resolverContext) { + Binder binder = resolverContext.getBinder(); + BindHandler bindHandler = getBindHandler(resolverContext); ConsulConfigProperties properties = binder - .bind(ConsulConfigProperties.PREFIX, Bindable.of(ConsulConfigProperties.class)) - .orElse(new ConsulConfigProperties()); + .bind(ConsulConfigProperties.PREFIX, Bindable.of(ConsulConfigProperties.class), bindHandler) + .orElseGet(ConsulConfigProperties::new); if (StringUtils.isEmpty(properties.getName())) { properties.setName(binder.bind("spring.application.name", String.class).orElse("application")); diff --git a/spring-cloud-consul-config/src/test/java/org/springframework/cloud/consul/config/ConsulConfigDataCustomizationIntegrationTests.java b/spring-cloud-consul-config/src/test/java/org/springframework/cloud/consul/config/ConsulConfigDataCustomizationIntegrationTests.java index 324f3e3e..ec54a75f 100644 --- a/spring-cloud-consul-config/src/test/java/org/springframework/cloud/consul/config/ConsulConfigDataCustomizationIntegrationTests.java +++ b/spring-cloud-consul-config/src/test/java/org/springframework/cloud/consul/config/ConsulConfigDataCustomizationIntegrationTests.java @@ -23,9 +23,15 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.springframework.boot.BootstrapRegistry; +import org.springframework.boot.Bootstrapper; import org.springframework.boot.SpringApplication; import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.context.properties.bind.BindContext; +import org.springframework.boot.context.properties.bind.BindHandler; +import org.springframework.boot.context.properties.bind.Bindable; +import org.springframework.boot.context.properties.source.ConfigurationPropertyName; import org.springframework.cloud.consul.ConsulProperties; import org.springframework.cloud.consul.test.ConsulTestcontainers; import org.springframework.context.ConfigurableApplicationContext; @@ -48,12 +54,16 @@ public class ConsulConfigDataCustomizationIntegrationTests { private static ConfigurableApplicationContext context; + private static BindHandlerBootstrapper bindHandlerBootstrapper; + @BeforeAll public static void setup() { ConsulTestcontainers.start(); SpringApplication application = new SpringApplication(Config.class); application.setWebApplicationType(WebApplicationType.NONE); + bindHandlerBootstrapper = new BindHandlerBootstrapper(); + application.addBootstrapper(bindHandlerBootstrapper); application.addBootstrapper(ConsulBootstrapper.fromConsulProperties(TestConsulClient::new)); context = application.run("--spring.application.name=" + APP_NAME, "--spring.config.import=consul:" + ConsulTestcontainers.getHost() + ":" @@ -73,6 +83,7 @@ public class ConsulConfigDataCustomizationIntegrationTests { public void consulClientIsCustom() { ConsulClient client = context.getBean(ConsulClient.class); assertThat(client).isInstanceOf(TestConsulClient.class); + assertThat(bindHandlerBootstrapper.onSuccessCount).isGreaterThan(0); } static class TestConsulClient extends ConsulClient { @@ -89,4 +100,22 @@ public class ConsulConfigDataCustomizationIntegrationTests { } + static class BindHandlerBootstrapper implements Bootstrapper { + + private int onSuccessCount = 0; + + @Override + public void intitialize(BootstrapRegistry registry) { + registry.register(BindHandler.class, context -> new BindHandler() { + @Override + public Object onSuccess(ConfigurationPropertyName name, Bindable target, BindContext context, + Object result) { + onSuccessCount++; + return result; + } + }); + } + + } + } diff --git a/spring-cloud-consul-config/src/test/java/org/springframework/cloud/consul/config/ConsulConfigDataLocationResolverTests.java b/spring-cloud-consul-config/src/test/java/org/springframework/cloud/consul/config/ConsulConfigDataLocationResolverTests.java index e54cbd87..30436792 100644 --- a/spring-cloud-consul-config/src/test/java/org/springframework/cloud/consul/config/ConsulConfigDataLocationResolverTests.java +++ b/spring-cloud-consul-config/src/test/java/org/springframework/cloud/consul/config/ConsulConfigDataLocationResolverTests.java @@ -24,6 +24,7 @@ import org.apache.commons.logging.LogFactory; import org.junit.jupiter.api.Test; import org.springframework.boot.BootstrapRegistry.InstanceSupplier; +import org.springframework.boot.DefaultBootstrapContext; import org.springframework.boot.context.config.ConfigDataLocation; import org.springframework.boot.context.config.ConfigDataLocationResolverContext; import org.springframework.boot.context.config.Profiles; @@ -70,7 +71,11 @@ public class ConsulConfigDataLocationResolverTests { @Test public void testLoadProperties() { - ConsulProperties properties = createResolver().loadProperties(Binder.get(new MockEnvironment()), + Binder binder = Binder.get(new MockEnvironment()); + ConfigDataLocationResolverContext resolverContext = mock(ConfigDataLocationResolverContext.class); + when(resolverContext.getBinder()).thenReturn(binder); + when(resolverContext.getBootstrapContext()).thenReturn(new DefaultBootstrapContext()); + ConsulProperties properties = createResolver().loadProperties(resolverContext, UriComponentsBuilder.fromUriString("consul://myhost:8502").build()); assertThat(properties.getHost()).isEqualTo("myhost"); assertThat(properties.getPort()).isEqualTo(8502); @@ -83,6 +88,7 @@ public class ConsulConfigDataLocationResolverTests { private List testResolveProfileSpecific(String location) { ConsulConfigDataLocationResolver resolver = createResolver(); ConfigDataLocationResolverContext context = mock(ConfigDataLocationResolverContext.class); + when(context.getBootstrapContext()).thenReturn(new DefaultBootstrapContext()); MockEnvironment env = new MockEnvironment(); env.setProperty("spring.application.name", "testapp"); when(context.getBinder()).thenReturn(Binder.get(env));