SGF-648 - Enhance @EnableSecurity to provide a default implementation of the GemFire AuthInitialize interface.

(cherry picked from commit 3859f70e45f32b23ba5d332d49d5ae724d7e0bbf)
Signed-off-by: John Blum <jblum@pivotal.io>
This commit is contained in:
John Blum
2017-07-20 00:32:23 -07:00
parent 116a821f41
commit 69412c5dc7
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));