diff --git a/spring-geode-autoconfigure/src/main/java/org/springframework/geode/boot/autoconfigure/ClientSecurityAutoConfiguration.java b/spring-geode-autoconfigure/src/main/java/org/springframework/geode/boot/autoconfigure/ClientSecurityAutoConfiguration.java index 2580b119..005c0e80 100644 --- a/spring-geode-autoconfigure/src/main/java/org/springframework/geode/boot/autoconfigure/ClientSecurityAutoConfiguration.java +++ b/spring-geode-autoconfigure/src/main/java/org/springframework/geode/boot/autoconfigure/ClientSecurityAutoConfiguration.java @@ -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); } diff --git a/spring-geode-autoconfigure/src/test/java/org/springframework/geode/boot/autoconfigure/security/auth/ClientSecurityAutoConfigurationUnitTests.java b/spring-geode-autoconfigure/src/test/java/org/springframework/geode/boot/autoconfigure/security/auth/ClientSecurityAutoConfigurationUnitTests.java index 8f019c25..f1ac28eb 100644 --- a/spring-geode-autoconfigure/src/test/java/org/springframework/geode/boot/autoconfigure/security/auth/ClientSecurityAutoConfigurationUnitTests.java +++ b/spring-geode-autoconfigure/src/test/java/org/springframework/geode/boot/autoconfigure/security/auth/ClientSecurityAutoConfigurationUnitTests.java @@ -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)); + } + } }