DATAGEODE-24 - Enhance @EnableSecurity to provide a default implementation of the Geode AuthInitialize interface.

This commit is contained in:
John Blum
2017-07-20 00:32:23 -07:00
parent d4c732cd87
commit 3859f70e45
10 changed files with 560 additions and 7 deletions

View File

@@ -0,0 +1,172 @@
/*
* Copyright 2017 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
*
* http://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.data.gemfire.config.annotation;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.data.gemfire.config.annotation.TestSecurityManager.SECURITY_PASSWORD;
import static org.springframework.data.gemfire.config.annotation.TestSecurityManager.SECURITY_USERNAME;
import java.util.Optional;
import org.apache.geode.cache.CacheLoader;
import org.apache.geode.cache.CacheLoaderException;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.LoaderHelper;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.client.ClientRegionShortcut;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import org.springframework.data.gemfire.LocalRegionFactoryBean;
import org.springframework.data.gemfire.client.ClientRegionFactoryBean;
import org.springframework.data.gemfire.process.ProcessWrapper;
import org.springframework.data.gemfire.test.support.ClientServerIntegrationTestsSupport;
import org.springframework.mock.env.MockPropertySource;
/**
* The AutoConfiguredAuthenticationConfigurationIntegrationTests class...
*
* @author John Blum
* @since 1.0.0
*/
public class AutoConfiguredAuthenticationConfigurationIntegrationTests extends ClientServerIntegrationTestsSupport {
private static final int PORT = 42124;
private static ProcessWrapper gemfireServerProcess;
private ConfigurableApplicationContext applicationContext;
@BeforeClass
public static void setupGemFireServer() throws Exception {
gemfireServerProcess = run(TestGemFireServerConfiguration.class, String.format("-Dgemfire.name=%1$s",
asApplicationName(AutoConfiguredAuthenticationConfigurationIntegrationTests.class)));
waitForServerToStart("localhost", PORT);
}
@AfterClass
public static void tearDownGemFireServer() {
stop(gemfireServerProcess);
}
@After
public void tearDown() {
Optional.ofNullable(this.applicationContext).ifPresent(ConfigurableApplicationContext::close);
}
private ConfigurableApplicationContext newApplicationContext(PropertySource<?> testPropertySource,
Class<?>... annotatedClasses) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
MutablePropertySources propertySources = applicationContext.getEnvironment().getPropertySources();
propertySources.addFirst(testPropertySource);
applicationContext.registerShutdownHook();
applicationContext.register(annotatedClasses);
applicationContext.refresh();
return applicationContext;
}
@Test
@Ignore
@SuppressWarnings("unchecked")
public void clientAuthenticatedWithServer() {
MockPropertySource testPropertySource = new MockPropertySource()
.withProperty("spring.data.gemfire.security.username", SECURITY_USERNAME)
.withProperty("spring.data.gemfire.security.password", SECURITY_PASSWORD);
this.applicationContext = newApplicationContext(testPropertySource, TestGemFireClientConfiguration.class);
assertThat(this.applicationContext).isNotNull();
assertThat(this.applicationContext.containsBean("Echo")).isTrue();
Region<Object, Object> echo = this.applicationContext.getBean("Echo", Region.class);
assertThat(echo.get("TEST")).isEqualTo("TEST");
}
@EnableSecurity
@ClientCacheApplication(servers = @ClientCacheApplication.Server(port = PORT))
static class TestGemFireClientConfiguration {
@Bean("Echo")
ClientRegionFactoryBean<Object, Object> echoRegion(GemFireCache gemfireCache) {
ClientRegionFactoryBean<Object, Object> echoRegion = new ClientRegionFactoryBean<>();
echoRegion.setCache(gemfireCache);
echoRegion.setClose(false);
echoRegion.setShortcut(ClientRegionShortcut.PROXY);
return echoRegion;
}
}
@CacheServerApplication(port = PORT)
@EnableSecurity(securityManagerClassName = "org.springframework.data.gemfire.config.annotation.TestSecurityManager")
static class TestGemFireServerConfiguration {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext =
new AnnotationConfigApplicationContext(TestGemFireServerConfiguration.class);
applicationContext.registerShutdownHook();
}
@Bean("Echo")
LocalRegionFactoryBean<Object, Object> echoRegion(GemFireCache gemfireCache) {
LocalRegionFactoryBean<Object, Object> echoRegion = new LocalRegionFactoryBean<>();
echoRegion.setCache(gemfireCache);
echoRegion.setCacheLoader(newEchoCacheLoader());
echoRegion.setClose(false);
echoRegion.setPersistent(false);
return echoRegion;
}
private CacheLoader<Object, Object> newEchoCacheLoader() {
return new CacheLoader<Object, Object>() {
@Override
public Object load(LoaderHelper<Object, Object> loaderHelper) throws CacheLoaderException {
return loaderHelper.getKey();
}
@Override
public void close() {
}
};
}
}
}

View File

@@ -0,0 +1,85 @@
/*
* Copyright 2017 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
*
* http://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.data.gemfire.config.annotation;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.security.Principal;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.apache.geode.security.AuthenticationFailedException;
import org.springframework.util.ObjectUtils;
/**
* The {@link TestSecurityManager} class is an Apache Geode / Pivotal GemFire
* {@link org.apache.geode.security.SecurityManager} implementation used for testing purposes.
*
* @author John Blum
* @see java.security.Principal
* @see java.util.Properties
* @see org.apache.geode.security.SecurityManager
* @since 2.0.0
*/
public final class TestSecurityManager implements org.apache.geode.security.SecurityManager {
public static final String SECURITY_USERNAME_PROPERTY = "security-username";
public static final String SECURITY_PASSWORD_PROPERTY = "security-password";
public static final String SECURITY_USERNAME = "testUser";
public static final String SECURITY_PASSWORD = "testP@55w0rd";
private final ConcurrentMap<String, String> authorizedUsers;
public TestSecurityManager() {
this.authorizedUsers = new ConcurrentHashMap<>();
this.authorizedUsers.putIfAbsent(SECURITY_USERNAME, SECURITY_PASSWORD);
}
@Override
public Object authenticate(Properties properties) throws AuthenticationFailedException {
String username = properties.getProperty(SECURITY_USERNAME_PROPERTY);
String password = properties.getProperty(SECURITY_PASSWORD_PROPERTY);
return validateIdentified(isIdentified(username, password) ? newPrincipal(username) : null, username);
}
private boolean isIdentified(String username, String password) {
return ObjectUtils.nullSafeEquals(this.authorizedUsers.get(String.valueOf(username)), password);
}
private Principal newPrincipal(String username) {
Principal mockPrincipal = mock(Principal.class, username);
when(mockPrincipal.getName()).thenReturn(username);
return mockPrincipal;
}
private Principal validateIdentified(Principal principal, String username) {
if (principal == null) {
throw new AuthenticationFailedException(String.format("User [%s] is not valid", username));
}
return principal;
}
}