Workaround issue caused by org.springframework.cloud:spring-cloud-services-starter-service-registry.

Resolves gh-21.
This commit is contained in:
John Blum
2019-01-29 10:29:22 -08:00
parent 795da35cde
commit 2e263257f2
2 changed files with 204 additions and 43 deletions

View File

@@ -21,6 +21,8 @@ import java.util.Properties;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.client.ClientCache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
@@ -35,14 +37,13 @@ import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.data.gemfire.client.ClientCacheFactoryBean;
import org.springframework.data.gemfire.config.annotation.EnableSecurity;
import org.springframework.data.gemfire.config.annotation.support.AutoConfiguredAuthenticationInitializer;
import org.springframework.geode.core.env.VcapPropertySource;
import org.springframework.geode.core.env.support.CloudCacheService;
import org.springframework.geode.core.env.support.Service;
import org.springframework.lang.Nullable;
/**
* Spring Boot {@link EnableAutoConfiguration auto-configuration} enabling Apache Geode's Security functionality,
@@ -78,10 +79,12 @@ import org.springframework.geode.core.env.support.Service;
@SuppressWarnings("unused")
public class ClientSecurityAutoConfiguration {
public static final String SECURITY_CLOUD_ENVIRONMENT_POST_PROCESSOR_ENABLED_PROPERTY =
public static final String CLOUD_SECURITY_ENVIRONMENT_POST_PROCESSOR_ENABLED_PROPERTY =
"spring.boot.data.gemfire.security.auth.environment.post-processor.enabled";
private static final String CLOUD_CACHE_PROPERTY_SOURCE_NAME = "cloudcache-configuration";
private static final Logger logger = LoggerFactory.getLogger(ClientSecurityAutoConfiguration.class);
private static final String CLOUD_CACHE_PROPERTY_SOURCE_NAME = "boot.data.gemfire.cloudcache";
private static final String MANAGEMENT_HTTP_HOST_PROPERTY = "spring.data.gemfire.management.http.host";
private static final String MANAGEMENT_HTTP_PORT_PROPERTY = "spring.data.gemfire.management.http.port";
@@ -109,17 +112,32 @@ public class ClientSecurityAutoConfiguration {
}
private boolean isCloudFoundryEnvironment(Environment environment) {
return Optional.ofNullable(environment).filter(CloudPlatform.CLOUD_FOUNDRY::isActive).isPresent();
return Optional.ofNullable(environment)
.filter(CloudPlatform.CLOUD_FOUNDRY::isActive)
.isPresent();
}
private boolean isEnabled(Environment environment) {
return environment.getProperty(SECURITY_CLOUD_ENVIRONMENT_POST_PROCESSOR_ENABLED_PROPERTY,
Boolean.class, true);
boolean clientSecurityAutoConfigurationEnabled =
environment.getProperty(CLOUD_SECURITY_ENVIRONMENT_POST_PROCESSOR_ENABLED_PROPERTY,
Boolean.class, true);
logger.debug("{} enabled {}", ClientSecurityAutoConfiguration.class.getSimpleName(),
clientSecurityAutoConfigurationEnabled);
return clientSecurityAutoConfigurationEnabled;
}
private boolean isSecurityPropertiesSet(Environment environment) {
return environment.containsProperty(SECURITY_USERNAME_PROPERTY)
boolean securityPropertiesSet = environment.containsProperty(SECURITY_USERNAME_PROPERTY)
&& environment.containsProperty(SECURITY_PASSWORD_PROPERTY);
logger.debug("Security Properties set {}", securityPropertiesSet);
return securityPropertiesSet;
}
private boolean isSecurityPropertiesNotSet(Environment environment) {
@@ -127,28 +145,30 @@ public class ClientSecurityAutoConfiguration {
}
private void configureAuthentication(Environment environment, Properties cloudCacheProperties,
VcapPropertySource propertySource, Service cloudCache) {
VcapPropertySource vcapPropertySource, CloudCacheService cloudCacheService) {
propertySource.findFirstUserByRoleClusterOperator(cloudCache)
vcapPropertySource.findFirstUserByRoleClusterOperator(cloudCacheService)
.filter(user -> isSecurityPropertiesNotSet(environment))
.ifPresent(user -> {
cloudCacheProperties.setProperty(SECURITY_USERNAME_PROPERTY, user.getName());
user.getPassword().ifPresent(password ->
cloudCacheProperties.setProperty(SECURITY_PASSWORD_PROPERTY, password));
});
}
private void configureLocators(Environment environment, Properties cloudCacheProperties,
VcapPropertySource propertySource, CloudCacheService cloudCache) {
VcapPropertySource vcapPropertySource, CloudCacheService cloudCacheService) {
cloudCache.getLocators().ifPresent(locators ->
cloudCacheService.getLocators().ifPresent(locators ->
cloudCacheProperties.setProperty(POOL_LOCATORS_PROPERTY, locators));
}
private void configureManagementRestApiAccess(Environment environment, Properties cloudCacheProperties,
VcapPropertySource propertySource, CloudCacheService cloudCache) {
VcapPropertySource vcapPropertySource, CloudCacheService cloudCacheService) {
cloudCache.getGfshUrl().ifPresent(url -> {
cloudCacheService.getGfshUrl().ifPresent(url -> {
cloudCacheProperties.setProperty(MANAGEMENT_USE_HTTP_PROPERTY, Boolean.TRUE.toString());
cloudCacheProperties.setProperty(MANAGEMENT_HTTP_HOST_PROPERTY, url.getHost());
cloudCacheProperties.setProperty(MANAGEMENT_HTTP_PORT_PROPERTY, String.valueOf(url.getPort()));
@@ -157,22 +177,27 @@ public class ClientSecurityAutoConfiguration {
public void configureSecurityContext(ConfigurableEnvironment environment) {
VcapPropertySource propertySource = VcapPropertySource.from(environment);
VcapPropertySource vcapPropertySource = toVcapPropertySource(environment);
Properties cloudCacheProperties = new Properties();
CloudCacheService cloudCache = propertySource.findFirstCloudCacheService();
CloudCacheService cloudCacheService = vcapPropertySource.findFirstCloudCacheService();
configureAuthentication(environment, cloudCacheProperties, propertySource, cloudCache);
configureLocators(environment, cloudCacheProperties, propertySource, cloudCache);
configureManagementRestApiAccess(environment, cloudCacheProperties, propertySource, cloudCache);
configureAuthentication(environment, cloudCacheProperties, vcapPropertySource, cloudCacheService);
configureLocators(environment, cloudCacheProperties, vcapPropertySource, cloudCacheService);
configureManagementRestApiAccess(environment, cloudCacheProperties, vcapPropertySource, cloudCacheService);
environment.getPropertySources()
.addFirst(newPropertySource(CLOUD_CACHE_PROPERTY_SOURCE_NAME, cloudCacheProperties));
.addLast(newPropertySource(CLOUD_CACHE_PROPERTY_SOURCE_NAME, cloudCacheProperties));
}
private PropertySource<?> newPropertySource(String name, Properties properties) {
return new PropertiesPropertySource(name, properties);
//return new PropertiesPropertySource(name, properties);
return new SpringDataGemFirePropertiesPropertySource(name, properties);
}
private VcapPropertySource toVcapPropertySource(Environment environment) {
return VcapPropertySource.from(environment);
}
}
@@ -198,4 +223,31 @@ public class ClientSecurityAutoConfiguration {
static class StandaloneApacheGeodeSecurityContextCondition { }
}
// This custom PropertySource is required to prevent Pivotal Spring Cloud Services
// (spring-cloud-services-starter-service-registry) from losing the GemFire/PCC Security Context credentials
// stored in the Environment.
static class SpringDataGemFirePropertiesPropertySource extends PropertySource<Properties> {
private static final String SPRING_DATA_GEMFIRE_PROPERTIES_PROPERTY_SOURCE_NAME =
"spring.data.gemfire.properties";
SpringDataGemFirePropertiesPropertySource(Properties springDataGemFireProperties) {
this(SPRING_DATA_GEMFIRE_PROPERTIES_PROPERTY_SOURCE_NAME, springDataGemFireProperties);
}
SpringDataGemFirePropertiesPropertySource(String name, Properties springDataGemFireProperties) {
super(name, springDataGemFireProperties);
}
@Nullable @Override @SuppressWarnings("all")
public Object getProperty(String name) {
return getSource().getProperty(name);
}
@Override @SuppressWarnings("all")
public boolean containsProperty(String name) {
return getSource().containsKey(name);
}
}
}

View File

@@ -16,6 +16,7 @@
package org.springframework.geode.boot.autoconfigure.security.auth;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
@@ -27,11 +28,16 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.util.Properties;
import org.junit.Test;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.StandardEnvironment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.geode.boot.autoconfigure.ClientSecurityAutoConfiguration;
import org.springframework.geode.boot.autoconfigure.ClientSecurityAutoConfiguration.AutoConfiguredCloudSecurityEnvironmentPostProcessor;
import org.springframework.mock.env.MockEnvironment;
/**
* Unit Tests for {@link ClientSecurityAutoConfiguration}.
@@ -55,15 +61,15 @@ public class ClientSecurityAutoConfigurationUnitTests {
ConfigurableEnvironment mockEnvironment = mock(ConfigurableEnvironment.class);
when(mockEnvironment.getProperty(eq(ClientSecurityAutoConfiguration.SECURITY_CLOUD_ENVIRONMENT_POST_PROCESSOR_ENABLED_PROPERTY),
eq(Boolean.class), eq(true))).thenReturn(true);
when(mockEnvironment.containsProperty(eq("VCAP_APPLICATION"))).thenReturn(true);
when(mockEnvironment.getProperty(eq(ClientSecurityAutoConfiguration.CLOUD_SECURITY_ENVIRONMENT_POST_PROCESSOR_ENABLED_PROPERTY),
eq(Boolean.class), eq(true))).thenReturn(true);
environmentPostProcessor.postProcessEnvironment(mockEnvironment, null);
verify(mockEnvironment, times(1))
.getProperty(eq(ClientSecurityAutoConfiguration.SECURITY_CLOUD_ENVIRONMENT_POST_PROCESSOR_ENABLED_PROPERTY),
.getProperty(eq(ClientSecurityAutoConfiguration.CLOUD_SECURITY_ENVIRONMENT_POST_PROCESSOR_ENABLED_PROPERTY),
eq(Boolean.class), eq(true));
verify(mockEnvironment, times(1)).containsProperty(eq("VCAP_APPLICATION"));
@@ -82,21 +88,20 @@ public class ClientSecurityAutoConfigurationUnitTests {
doNothing().when(environmentPostProcessor).configureSecurityContext(any(ConfigurableEnvironment.class));
ConfigurableEnvironment environment = spy(new StandardEnvironment());
ConfigurableEnvironment mockEnvironment = spy(new MockEnvironment());
doReturn(true).when(environment).containsProperty(eq("VCAP_SERVICES"));
doReturn(true).when(mockEnvironment).containsProperty(eq("VCAP_SERVICES"));
environmentPostProcessor.postProcessEnvironment(environment, null);
environmentPostProcessor.postProcessEnvironment(mockEnvironment, null);
verify(environment, times(1))
.getProperty(eq(ClientSecurityAutoConfiguration.SECURITY_CLOUD_ENVIRONMENT_POST_PROCESSOR_ENABLED_PROPERTY),
verify(mockEnvironment, times(1))
.getProperty(eq(ClientSecurityAutoConfiguration.CLOUD_SECURITY_ENVIRONMENT_POST_PROCESSOR_ENABLED_PROPERTY),
eq(Boolean.class), eq(true));
verify(environment, times(1)).containsProperty(eq("VCAP_APPLICATION"));
verify(environment, times(1)).containsProperty(eq("VCAP_SERVICES"));
verify(mockEnvironment, times(1)).containsProperty(eq("VCAP_APPLICATION"));
verify(mockEnvironment, times(1)).containsProperty(eq("VCAP_SERVICES"));
verify(environmentPostProcessor, times(1))
.configureSecurityContext(eq(environment));
verify(environmentPostProcessor, times(1)).configureSecurityContext(eq(mockEnvironment));
}
@Test
@@ -109,16 +114,16 @@ public class ClientSecurityAutoConfigurationUnitTests {
ConfigurableEnvironment mockEnvironment = mock(ConfigurableEnvironment.class);
when(mockEnvironment.getProperty(eq(ClientSecurityAutoConfiguration.SECURITY_CLOUD_ENVIRONMENT_POST_PROCESSOR_ENABLED_PROPERTY),
eq(Boolean.class), eq(true))).thenReturn(false);
when(mockEnvironment.containsProperty(eq("VCAP_APPLICATION"))).thenReturn(true);
when(mockEnvironment.containsProperty(eq("VCAP_SERVICES"))).thenReturn(true);
when(mockEnvironment.getProperty(eq(ClientSecurityAutoConfiguration.CLOUD_SECURITY_ENVIRONMENT_POST_PROCESSOR_ENABLED_PROPERTY),
eq(Boolean.class), eq(true))).thenReturn(false);
environmentPostProcessor.postProcessEnvironment(mockEnvironment, null);
verify(mockEnvironment, times(1))
.getProperty(eq(ClientSecurityAutoConfiguration.SECURITY_CLOUD_ENVIRONMENT_POST_PROCESSOR_ENABLED_PROPERTY),
.getProperty(eq(ClientSecurityAutoConfiguration.CLOUD_SECURITY_ENVIRONMENT_POST_PROCESSOR_ENABLED_PROPERTY),
eq(Boolean.class), eq(true));
verify(mockEnvironment, never()).containsProperty(eq("VCAP_APPLICATION"));
@@ -136,20 +141,124 @@ public class ClientSecurityAutoConfigurationUnitTests {
ConfigurableEnvironment mockEnvironment = mock(ConfigurableEnvironment.class);
when(mockEnvironment.getProperty(eq(ClientSecurityAutoConfiguration.SECURITY_CLOUD_ENVIRONMENT_POST_PROCESSOR_ENABLED_PROPERTY),
eq(Boolean.class), eq(true))).thenReturn(true);
when(mockEnvironment.containsProperty(eq("VCAP_APPLICATION"))).thenReturn(false);
when(mockEnvironment.containsProperty(eq("VCAP_SERVICES"))).thenReturn(false);
when(mockEnvironment.getProperty(eq(ClientSecurityAutoConfiguration.CLOUD_SECURITY_ENVIRONMENT_POST_PROCESSOR_ENABLED_PROPERTY),
eq(Boolean.class), eq(true))).thenReturn(true);
environmentPostProcessor.postProcessEnvironment(mockEnvironment, null);
verify(mockEnvironment, times(1))
.getProperty(eq(ClientSecurityAutoConfiguration.SECURITY_CLOUD_ENVIRONMENT_POST_PROCESSOR_ENABLED_PROPERTY),
.getProperty(eq(ClientSecurityAutoConfiguration.CLOUD_SECURITY_ENVIRONMENT_POST_PROCESSOR_ENABLED_PROPERTY),
eq(Boolean.class), eq(true));
verify(mockEnvironment, times(1)).containsProperty(eq("VCAP_APPLICATION"));
verify(mockEnvironment, times(1)).containsProperty(eq("VCAP_SERVICES"));
verify(environmentPostProcessor, never()).configureSecurityContext(eq(mockEnvironment));
}
@Test
@SuppressWarnings("unchecked")
public void configuresSecurityContext() {
ConfigurableEnvironment mockEnvironment = mock(ConfigurableEnvironment.class);
Properties vcapProperties = new Properties();
vcapProperties.setProperty("vcap.application.name", "TestApp");
vcapProperties.setProperty("vcap.application.uris", "test-app.apps.cloud.skullbox.com");
vcapProperties.setProperty("vcap.services.test-pcc.credentials.locators", "boombox[10334],skullbox[10334]");
vcapProperties.setProperty("vcap.services.test-pcc.credentials.urls.gfsh", "https://cloud.skullbox.com:8080/gemfire/v1");
vcapProperties.setProperty("vcap.services.test-pcc.credentials.urls.pulse", "https://cloud.skullbox.com:8080/pulse");
vcapProperties.setProperty("vcap.services.test-pcc.credentials.users[0].username", "Abuser");
vcapProperties.setProperty("vcap.services.test-pcc.credentials.users[0].password", "p@55w0rd");
vcapProperties.setProperty("vcap.services.test-pcc.credentials.users[0].roles", "cluster_developer");
vcapProperties.setProperty("vcap.services.test-pcc.credentials.users[1].username", "Master");
vcapProperties.setProperty("vcap.services.test-pcc.credentials.users[1].password", "p@$$w0rd");
vcapProperties.setProperty("vcap.services.test-pcc.credentials.users[1].roles", "cluster_operator");
vcapProperties.setProperty("vcap.services.test-pcc.tags", "gemfire,cloudcache,test,geode");
PropertySource vcapPropertySource = new PropertiesPropertySource("vcap", vcapProperties);
MutablePropertySources propertySources = new MutablePropertySources();
propertySources.addFirst(vcapPropertySource);
when(mockEnvironment.getPropertySources()).thenReturn(propertySources);
AutoConfiguredCloudSecurityEnvironmentPostProcessor environmentPostProcessor =
spy(new AutoConfiguredCloudSecurityEnvironmentPostProcessor());
environmentPostProcessor.configureSecurityContext(mockEnvironment);
verify(mockEnvironment, times(2)).getPropertySources();
assertThat(propertySources.contains("boot.data.gemfire.cloudcache")).isTrue();
PropertySource propertySource = propertySources.get("boot.data.gemfire.cloudcache");
assertThat(propertySource).isNotNull();
assertThat(propertySource.getName()).isEqualTo("boot.data.gemfire.cloudcache");
assertThat(propertySource.getProperty("spring.data.gemfire.security.username")).isEqualTo("Master");
assertThat(propertySource.getProperty("spring.data.gemfire.security.password")).isEqualTo("p@$$w0rd");
assertThat(propertySource.getProperty("spring.data.gemfire.pool.locators"))
.isEqualTo("boombox[10334],skullbox[10334]");
assertThat(propertySource.getProperty("spring.data.gemfire.management.use-http")).isEqualTo("true");
assertThat(propertySource.getProperty("spring.data.gemfire.management.http.host")).isEqualTo("cloud.skullbox.com");
assertThat(propertySource.getProperty("spring.data.gemfire.management.http.port")).isEqualTo("8080");
}
@Test
@SuppressWarnings("unchecked")
public void configuresSecurityContextWithLocatorsOnly() {
ConfigurableEnvironment mockEnvironment = mock(ConfigurableEnvironment.class);
when(mockEnvironment.containsProperty("spring.data.gemfire.security.username")).thenReturn(true);
when(mockEnvironment.containsProperty("spring.data.gemfire.security.password")).thenReturn(true);
Properties vcapProperties = new Properties();
vcapProperties.setProperty("vcap.application.name", "TestApp");
vcapProperties.setProperty("vcap.application.uris", "test-app.apps.cloud.skullbox.com");
vcapProperties.setProperty("vcap.services.test-pcc.credentials.locators", "boombox[10334],skullbox[10334]");
vcapProperties.setProperty("vcap.services.test-pcc.credentials.urls.pulse", "https://cloud.skullbox.com:8080/pulse");
vcapProperties.setProperty("vcap.services.test-pcc.credentials.users[0].username", "Abuser");
vcapProperties.setProperty("vcap.services.test-pcc.credentials.users[0].password", "p@55w0rd");
vcapProperties.setProperty("vcap.services.test-pcc.credentials.users[0].roles", "cluster_developer");
vcapProperties.setProperty("vcap.services.test-pcc.credentials.users[1].username", "Master");
vcapProperties.setProperty("vcap.services.test-pcc.credentials.users[1].password", "p@$$w0rd");
vcapProperties.setProperty("vcap.services.test-pcc.credentials.users[1].roles", "cluster_operator");
vcapProperties.setProperty("vcap.services.test-pcc.tags", "gemfire,cloudcache,test");
PropertySource vcapPropertySource = new PropertiesPropertySource("vcap", vcapProperties);
MutablePropertySources propertySources = new MutablePropertySources();
propertySources.addFirst(vcapPropertySource);
when(mockEnvironment.getPropertySources()).thenReturn(propertySources);
AutoConfiguredCloudSecurityEnvironmentPostProcessor environmentPostProcessor =
spy(new AutoConfiguredCloudSecurityEnvironmentPostProcessor());
environmentPostProcessor.configureSecurityContext(mockEnvironment);
verify(mockEnvironment, times(2)).getPropertySources();
assertThat(propertySources.contains("boot.data.gemfire.cloudcache")).isTrue();
PropertySource propertySource = propertySources.get("boot.data.gemfire.cloudcache");
assertThat(propertySource).isNotNull();
assertThat(propertySource.getName()).isEqualTo("boot.data.gemfire.cloudcache");
assertThat(propertySource.containsProperty("spring.data.gemfire.security.username")).isFalse();
assertThat(propertySource.containsProperty("spring.data.gemfire.security.password")).isFalse();
assertThat(propertySource.getProperty("spring.data.gemfire.pool.locators"))
.isEqualTo("boombox[10334],skullbox[10334]");
assertThat(propertySource.containsProperty("spring.data.gemfire.management.use-http")).isFalse();
assertThat(propertySource.containsProperty("spring.data.gemfire.management.http.host")).isFalse();
assertThat(propertySource.containsProperty("spring.data.gemfire.management.http.port")).isFalse();
}
}