Improve Client Security Auto-configuration to allow users to configure the runtime user of their Spring Boot applications running in Pivotal CloudFoundray (PCF) when using Pivotal Cloud Cache (PCC).

Note: configuring the runtime user of a Spring Boot app outside of CloudFoundry is already supported.  Simply set the 'spring.data.gemfire.security.username' and 'spring.data.gemfire.security.password' properties.

Resolves gh-44.
This commit is contained in:
John Blum
2019-09-10 14:25:36 -07:00
parent 55cef7e7ee
commit e1017d482f
2 changed files with 235 additions and 24 deletions

View File

@@ -47,6 +47,7 @@ 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.User;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
@@ -79,6 +80,7 @@ import org.springframework.util.StringUtils;
* @see org.springframework.geode.core.env.VcapPropertySource
* @see org.springframework.geode.core.env.support.CloudCacheService
* @see org.springframework.geode.core.env.support.Service
* @see org.springframework.geode.core.env.support.User
* @since 1.0.0
*/
@Configuration
@@ -136,7 +138,7 @@ public class ClientSecurityAutoConfiguration {
environment.getProperty(CLOUD_SECURITY_ENVIRONMENT_POST_PROCESSOR_ENABLED_PROPERTY,
Boolean.class, true);
logger.debug("{} enabled {}", ClientSecurityAutoConfiguration.class.getSimpleName(),
logger.debug("{} enabled? [{}]", ClientSecurityAutoConfiguration.class.getSimpleName(),
clientSecurityAutoConfigurationEnabled);
return clientSecurityAutoConfigurationEnabled;
@@ -147,7 +149,7 @@ public class ClientSecurityAutoConfiguration {
boolean securityPropertiesSet = environment.containsProperty(SECURITY_USERNAME_PROPERTY)
&& environment.containsProperty(SECURITY_PASSWORD_PROPERTY);
logger.debug("Security Properties set {}", securityPropertiesSet);
logger.debug("Security Properties set? [{}]", securityPropertiesSet);
return securityPropertiesSet;
}
@@ -159,15 +161,36 @@ public class ClientSecurityAutoConfiguration {
private void configureAuthentication(Environment environment, VcapPropertySource vcapPropertySource,
CloudCacheService cloudCacheService, Properties cloudCacheProperties) {
vcapPropertySource.findFirstUserByRoleClusterOperator(cloudCacheService)
.filter(user -> isSecurityPropertiesNotSet(environment))
.ifPresent(user -> {
if (isSecurityPropertiesNotSet(environment)) {
if (environment.containsProperty(SECURITY_USERNAME_PROPERTY)) {
cloudCacheProperties.setProperty(SECURITY_USERNAME_PROPERTY, user.getName());
String targetUsername = environment.getProperty(SECURITY_USERNAME_PROPERTY);
user.getPassword().ifPresent(password ->
cloudCacheProperties.setProperty(SECURITY_PASSWORD_PROPERTY, password));
});
vcapPropertySource.findUserByName(cloudCacheService, targetUsername)
.flatMap(User::getPassword)
.map(password -> {
cloudCacheProperties.setProperty(SECURITY_USERNAME_PROPERTY, targetUsername);
cloudCacheProperties.setProperty(SECURITY_PASSWORD_PROPERTY, password);
return password;
})
.orElseThrow(() -> newIllegalStateException(
"No User with name [%s] was configured for Cloud Cache service [%s]",
targetUsername, cloudCacheService.getName()));
}
else {
vcapPropertySource.findFirstUserByRoleClusterOperator(cloudCacheService)
.ifPresent(user -> {
cloudCacheProperties.setProperty(SECURITY_USERNAME_PROPERTY, user.getName());
user.getPassword().ifPresent(password ->
cloudCacheProperties.setProperty(SECURITY_PASSWORD_PROPERTY, password));
});
}
}
}
private void configureLocators(Environment environment, VcapPropertySource vcapPropertySource,
@@ -212,11 +235,11 @@ public class ClientSecurityAutoConfiguration {
.orElseGet(() -> {
if (StringUtils.hasText(cloudcacheServiceInstanceName)) {
throw newIllegalStateException("No CloudCache Service Instance with name [%s] was found",
throw newIllegalStateException("No Cloud Cache service instance with name [%s] was found",
cloudcacheServiceInstanceName);
}
else {
logger.warn("No CloudCache Service Instance was found");
logger.warn("No Cloud Cache service instance was found");
}
return null;

View File

@@ -36,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.core.env.StandardEnvironment;
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;
@@ -184,7 +185,8 @@ public class ClientSecurityAutoConfigurationUnitTests {
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");
vcapProperties.setProperty("vcap.services.test-pcc.name", "test-pcc");
vcapProperties.setProperty("vcap.services.test-pcc.tags", "gemfire,cloudcache,test,geode,pivotal");
PropertySource vcapPropertySource = new PropertiesPropertySource("vcap", vcapProperties);
@@ -224,6 +226,8 @@ public class ClientSecurityAutoConfigurationUnitTests {
when(mockEnvironment.containsProperty("spring.data.gemfire.security.username")).thenReturn(true);
when(mockEnvironment.containsProperty("spring.data.gemfire.security.password")).thenReturn(true);
MutablePropertySources propertySources = new MutablePropertySources();
Properties vcapProperties = new Properties();
vcapProperties.setProperty("vcap.application.name", "TestApp");
@@ -240,8 +244,6 @@ public class ClientSecurityAutoConfigurationUnitTests {
PropertySource vcapPropertySource = new PropertiesPropertySource("vcap", vcapProperties);
MutablePropertySources propertySources = new MutablePropertySources();
propertySources.addFirst(vcapPropertySource);
when(mockEnvironment.getPropertySources()).thenReturn(propertySources);
@@ -275,12 +277,12 @@ public class ClientSecurityAutoConfigurationUnitTests {
MutablePropertySources propertySources = spy(new MutablePropertySources());
Properties vcap = new Properties();
Properties vcapProperties = new Properties();
vcap.setProperty("vcap.application.name", "TestApp");
vcap.setProperty("vcap.application.uris", "test-app.apps.cloud.skullbox.com");
vcapProperties.setProperty("vcap.application.name", "TestApp");
vcapProperties.setProperty("vcap.application.uris", "test-app.apps.cloud.skullbox.com");
PropertySource vcapPropertySource = new PropertiesPropertySource("vcap", vcap);
PropertySource vcapPropertySource = new PropertiesPropertySource("vcap", vcapProperties);
propertySources.addFirst(vcapPropertySource);
@@ -297,7 +299,7 @@ public class ClientSecurityAutoConfigurationUnitTests {
TestAppender testAppender = TestAppender.getInstance();
assertThat(testAppender).isNotNull();
assertThat(testAppender.lastLogMessage()).isEqualTo("No CloudCache Service Instance was found");
assertThat(testAppender.lastLogMessage()).isEqualTo("No Cloud Cache service instance was found");
}
finally {
@@ -315,12 +317,12 @@ public class ClientSecurityAutoConfigurationUnitTests {
MutablePropertySources propertySources = spy(new MutablePropertySources());
Properties vcap = new Properties();
Properties vcapProperties = new Properties();
vcap.setProperty("vcap.application.name", "TestApp");
vcap.setProperty("vcap.application.uris", "test-app.apps.cloud.skullbox.com");
vcapProperties.setProperty("vcap.application.name", "TestApp");
vcapProperties.setProperty("vcap.application.uris", "test-app.apps.cloud.skullbox.com");
PropertySource vcapPropertySource = new PropertiesPropertySource("vcap", vcap);
PropertySource vcapPropertySource = new PropertiesPropertySource("vcap", vcapProperties);
propertySources.addFirst(vcapPropertySource);
@@ -336,7 +338,7 @@ public class ClientSecurityAutoConfigurationUnitTests {
}
catch (IllegalStateException expected) {
assertThat(expected).hasMessage("No CloudCache Service Instance with name [test-pcc] was found");
assertThat(expected).hasMessage("No Cloud Cache service instance with name [test-pcc] was found");
assertThat(expected).hasNoCause();
throw expected;
@@ -349,4 +351,190 @@ public class ClientSecurityAutoConfigurationUnitTests {
verify(propertySources, never()).addLast(any(PropertySource.class));
}
}
@Test
public void configuresAuthenticationWithCloudPlatformCredentials() {
ConfigurableEnvironment environment = spy(new StandardEnvironment());
Properties vcap = new Properties();
vcap.setProperty("vcap.application.name", "TestApp");
vcap.setProperty("vcap.application.uris", "test-app.apps.cloud.skullbox.com");
vcap.setProperty("vcap.services.test-pcc.name", "test-pcc");
vcap.setProperty("vcap.services.test-pcc.tags", "pivotal, cloudcache, database, gemfire");
vcap.setProperty("vcap.services.test-pcc.credentials.users", "Abuser, Master");
vcap.setProperty("vcap.services.test-pcc.credentials.users[0].username", "Abuser");
vcap.setProperty("vcap.services.test-pcc.credentials.users[0].password", "p@55w0rd");
vcap.setProperty("vcap.services.test-pcc.credentials.users[0].roles", "cluster_developer");
vcap.setProperty("vcap.services.test-pcc.credentials.users[1].username", "Master");
vcap.setProperty("vcap.services.test-pcc.credentials.users[1].password", "p9@$$w0rd");
vcap.setProperty("vcap.services.test-pcc.credentials.users[1].roles", "cluster_operator");
PropertiesPropertySource vcapPropertySource = new PropertiesPropertySource("vcap", vcap);
environment.getPropertySources().addLast(vcapPropertySource);
AutoConfiguredCloudSecurityEnvironmentPostProcessor environmentPostProcessor =
new AutoConfiguredCloudSecurityEnvironmentPostProcessor();
environmentPostProcessor.configureSecurityContext(environment);
assertThat(environment.getProperty("spring.data.gemfire.security.username")).isEqualTo("Master");
assertThat(environment.getProperty("spring.data.gemfire.security.password")).isEqualTo("p9@$$w0rd");
verify(environment, times(2))
.containsProperty(eq("spring.data.gemfire.security.username"));
verify(environment, never())
.containsProperty(eq("spring.data.gemfire.security.password"));
}
@Test
public void configuresAuthenticationWithCloudPlatformCredentialsAndTargetUser() {
ConfigurableEnvironment environment = spy(new StandardEnvironment());
Properties springDataGemFire = new Properties();
springDataGemFire.setProperty("spring.data.gemfire.security.username", "Abuser");
Properties vcap = new Properties();
vcap.setProperty("vcap.application.name", "TestApp");
vcap.setProperty("vcap.application.uris", "test-app.apps.cloud.skullbox.com");
vcap.setProperty("vcap.services.test-pcc.name", "test-pcc");
vcap.setProperty("vcap.services.test-pcc.tags", "pivotal, cloudcache, database, gemfire");
vcap.setProperty("vcap.services.test-pcc.credentials.users", "Abuser, Master");
vcap.setProperty("vcap.services.test-pcc.credentials.users[0].username", "Abuser");
vcap.setProperty("vcap.services.test-pcc.credentials.users[0].password", "p@55w0rd");
vcap.setProperty("vcap.services.test-pcc.credentials.users[0].roles", "cluster_developer");
vcap.setProperty("vcap.services.test-pcc.credentials.users[1].username", "Master");
vcap.setProperty("vcap.services.test-pcc.credentials.users[1].password", "p9@$$w0rd");
vcap.setProperty("vcap.services.test-pcc.credentials.users[1].roles", "cluster_operator");
PropertiesPropertySource vcapPropertySource = new PropertiesPropertySource("vcap", vcap);
PropertiesPropertySource springDataGemFirePropertySource =
new PropertiesPropertySource("spring.data.gemfire", springDataGemFire);
environment.getPropertySources().addLast(vcapPropertySource);
environment.getPropertySources().addLast(springDataGemFirePropertySource);
AutoConfiguredCloudSecurityEnvironmentPostProcessor environmentPostProcessor =
new AutoConfiguredCloudSecurityEnvironmentPostProcessor();
environmentPostProcessor.configureSecurityContext(environment);
assertThat(environment.getProperty("spring.data.gemfire.security.username")).isEqualTo("Abuser");
assertThat(environment.getProperty("spring.data.gemfire.security.password")).isEqualTo("p@55w0rd");
verify(environment, times(2))
.containsProperty(eq("spring.data.gemfire.security.username"));
verify(environment, times(1))
.containsProperty(eq("spring.data.gemfire.security.password"));
}
@Test(expected = IllegalStateException.class)
public void configuresAuthenticationWithCloudPlatformCredentialsAndNonExistingTargetUserThrowsIllegalStateException() {
ConfigurableEnvironment environment = spy(new StandardEnvironment());
Properties springDataGemFire = new Properties();
springDataGemFire.setProperty("spring.data.gemfire.security.username", "NonExistingUser");
Properties vcap = new Properties();
vcap.setProperty("vcap.application.name", "TestApp");
vcap.setProperty("vcap.application.uris", "test-app.apps.cloud.skullbox.com");
vcap.setProperty("vcap.services.test-pcc.name", "test-pcc");
vcap.setProperty("vcap.services.test-pcc.tags", "pivotal, cloudcache, database, gemfire");
vcap.setProperty("vcap.services.test-pcc.credentials.users", "Abuser, Master");
vcap.setProperty("vcap.services.test-pcc.credentials.users[0].username", "Abuser");
vcap.setProperty("vcap.services.test-pcc.credentials.users[0].password", "p@55w0rd");
vcap.setProperty("vcap.services.test-pcc.credentials.users[0].roles", "cluster_developer");
vcap.setProperty("vcap.services.test-pcc.credentials.users[1].username", "Master");
vcap.setProperty("vcap.services.test-pcc.credentials.users[1].password", "p9@$$w0rd");
vcap.setProperty("vcap.services.test-pcc.credentials.users[1].roles", "cluster_operator");
PropertiesPropertySource vcapPropertySource = new PropertiesPropertySource("vcap", vcap);
PropertiesPropertySource springDataGemFirePropertySource =
new PropertiesPropertySource("spring.data.gemfire", springDataGemFire);
environment.getPropertySources().addLast(vcapPropertySource);
environment.getPropertySources().addLast(springDataGemFirePropertySource);
AutoConfiguredCloudSecurityEnvironmentPostProcessor environmentPostProcessor =
new AutoConfiguredCloudSecurityEnvironmentPostProcessor();
try {
environmentPostProcessor.configureSecurityContext(environment);
}
catch (IllegalStateException expected) {
assertThat(expected)
.hasMessage("No User with name [NonExistingUser] was configured for Cloud Cache service [test-pcc]");
assertThat(expected).hasNoCause();
throw expected;
}
finally {
verify(environment, times(2))
.containsProperty(eq("spring.data.gemfire.security.username"));
verify(environment, times(1))
.containsProperty(eq("spring.data.gemfire.security.password"));
}
}
@Test
public void configuresAuthenticationWithUserSuppliedCredentials() {
ConfigurableEnvironment environment = spy(new StandardEnvironment());
Properties springDataGemFire = new Properties();
springDataGemFire.setProperty("spring.data.gemfire.security.username", "MyUser");
springDataGemFire.setProperty("spring.data.gemfire.security.password", "s3cUr3");
Properties vcap = new Properties();
vcap.setProperty("vcap.application.name", "TestApp");
vcap.setProperty("vcap.application.uris", "test-app.apps.cloud.skullbox.com");
vcap.setProperty("vcap.services.test-pcc.name", "test-pcc");
vcap.setProperty("vcap.services.test-pcc.tags", "pivotal, cloudcache, database, gemfire");
vcap.setProperty("vcap.services.test-pcc.credentials.users", "Abuser, Master");
vcap.setProperty("vcap.services.test-pcc.credentials.users[0].username", "Abuser");
vcap.setProperty("vcap.services.test-pcc.credentials.users[0].password", "p@55w0rd");
vcap.setProperty("vcap.services.test-pcc.credentials.users[0].roles", "cluster_developer");
vcap.setProperty("vcap.services.test-pcc.credentials.users[1].username", "Master");
vcap.setProperty("vcap.services.test-pcc.credentials.users[1].password", "p9@$$w0rd");
vcap.setProperty("vcap.services.test-pcc.credentials.users[1].roles", "cluster_operator");
PropertiesPropertySource vcapPropertySource = new PropertiesPropertySource("vcap", vcap);
PropertiesPropertySource springDataGemFirePropertySource =
new PropertiesPropertySource("spring.data.gemfire", springDataGemFire);
environment.getPropertySources().addLast(vcapPropertySource);
environment.getPropertySources().addLast(springDataGemFirePropertySource);
AutoConfiguredCloudSecurityEnvironmentPostProcessor environmentPostProcessor =
new AutoConfiguredCloudSecurityEnvironmentPostProcessor();
environmentPostProcessor.configureSecurityContext(environment);
assertThat(environment.getProperty("spring.data.gemfire.security.username")).isEqualTo("MyUser");
assertThat(environment.getProperty("spring.data.gemfire.security.password")).isEqualTo("s3cUr3");
verify(environment, times(1))
.containsProperty(eq("spring.data.gemfire.security.username"));
verify(environment, times(1))
.containsProperty(eq("spring.data.gemfire.security.password"));
}
}