diff --git a/spring-geode-autoconfigure/spring-geode-autoconfigure.gradle b/spring-geode-autoconfigure/spring-geode-autoconfigure.gradle index 45c678dd..959b4533 100644 --- a/spring-geode-autoconfigure/spring-geode-autoconfigure.gradle +++ b/spring-geode-autoconfigure/spring-geode-autoconfigure.gradle @@ -11,6 +11,8 @@ dependencies { provided "javax.servlet:javax.servlet-api" + testCompile project(":apache-geode-extensions") + testCompile "org.assertj:assertj-core" testCompile "junit:junit" testCompile "org.mockito:mockito-core" @@ -29,6 +31,12 @@ dependencies { exclude group: "org.springframework.boot", module: "spring-boot-starter-logging" } + testRuntime("org.springframework.boot:spring-boot-starter-jetty") { + exclude group: "org.springframework.boot", module: "spring-boot-starter-logging" + } + + testRuntime "org.springframework.shell:spring-shell:$springShellVersion" + // Runtime Test dependency on Spring Cloud Services (SCS) to verify workaround to SCS problem! // testRuntime("io.pivotal.spring.cloud:spring-cloud-services-starter-service-registry:2.0.3.RELEASE") { // exclude group: "org.springframework.boot", module: "spring-boot-starter-logging" 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 2d93b5d7..6b7e9afe 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 @@ -35,6 +35,7 @@ import org.springframework.boot.cloud.CloudPlatform; import org.springframework.boot.env.EnvironmentPostProcessor; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.Environment; import org.springframework.core.env.PropertySource; @@ -58,6 +59,7 @@ import org.springframework.lang.Nullable; * @see org.springframework.boot.cloud.CloudPlatform * @see org.springframework.boot.env.EnvironmentPostProcessor * @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 @@ -65,6 +67,7 @@ import org.springframework.lang.Nullable; * @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 @@ -76,6 +79,7 @@ import org.springframework.lang.Nullable; @ConditionalOnClass({ ClientCacheFactoryBean.class, ClientCache.class }) @ConditionalOnMissingBean(GemFireCache.class) @EnableSecurity +@Import(HttpBasicAuthenticationSecurityConfiguration.class) @SuppressWarnings("unused") public class ClientSecurityAutoConfiguration { diff --git a/spring-geode-samples/boot/configuration/src/main/java/example/app/crm/config/HttpSecurityConfiguration.java b/spring-geode-autoconfigure/src/main/java/org/springframework/geode/boot/autoconfigure/HttpBasicAuthenticationSecurityConfiguration.java similarity index 86% rename from spring-geode-samples/boot/configuration/src/main/java/example/app/crm/config/HttpSecurityConfiguration.java rename to spring-geode-autoconfigure/src/main/java/org/springframework/geode/boot/autoconfigure/HttpBasicAuthenticationSecurityConfiguration.java index 43e2b515..1cab70dd 100644 --- a/spring-geode-samples/boot/configuration/src/main/java/example/app/crm/config/HttpSecurityConfiguration.java +++ b/spring-geode-autoconfigure/src/main/java/org/springframework/geode/boot/autoconfigure/HttpBasicAuthenticationSecurityConfiguration.java @@ -14,7 +14,7 @@ * permissions and limitations under the License. */ -package example.app.crm.config; +package org.springframework.geode.boot.autoconfigure; import java.io.IOException; import java.lang.reflect.Method; @@ -29,10 +29,10 @@ import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Profile; import org.springframework.core.env.Environment; import org.springframework.data.gemfire.config.admin.remote.RestHttpGemfireAdminTemplate; import org.springframework.data.gemfire.config.annotation.ClusterConfigurationConfiguration; +import org.springframework.geode.core.util.ObjectUtils; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpRequest; import org.springframework.http.client.ClientHttpRequestExecution; @@ -49,20 +49,26 @@ import org.springframework.web.client.RestTemplate; * @author John Blum * @see java.net.Authenticator * @see java.net.PasswordAuthentication + * @see org.springframework.beans.factory.config.BeanPostProcessor * @see org.springframework.context.annotation.Bean * @see org.springframework.context.annotation.Configuration - * @see org.springframework.context.annotation.Profile * @see org.springframework.core.env.Environment + * @see org.springframework.http.client.ClientHttpRequestInterceptor * @since 1.0.0 */ @Configuration -@Profile("security") @SuppressWarnings("unused") -public class HttpSecurityConfiguration { +public class HttpBasicAuthenticationSecurityConfiguration { private static final String DEFAULT_USERNAME = "test"; private static final String DEFAULT_PASSWORD = DEFAULT_USERNAME; + private static final String SPRING_DATA_GEMFIRE_SECURITY_USERNAME_PROPERTY = + "spring.data.gemfire.security.username"; + + private static final String SPRING_DATA_GEMFIRE_SECURITY_PASSWORD_PROPERTY = + "spring.data.gemfire.security.password"; + @Bean public Authenticator authenticator(Environment environment) { @@ -72,10 +78,10 @@ public class HttpSecurityConfiguration { protected PasswordAuthentication getPasswordAuthentication() { String username = - environment.getProperty("spring.data.gemfire.security.username", DEFAULT_USERNAME); + environment.getProperty(SPRING_DATA_GEMFIRE_SECURITY_USERNAME_PROPERTY, DEFAULT_USERNAME); String password = - environment.getProperty("spring.data.gemfire.security.password", DEFAULT_PASSWORD); + environment.getProperty(SPRING_DATA_GEMFIRE_SECURITY_PASSWORD_PROPERTY, DEFAULT_PASSWORD); return new PasswordAuthentication(username, password.toCharArray()); } @@ -127,7 +133,7 @@ public class HttpSecurityConfiguration { @SuppressWarnings("unchecked") private T invokeMethod(Object target, String methodName) { - return this.doOperationSafely(() -> { + return ObjectUtils.doOperationSafely(() -> { Method method = target.getClass().getDeclaredMethod(methodName); @@ -168,11 +174,11 @@ public class HttpSecurityConfiguration { } protected String getUsername() { - return this.environment.getProperty("spring.data.gemfire.security.username"); + return this.environment.getProperty(SPRING_DATA_GEMFIRE_SECURITY_USERNAME_PROPERTY); } protected String getPassword() { - return this.environment.getProperty("spring.data.gemfire.security.password"); + return this.environment.getProperty(SPRING_DATA_GEMFIRE_SECURITY_PASSWORD_PROPERTY); } @Override diff --git a/spring-geode-autoconfigure/src/main/java/org/springframework/geode/boot/autoconfigure/PeerSecurityAutoConfiguration.java b/spring-geode-autoconfigure/src/main/java/org/springframework/geode/boot/autoconfigure/PeerSecurityAutoConfiguration.java index 4f006909..e932fa38 100644 --- a/spring-geode-autoconfigure/src/main/java/org/springframework/geode/boot/autoconfigure/PeerSecurityAutoConfiguration.java +++ b/spring-geode-autoconfigure/src/main/java/org/springframework/geode/boot/autoconfigure/PeerSecurityAutoConfiguration.java @@ -20,6 +20,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; import org.springframework.data.gemfire.client.ClientCacheFactoryBean; import org.springframework.data.gemfire.config.annotation.ApacheShiroSecurityConfiguration; import org.springframework.data.gemfire.config.annotation.EnableBeanFactoryLocator; @@ -34,12 +35,14 @@ import org.springframework.data.gemfire.config.annotation.GeodeIntegratedSecurit * @see org.apache.geode.security.SecurityManager * @see org.springframework.boot.autoconfigure.EnableAutoConfiguration * @see org.springframework.context.annotation.Configuration + * @see org.springframework.context.annotation.Import * @see org.springframework.data.gemfire.CacheFactoryBean * @see org.springframework.data.gemfire.client.ClientCacheFactoryBean * @see org.springframework.data.gemfire.config.annotation.ApacheShiroSecurityConfiguration * @see org.springframework.data.gemfire.config.annotation.EnableBeanFactoryLocator * @see org.springframework.data.gemfire.config.annotation.EnableSecurity * @see org.springframework.data.gemfire.config.annotation.GeodeIntegratedSecurityConfiguration + * @see org.springframework.geode.boot.autoconfigure.HttpBasicAuthenticationSecurityConfiguration * @see org.springframework.geode.security.support.SecurityManagerProxy * @since 1.0.0 */ @@ -52,6 +55,7 @@ import org.springframework.data.gemfire.config.annotation.GeodeIntegratedSecurit }) @EnableBeanFactoryLocator @EnableSecurity(securityManagerClassName = "org.springframework.geode.security.support.SecurityManagerProxy") +@Import(HttpBasicAuthenticationSecurityConfiguration.class) @SuppressWarnings("unused") public class PeerSecurityAutoConfiguration { diff --git a/spring-geode-autoconfigure/src/test/java/org/springframework/geode/boot/autoconfigure/cluster/ClusterConfigurationWithAuthenticationIntegrationTests.java b/spring-geode-autoconfigure/src/test/java/org/springframework/geode/boot/autoconfigure/cluster/ClusterConfigurationWithAuthenticationIntegrationTests.java new file mode 100644 index 00000000..28beea09 --- /dev/null +++ b/spring-geode-autoconfigure/src/test/java/org/springframework/geode/boot/autoconfigure/cluster/ClusterConfigurationWithAuthenticationIntegrationTests.java @@ -0,0 +1,176 @@ +/* + * Copyright 2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +package org.springframework.geode.boot.autoconfigure.cluster; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.stream.Collectors; + +import org.apache.geode.cache.GemFireCache; +import org.apache.geode.cache.Region; +import org.apache.geode.distributed.internal.DistributionConfig; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.ApplicationRunner; +import org.springframework.boot.WebApplicationType; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.FilterType; +import org.springframework.core.io.ClassPathResource; +import org.springframework.data.gemfire.GemfireTemplate; +import org.springframework.data.gemfire.GemfireUtils; +import org.springframework.data.gemfire.config.annotation.CacheServerApplication; +import org.springframework.data.gemfire.config.annotation.EnableClusterConfiguration; +import org.springframework.data.gemfire.config.annotation.EnableEntityDefinedRegions; +import org.springframework.data.gemfire.config.annotation.EnableLogging; +import org.springframework.data.gemfire.config.annotation.EnableManager; +import org.springframework.data.gemfire.tests.integration.ForkingClientServerIntegrationTestsSupport; +import org.springframework.geode.security.TestSecurityManager; +import org.springframework.test.context.junit4.SpringRunner; + +import example.app.model.Book; +import example.app.model.ISBN; + +/** + * Integration tests testing the SDG {@link EnableClusterConfiguration} annotation functionality + * when the GemFire/Geode server is configured with Security (Authentication). + * + * @author John Blum + * @see org.junit.Test + * @see org.springframework.boot.test.context.SpringBootTest + * @see org.springframework.data.gemfire.config.annotation.EnableClusterConfiguration + * @see org.springframework.data.gemfire.tests.integration.ForkingClientServerIntegrationTestsSupport + * @see org.springframework.test.context.junit4.SpringRunner + * @since 1.0.0 + */ +@RunWith(SpringRunner.class) +@SpringBootTest( + classes = ClusterConfigurationWithAuthenticationIntegrationTests.GeodeClientConfiguration.class, + webEnvironment = SpringBootTest.WebEnvironment.NONE, + properties = { + "spring.data.gemfire.security.username=test", + "spring.data.gemfire.security.password=test" + } +) +@SuppressWarnings("unused") +public class ClusterConfigurationWithAuthenticationIntegrationTests extends ForkingClientServerIntegrationTestsSupport { + + private static final String GEMFIRE_LOG_LEVEL = "error"; + + @BeforeClass + public static void startGemFireServer() throws IOException { + startGemFireServer(GeodeServerConfiguration.class); + } + + @Autowired + @Qualifier("booksTemplate") + private GemfireTemplate booksTemplate; + + @Before + public void setup() { + assertThat(this.booksTemplate).isNotNull(); + } + + @Test + public void clusterConfigurationAndRegionDataAccessOperationsAreSuccessful() { + + Book expetedSeriesOfUnfortunateEvents = Book + .newBook("A Serious of Unfortunate Events") + .identifiedBy(ISBN.autoGenerated()); + + this.booksTemplate.put(expetedSeriesOfUnfortunateEvents.getIsbn(), expetedSeriesOfUnfortunateEvents); + + Book actualSeriesOfUnfortunateEvents = this.booksTemplate.get(expetedSeriesOfUnfortunateEvents.getIsbn()); + + assertThat(actualSeriesOfUnfortunateEvents).isNotNull(); + assertThat(actualSeriesOfUnfortunateEvents).isEqualTo(expetedSeriesOfUnfortunateEvents); + assertThat(actualSeriesOfUnfortunateEvents).isNotSameAs(expetedSeriesOfUnfortunateEvents); + } + + @SpringBootApplication + @EnableLogging(logLevel = GEMFIRE_LOG_LEVEL) + @EnableEntityDefinedRegions(basePackageClasses = Book.class) + @EnableClusterConfiguration(useHttp = true) + static class GeodeClientConfiguration { } + + @SpringBootApplication + @ComponentScan(excludeFilters = + @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = GeodeClientConfiguration.class) + ) + @CacheServerApplication(name = "ClusterConfigurationWithAuthenticationIntegrationTests", logLevel = GEMFIRE_LOG_LEVEL) + @EnableManager(start = true) + static class GeodeServerConfiguration { + + private static final String GEODE_HOME_PROPERTY = DistributionConfig.GEMFIRE_PREFIX + "home"; + + public static void main(String[] args) throws IOException { + + resolveAndConfigureGeodeHome(); + + //System.err.printf("%s [%s]%n", GEODE_HOME_PROPERTY, System.getProperty(GEODE_HOME_PROPERTY)); + + new SpringApplicationBuilder(GeodeServerConfiguration.class) + .web(WebApplicationType.NONE) + .build() + .run(args); + } + + private static void resolveAndConfigureGeodeHome() throws IOException { + + ClassPathResource resource = new ClassPathResource("/geode-home"); + + File resourceFile = resource.getFile(); + + System.setProperty(GEODE_HOME_PROPERTY, resourceFile.getAbsolutePath()); + } + + @Bean + org.apache.geode.security.SecurityManager testSecurityManager() { + return new TestSecurityManager(); + } + + @Bean + ApplicationRunner peerCacheVerifier(GemFireCache cache) { + + return args -> { + + assertThat(cache).isNotNull(); + assertThat(GemfireUtils.isPeer(cache)).isTrue(); + assertThat(cache.getName()) + .isEqualTo(ClusterConfigurationWithAuthenticationIntegrationTests.class.getSimpleName()); + + List regionNames = cache.rootRegions().stream() + .map(Region::getName) + .collect(Collectors.toList()); + + assertThat(regionNames) + .describedAs("Expected no Regions; but was [%s]", regionNames) + .isEmpty(); + }; + } + } +} diff --git a/spring-geode-autoconfigure/src/test/resources/geode-home/tools/Extensions/geode-web-1.2.1.war b/spring-geode-autoconfigure/src/test/resources/geode-home/tools/Extensions/geode-web-1.2.1.war new file mode 100644 index 00000000..bad45d3f Binary files /dev/null and b/spring-geode-autoconfigure/src/test/resources/geode-home/tools/Extensions/geode-web-1.2.1.war differ