Implement HTTP client authentication on cluster configuration push when sending configuration meta-data from client to server (cluster Manager).

Resolves gh-16.
This commit is contained in:
John Blum
2019-04-24 00:27:30 -07:00
parent 786deaeef9
commit a293b97113
6 changed files with 208 additions and 10 deletions

View File

@@ -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"

View File

@@ -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 {

View File

@@ -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> 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

View File

@@ -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 {

View File

@@ -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<String> regionNames = cache.rootRegions().stream()
.map(Region::getName)
.collect(Collectors.toList());
assertThat(regionNames)
.describedAs("Expected no Regions; but was [%s]", regionNames)
.isEmpty();
};
}
}
}