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

@@ -99,6 +99,7 @@ public class ApacheShiroSecurityConfiguration extends AbstractAnnotationConfigSu
* @see org.springframework.beans.factory.BeanFactory
*/
@Override
@SuppressWarnings("all")
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
super.setBeanFactory(Optional.ofNullable(beanFactory)
@@ -197,10 +198,12 @@ public class ApacheShiroSecurityConfiguration extends AbstractAnnotationConfigSu
protected List<Realm> resolveRealms() {
try {
Map<String, Realm> realmBeans = getListableBeanFactory()
.getBeansOfType(Realm.class, false, true);
Map<String, Realm> realmBeans = getListableBeanFactory().getBeansOfType(Realm.class, false, true);
List<Realm> realms = new ArrayList<>(CollectionUtils.nullSafeMap(realmBeans).values());
Collections.sort(realms, OrderComparator.INSTANCE);
realms.sort(OrderComparator.INSTANCE);
return realms;
}
catch (Exception ignore) {
@@ -278,6 +281,7 @@ public class ApacheShiroSecurityConfiguration extends AbstractAnnotationConfigSu
* @inheritDoc
*/
@Override
@SuppressWarnings("all")
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return ClassUtils.isPresent(APACHE_SHIRO_LIFECYCLE_BEAN_POST_PROCESSOR_CLASS_NAME,
context.getClassLoader());

View File

@@ -0,0 +1,97 @@
/*
* 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 java.util.Map;
import java.util.Optional;
import java.util.Properties;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.data.gemfire.config.annotation.support.AutoConfiguredAuthenticationInitializer;
import org.springframework.data.gemfire.config.annotation.support.EmbeddedServiceConfigurationSupport;
import org.springframework.data.gemfire.util.PropertiesBuilder;
import org.springframework.util.StringUtils;
/**
* The {@link AutoConfiguredAuthenticationConfiguration} class is a Spring {@link Configuration @Configuration} class
* that auto-configures Pivotal GemFire / Apache Geode Authentication by providing a implementation
* of the {@link org.apache.geode.security.AuthInitialize} interface along with setting the necessary GemFire / Geode
* properties.
*
* @author John Blum
* @see java.util.Properties
* @see org.apache.geode.security.AuthInitialize
* @see org.springframework.context.annotation.Condition
* @see org.springframework.context.annotation.Conditional
* @see org.springframework.context.annotation.Configuration
* @see org.springframework.core.env.Environment
* @see org.springframework.data.gemfire.config.annotation.EnableBeanFactoryLocator
* @see org.springframework.data.gemfire.config.annotation.support.AutoConfiguredAuthenticationInitializer
* @see org.springframework.data.gemfire.config.annotation.support.EmbeddedServiceConfigurationSupport
* @see org.springframework.data.gemfire.util.PropertiesBuilder
* @since 2.0.0
*/
@Configuration
@EnableBeanFactoryLocator
@Conditional(AutoConfiguredAuthenticationConfiguration.AutoConfiguredAuthenticationCondition.class)
public class AutoConfiguredAuthenticationConfiguration extends EmbeddedServiceConfigurationSupport {
protected static final String AUTO_CONFIGURED_AUTH_INIT_STATIC_FACTORY_METHOD =
AutoConfiguredAuthenticationInitializer.class.getName().concat(".newAuthenticationInitializer");
protected static final String SECURITY_CLIENT_AUTH_INIT = "security-client-auth-init";
protected static final String SECURITY_PEER_AUTH_INIT = "security-peer-auth-init";
/* (non-Javadoc) */
@Override
protected Class getAnnotationType() {
return EnableSecurity.class;
}
/* (non-Javadoc) */
@Override
protected Properties toGemFireProperties(Map<String, Object> annotationAttributes) {
return Optional.of(PropertiesBuilder.create())
.filter(builder -> isMatch(environment()))
.map(builder -> builder
.setProperty(SECURITY_CLIENT_AUTH_INIT, AUTO_CONFIGURED_AUTH_INIT_STATIC_FACTORY_METHOD)
.setProperty(SECURITY_PEER_AUTH_INIT, AUTO_CONFIGURED_AUTH_INIT_STATIC_FACTORY_METHOD))
.map(PropertiesBuilder::build)
.orElse(null);
}
/* (non-Javadoc) */
private static boolean isMatch(Environment environment) {
return StringUtils.hasText(environment.getProperty(
AutoConfiguredAuthenticationInitializer.SDG_SECURITY_USERNAME_PROPERTY));
}
public static class AutoConfiguredAuthenticationCondition implements Condition {
@Override
@SuppressWarnings("all")
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
return isMatch(conditionContext.getEnvironment());
}
}
}

View File

@@ -47,7 +47,8 @@ import org.springframework.context.annotation.Import;
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Import({ ApacheShiroSecurityConfiguration.class, GeodeIntegratedSecurityConfiguration.class })
@Import({ ApacheShiroSecurityConfiguration.class, AutoConfiguredAuthenticationConfiguration.class,
GeodeIntegratedSecurityConfiguration.class })
@UsesGemFireProperties
@SuppressWarnings({ "unused" })
public @interface EnableSecurity {
@@ -117,6 +118,24 @@ public @interface EnableSecurity {
*/
String securityPostProcessorClassName() default "";
/**
* The {@literal security-username} used by a GemFire cache client application required to authenticate.
*
* Defaults to unset.
*
* Use the {@literal spring.data.gemfire.security.username} in {@literal application.properties}.
*/
String securityUsername() default "";
/**
* The {@literal security-password} used by a GemFire cache client application required to authenticate.
*
* Defaults to unset.
*
* Use the {@literal spring.data.gemfire.security.password} in {@literal application.properties}.
*/
String securityPassword() default "";
/**
* Sets the Geode System Property referring to the location of an Apache Shiro INI file used to configure
* the Apache Shiro Security Framework to secure Apache Geode.

View File

@@ -61,7 +61,6 @@ public class GeodeIntegratedSecurityConfiguration extends EmbeddedServiceConfigu
*/
protected boolean isShiroSecurityConfigured() {
try {
// NOTE experimental...
//return resolveBean(ApacheShiroSecurityConfiguration.class).isRealmsPresent();
return false;
}

View File

@@ -0,0 +1,94 @@
/*
* 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.support;
import java.util.Optional;
import java.util.Properties;
import org.apache.geode.LogWriter;
import org.apache.geode.distributed.DistributedMember;
import org.apache.geode.security.AuthInitialize;
import org.apache.geode.security.AuthenticationFailedException;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.data.gemfire.support.WiringDeclarableSupport;
/**
* The {@link AbstractAuthInitialize} class is an abstract support class and basic implementation
* of the {@link AuthInitialize} interface used in the authentication of a client or peer
* with a secure GemFire/Geode cluster.
*
* @author John Blum
* @see java.util.Properties
* @see org.apache.geode.security.AuthInitialize
* @see org.springframework.context.EnvironmentAware
* @see org.springframework.core.env.Environment
* @see org.springframework.data.gemfire.support.WiringDeclarableSupport
* @since 2.0.0
*/
public abstract class AbstractAuthInitialize extends WiringDeclarableSupport
implements AuthInitialize, EnvironmentAware {
private Environment environment;
/**
* Sets a reference to the configured Spring {@link Environment}.
*
* @param environment reference to the configured Spring {@link Environment}.
* @see org.springframework.core.env.Environment
*/
@Override
@SuppressWarnings("all")
public void setEnvironment(Environment environment) {
this.environment = environment;
}
/**
* Returns a reference to the configured Spring {@link Environment}.
*
* @return a reference to the configured Spring {@link Environment}.
* @see org.springframework.core.env.Environment
*/
protected Optional<Environment> getEnvironment() {
return Optional.ofNullable(this.environment);
}
/* (non-Javadoc */
@Override
@SuppressWarnings("deprecation")
public final void init(LogWriter logWriter, LogWriter logWriter1) throws AuthenticationFailedException {
doInit();
}
protected void doInit() {
}
/* (non-Javadoc */
@Override
public final Properties getCredentials(Properties properties, DistributedMember distributedMember, boolean isPeer)
throws AuthenticationFailedException {
return doGetCredentials(properties);
}
protected abstract Properties doGetCredentials(Properties properties);
/* (non-Javadoc */
@Override
public void close() {
}
}

View File

@@ -0,0 +1,66 @@
/*
* 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.support;
import java.util.Properties;
import org.apache.shiro.util.StringUtils;
/**
* The AutoConfiguredAuthenticationInitializer class...
*
* @author John Blum
* @since 1.0.0
*/
@SuppressWarnings("unused")
public class AutoConfiguredAuthenticationInitializer extends AbstractAuthInitialize {
public static final String SDG_SECURITY_USERNAME_PROPERTY = "spring.data.gemfire.security.username";
public static final String SDG_SECURITY_PASSWORD_PROPERTY = "spring.data.gemfire.security.password";
public static final String SECURITY_USERNAME_PROPERTY = "security-username";
public static final String SECURITY_PASSWORD_PROPERTY = "security-password";
/**
* Factory method used to construct a new instance of {@link AutoConfiguredAuthenticationInitializer}.
*
* @return a new instance of {@link AutoConfiguredAuthenticationInitializer}.
* @see org.springframework.data.gemfire.config.annotation.support.AutoConfiguredAuthenticationInitializer
*/
public static AutoConfiguredAuthenticationInitializer newAuthenticationInitializer() {
return new AutoConfiguredAuthenticationInitializer();
}
/* (non-Javadoc) */
@Override
protected Properties doGetCredentials(Properties properties) {
getEnvironment()
.filter(environment -> StringUtils.hasText(environment.getProperty(SDG_SECURITY_USERNAME_PROPERTY)))
.ifPresent(environment -> {
String securityUsername = environment.getProperty(SDG_SECURITY_USERNAME_PROPERTY);
String securityPassword = environment.getProperty(SDG_SECURITY_PASSWORD_PROPERTY);
properties.setProperty(SECURITY_USERNAME_PROPERTY, securityUsername);
properties.setProperty(SECURITY_PASSWORD_PROPERTY, securityPassword);
});
return properties;
}
}

View File

@@ -77,8 +77,11 @@ public class GemfireBeanFactoryLocator implements BeanFactoryAware, BeanNameAwar
* @see #afterPropertiesSet()
*/
public static GemfireBeanFactoryLocator newBeanFactoryLocator() {
GemfireBeanFactoryLocator beanFactoryLocator = new GemfireBeanFactoryLocator();
beanFactoryLocator.afterPropertiesSet();
return beanFactoryLocator;
}
@@ -99,6 +102,7 @@ public class GemfireBeanFactoryLocator implements BeanFactoryAware, BeanNameAwar
* @see #afterPropertiesSet()
*/
public static GemfireBeanFactoryLocator newBeanFactoryLocator(BeanFactory beanFactory, String associatedBeanName) {
Assert.isTrue(beanFactory == null || StringUtils.hasText(associatedBeanName),
"associatedBeanName must be specified when BeanFactory is not null");
@@ -121,6 +125,7 @@ public class GemfireBeanFactoryLocator implements BeanFactoryAware, BeanNameAwar
* @see org.springframework.beans.factory.BeanFactory
*/
protected static BeanFactory resolveBeanFactory(String beanFactoryKey) {
BeanFactory beanFactory = BEAN_FACTORIES.get(beanFactoryKey);
Assert.isTrue(BEAN_FACTORIES.isEmpty() || beanFactory != null,
@@ -142,12 +147,15 @@ public class GemfireBeanFactoryLocator implements BeanFactoryAware, BeanNameAwar
* @see org.springframework.beans.factory.BeanFactory
*/
protected static synchronized BeanFactory resolveSingleBeanFactory() {
if (!BEAN_FACTORIES.isEmpty()) {
boolean allTheSameBeanFactory = true;
BeanFactory currentBeanFactory = null;
for (BeanFactory beanFactory : BEAN_FACTORIES.values()) {
allTheSameBeanFactory &= nullOrEquals(currentBeanFactory, beanFactory);
currentBeanFactory = beanFactory;
@@ -178,10 +186,12 @@ public class GemfireBeanFactoryLocator implements BeanFactoryAware, BeanNameAwar
* @see org.springframework.beans.factory.BeanFactory
*/
protected static synchronized void registerAliases(Set<String> names, BeanFactory beanFactory) {
Assert.isTrue(nullSafeSet(names).isEmpty() || beanFactory != null,
"BeanFactory must not be null when aliases are specified");
for (String name : nullSafeSet(names)) {
BeanFactory existingBeanFactory = BEAN_FACTORIES.putIfAbsent(name, beanFactory);
Assert.isTrue(nullOrEquals(existingBeanFactory, beanFactory),
@@ -204,6 +214,7 @@ public class GemfireBeanFactoryLocator implements BeanFactoryAware, BeanNameAwar
*/
@Override
public void afterPropertiesSet() {
BeanFactory beanFactory = getBeanFactory();
registerAliases(resolveAndInitializeBeanNamesWithAliases(beanFactory), beanFactory);
@@ -221,9 +232,11 @@ public class GemfireBeanFactoryLocator implements BeanFactoryAware, BeanNameAwar
* @see #getAssociatedBeanName()
*/
Set<String> resolveAndInitializeBeanNamesWithAliases(BeanFactory beanFactory) {
String associatedBeanName = getAssociatedBeanName();
if (beanFactory != null && StringUtils.hasText(associatedBeanName)) {
String[] beanAliases = beanFactory.getAliases(associatedBeanName);
this.associatedBeanNameWithAliases = new TreeSet<>();
@@ -272,8 +285,8 @@ public class GemfireBeanFactoryLocator implements BeanFactoryAware, BeanNameAwar
* @see #resolveSingleBeanFactory()
*/
public BeanFactory useBeanFactory(String beanFactoryKey) {
return newBeanFactoryReference(StringUtils.hasText(beanFactoryKey) ? resolveBeanFactory(beanFactoryKey)
: resolveSingleBeanFactory()).get();
return newBeanFactoryReference(StringUtils.hasText(beanFactoryKey)
? resolveBeanFactory(beanFactoryKey) : resolveSingleBeanFactory()).get();
}
/**
@@ -380,6 +393,7 @@ public class GemfireBeanFactoryLocator implements BeanFactoryAware, BeanNameAwar
* @see org.springframework.beans.factory.BeanFactory
*/
public BeanFactory get() {
BeanFactory beanFactory = this.beanFactory.get();
Assert.state(beanFactory != null, UNINITIALIZED_BEAN_FACTORY_REFERENCE_MESSAGE);

View File

@@ -85,6 +85,7 @@ public abstract class WiringDeclarableSupport extends DeclarableSupport {
* @see #newBeanConfigurer(BeanFactory, String)
*/
protected boolean configureThis(BeanFactory beanFactory, String templateBeanName) {
BeanConfigurerSupport beanConfigurer = newBeanConfigurer(beanFactory, templateBeanName);
beanConfigurer.configureBean(this);
@@ -122,11 +123,13 @@ public abstract class WiringDeclarableSupport extends DeclarableSupport {
* @see org.springframework.beans.factory.BeanFactory
*/
protected BeanConfigurerSupport newBeanConfigurer(BeanFactory beanFactory, String templateBeanName) {
BeanConfigurerSupport beanConfigurer = new BeanConfigurerSupport();
beanConfigurer.setBeanFactory(beanFactory);
if (StringUtils.hasText(templateBeanName)) {
Assert.isTrue(beanFactory.containsBean(templateBeanName),
String.format("Cannot find bean with name [%s]", templateBeanName));

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;
}
}