Add functionality to target the configuration of a specific CloudCache Service Instance in PCF.

Introduces the new 'spring.boot.data.gemfire.cloud.cloudfoundry.service.cloudcache.name' property enabling users to target a specific CloudCache Service Instance in PCF.

Fails fast and throws an IllegalStateException if the target CloudCache Service Instance cannot be found.

Logs warning when no CloudCache Service Instance is found.  However, client security auto-configuration will no longer require a CloudCache Service Instance when deploying Spring Boot, GemFire ClientCache app to PCF.  This enables users to deploy Spring Boot, ClientCache apps to PCF and connect them to external Apache Geode or Pivotal GemFire clusters.

Resolves gh-33.
This commit is contained in:
John Blum
2019-06-28 18:50:00 -07:00
parent 208ef38b19
commit f4feb248ea
2 changed files with 136 additions and 23 deletions

View File

@@ -15,6 +15,8 @@
*/
package org.springframework.geode.boot.autoconfigure;
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalStateException;
import java.util.Optional;
import java.util.Properties;
@@ -42,6 +44,7 @@ import org.springframework.data.gemfire.config.annotation.support.AutoConfigured
import org.springframework.geode.core.env.VcapPropertySource;
import org.springframework.geode.core.env.support.CloudCacheService;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -56,18 +59,18 @@ import org.slf4j.LoggerFactory;
* @see org.apache.geode.cache.client.ClientCache
* @see org.springframework.boot.SpringApplication
* @see org.springframework.boot.autoconfigure.EnableAutoConfiguration
* @see org.springframework.boot.autoconfigure.condition.ConditionalOnCloudPlatform
* @see org.springframework.boot.cloud.CloudPlatform
* @see org.springframework.boot.env.EnvironmentPostProcessor
* @see org.springframework.context.annotation.Conditional
* @see org.springframework.context.annotation.Configuration
* @see org.springframework.context.annotation.Import
* @see org.springframework.core.env.ConfigurableEnvironment
* @see org.springframework.core.env.Environment
* @see org.springframework.core.env.PropertiesPropertySource
* @see org.springframework.core.env.PropertySource
* @see org.springframework.data.gemfire.client.ClientCacheFactoryBean
* @see org.springframework.data.gemfire.config.annotation.EnableSecurity
* @see org.springframework.data.gemfire.config.annotation.support.AutoConfiguredAuthenticationInitializer
* @see org.springframework.geode.boot.autoconfigure.ClientCacheAutoConfiguration
* @see org.springframework.geode.boot.autoconfigure.HttpBasicAuthenticationSecurityConfiguration
* @see org.springframework.geode.core.env.VcapPropertySource
* @see org.springframework.geode.core.env.support.CloudCacheService
* @see org.springframework.geode.core.env.support.Service
@@ -83,6 +86,9 @@ import org.slf4j.LoggerFactory;
@SuppressWarnings("unused")
public class ClientSecurityAutoConfiguration {
public static final String CLOUD_CACHE_SERVICE_INSTANCE_NAME_PROPERTY =
"spring.boot.data.gemfire.cloud.cloudfoundry.service.cloudcache.name";
public static final String CLOUD_SECURITY_ENVIRONMENT_POST_PROCESSOR_ENABLED_PROPERTY =
"spring.boot.data.gemfire.security.auth.environment.post-processor.enabled";
@@ -116,7 +122,7 @@ public class ClientSecurityAutoConfiguration {
}
private boolean isCloudFoundryEnvironment(Environment environment) {
return environment != null && CloudPlatform.CLOUD_FOUNDRY.isActive(environment);
return CloudPlatform.CLOUD_FOUNDRY.isActive(environment);
}
private boolean isEnabled(Environment environment) {
@@ -145,8 +151,8 @@ public class ClientSecurityAutoConfiguration {
return !isSecurityPropertiesSet(environment);
}
private void configureAuthentication(Environment environment, Properties cloudCacheProperties,
VcapPropertySource vcapPropertySource, CloudCacheService cloudCacheService) {
private void configureAuthentication(Environment environment, VcapPropertySource vcapPropertySource,
CloudCacheService cloudCacheService, Properties cloudCacheProperties) {
vcapPropertySource.findFirstUserByRoleClusterOperator(cloudCacheService)
.filter(user -> isSecurityPropertiesNotSet(environment))
@@ -159,15 +165,15 @@ public class ClientSecurityAutoConfiguration {
});
}
private void configureLocators(Environment environment, Properties cloudCacheProperties,
VcapPropertySource vcapPropertySource, CloudCacheService cloudCacheService) {
private void configureLocators(Environment environment, VcapPropertySource vcapPropertySource,
CloudCacheService cloudCacheService, Properties cloudCacheProperties) {
cloudCacheService.getLocators().ifPresent(locators ->
cloudCacheProperties.setProperty(POOL_LOCATORS_PROPERTY, locators));
}
private void configureManagementRestApiAccess(Environment environment, Properties cloudCacheProperties,
VcapPropertySource vcapPropertySource, CloudCacheService cloudCacheService) {
private void configureManagementRestApiAccess(Environment environment, VcapPropertySource vcapPropertySource,
CloudCacheService cloudCacheService, Properties cloudCacheProperties) {
cloudCacheService.getGfshUrl().ifPresent(url -> {
cloudCacheProperties.setProperty(MANAGEMENT_USE_HTTP_PROPERTY, Boolean.TRUE.toString());
@@ -178,22 +184,41 @@ public class ClientSecurityAutoConfiguration {
public void configureSecurityContext(ConfigurableEnvironment environment) {
VcapPropertySource vcapPropertySource = toVcapPropertySource(environment);
String cloudcacheServiceInstanceName = environment.getProperty(CLOUD_CACHE_SERVICE_INSTANCE_NAME_PROPERTY);
Properties cloudCacheProperties = new Properties();
VcapPropertySource vcapPropertySource = StringUtils.hasText(cloudcacheServiceInstanceName)
? toVcapPropertySource(environment).withVcapServiceName(cloudcacheServiceInstanceName)
: toVcapPropertySource(environment);
CloudCacheService cloudCacheService = vcapPropertySource.findFirstCloudCacheService();
vcapPropertySource.findFirstCloudCacheService()
.map(cloudCacheService -> {
configureAuthentication(environment, cloudCacheProperties, vcapPropertySource, cloudCacheService);
configureLocators(environment, cloudCacheProperties, vcapPropertySource, cloudCacheService);
configureManagementRestApiAccess(environment, cloudCacheProperties, vcapPropertySource, cloudCacheService);
Properties cloudCacheProperties = new Properties();
environment.getPropertySources()
.addLast(newPropertySource(CLOUD_CACHE_PROPERTY_SOURCE_NAME, cloudCacheProperties));
configureAuthentication(environment, vcapPropertySource, cloudCacheService, cloudCacheProperties);
configureLocators(environment, vcapPropertySource, cloudCacheService, cloudCacheProperties);
configureManagementRestApiAccess(environment, vcapPropertySource, cloudCacheService, cloudCacheProperties);
environment.getPropertySources()
.addLast(newPropertySource(CLOUD_CACHE_PROPERTY_SOURCE_NAME, cloudCacheProperties));
return cloudCacheService;
})
.orElseGet(() -> {
if (StringUtils.hasText(cloudcacheServiceInstanceName)) {
throw newIllegalStateException("No CloudCache Service Instance with name [%s] was found",
cloudcacheServiceInstanceName);
}
else {
logger.warn("No CloudCache Service Instance was found");
}
return null;
});
}
private PropertySource<?> newPropertySource(String name, Properties properties) {
//return new PropertiesPropertySource(name, properties);
return new SpringDataGemFirePropertiesPropertySource(name, properties);
}
@@ -241,12 +266,12 @@ public class ClientSecurityAutoConfiguration {
super(name, springDataGemFireProperties);
}
@Nullable @Override @SuppressWarnings("all")
@Nullable @Override
public Object getProperty(String name) {
return getSource().getProperty(name);
}
@Override @SuppressWarnings("all")
@Override
public boolean containsProperty(String name) {
return getSource().containsKey(name);
}

View File

@@ -17,6 +17,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.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
@@ -35,6 +36,7 @@ import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.data.gemfire.tests.logging.slf4j.logback.TestAppender;
import org.springframework.geode.boot.autoconfigure.ClientSecurityAutoConfiguration;
import org.springframework.geode.boot.autoconfigure.ClientSecurityAutoConfiguration.AutoConfiguredCloudSecurityEnvironmentPostProcessor;
import org.springframework.mock.env.MockEnvironment;
@@ -43,10 +45,16 @@ import org.springframework.mock.env.MockEnvironment;
* Unit Tests for {@link ClientSecurityAutoConfiguration}.
*
* @author John Blum
* @see java.util.Properties
* @see org.junit.Test
* @see org.mockito.Mockito
* @see org.springframework.core.env.ConfigurableEnvironment
* @see org.springframework.core.env.MutablePropertySources
* @see org.springframework.core.env.PropertiesPropertySource
* @see org.springframework.core.env.PropertySource
* @see org.springframework.data.gemfire.tests.logging.slf4j.logback.TestAppender
* @see org.springframework.geode.boot.autoconfigure.ClientSecurityAutoConfiguration
* @see org.springframework.mock.env.MockEnvironment
* @since 1.0.0
*/
public class ClientSecurityAutoConfigurationUnitTests {
@@ -159,7 +167,6 @@ public class ClientSecurityAutoConfigurationUnitTests {
}
@Test
@SuppressWarnings("unchecked")
public void configuresSecurityContext() {
ConfigurableEnvironment mockEnvironment = mock(ConfigurableEnvironment.class);
@@ -210,7 +217,6 @@ public class ClientSecurityAutoConfigurationUnitTests {
}
@Test
@SuppressWarnings("unchecked")
public void configuresSecurityContextWithLocatorsOnly() {
ConfigurableEnvironment mockEnvironment = mock(ConfigurableEnvironment.class);
@@ -261,4 +267,86 @@ public class ClientSecurityAutoConfigurationUnitTests {
assertThat(propertySource.containsProperty("spring.data.gemfire.management.http.host")).isFalse();
assertThat(propertySource.containsProperty("spring.data.gemfire.management.http.port")).isFalse();
}
@Test
public void configureSecurityContextWhenNoCloudCacheServiceInstanceIsFoundLogsWarning() {
ConfigurableEnvironment mockEnvironment = mock(ConfigurableEnvironment.class);
MutablePropertySources propertySources = spy(new MutablePropertySources());
Properties vcap = new Properties();
vcap.setProperty("vcap.application.name", "TestApp");
vcap.setProperty("vcap.application.uris", "test-app.apps.cloud.skullbox.com");
PropertySource vcapPropertySource = new PropertiesPropertySource("vcap", vcap);
propertySources.addFirst(vcapPropertySource);
when(mockEnvironment.getPropertySources()).thenReturn(propertySources);
when(mockEnvironment.getProperty(anyString())).thenReturn(null);
AutoConfiguredCloudSecurityEnvironmentPostProcessor environmentPostProcessor =
new AutoConfiguredCloudSecurityEnvironmentPostProcessor();
try {
environmentPostProcessor.configureSecurityContext(mockEnvironment);
TestAppender testAppender = TestAppender.getInstance();
assertThat(testAppender).isNotNull();
assertThat(testAppender.lastLogMessage()).isEqualTo("No CloudCache Service Instance was found");
}
finally {
verify(mockEnvironment, times(1))
.getProperty(eq(ClientSecurityAutoConfiguration.CLOUD_CACHE_SERVICE_INSTANCE_NAME_PROPERTY));
verify(mockEnvironment, times(1)).getPropertySources();
verify(propertySources, never()).addLast(any(PropertySource.class));
}
}
@Test(expected = IllegalStateException.class)
public void configureSecurityContextWhenTargetCloudCacheServiceInstanceIsNotFoundThrowsIllegalStateException() {
ConfigurableEnvironment mockEnvironment = mock(ConfigurableEnvironment.class);
MutablePropertySources propertySources = spy(new MutablePropertySources());
Properties vcap = new Properties();
vcap.setProperty("vcap.application.name", "TestApp");
vcap.setProperty("vcap.application.uris", "test-app.apps.cloud.skullbox.com");
PropertySource vcapPropertySource = new PropertiesPropertySource("vcap", vcap);
propertySources.addFirst(vcapPropertySource);
when(mockEnvironment.getPropertySources()).thenReturn(propertySources);
when(mockEnvironment.getProperty(ClientSecurityAutoConfiguration.CLOUD_CACHE_SERVICE_INSTANCE_NAME_PROPERTY))
.thenReturn("test-pcc");
AutoConfiguredCloudSecurityEnvironmentPostProcessor environmentPostProcessor =
new AutoConfiguredCloudSecurityEnvironmentPostProcessor();
try {
environmentPostProcessor.configureSecurityContext(mockEnvironment);
}
catch (IllegalStateException expected) {
assertThat(expected).hasMessage("No CloudCache Service Instance with name [test-pcc] was found");
assertThat(expected).hasNoCause();
throw expected;
}
finally {
verify(mockEnvironment, times(1))
.getProperty(eq(ClientSecurityAutoConfiguration.CLOUD_CACHE_SERVICE_INSTANCE_NAME_PROPERTY));
verify(mockEnvironment, times(1)).getPropertySources();
verify(propertySources, never()).addLast(any(PropertySource.class));
}
}
}