DATAGEODE-24 - Enhance @EnableSecurity to provide a default implementation of the Geode AuthInitialize interface.
This commit is contained in:
@@ -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());
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -61,7 +61,6 @@ public class GeodeIntegratedSecurityConfiguration extends EmbeddedServiceConfigu
|
||||
*/
|
||||
protected boolean isShiroSecurityConfigured() {
|
||||
try {
|
||||
// NOTE experimental...
|
||||
//return resolveBean(ApacheShiroSecurityConfiguration.class).isRealmsPresent();
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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() {
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user