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:
@@ -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"
|
||||
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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 {
|
||||
|
||||
|
||||
@@ -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();
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Reference in New Issue
Block a user