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:
@@ -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;
|
||||
|
||||
@@ -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"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user