Enable Locator applications bootstrapped with SDG to be configured with Security (Auth) using @EnableSecurity.

Closes #621.
This commit is contained in:
John Blum
2022-08-30 13:57:35 -07:00
parent f7472d2d36
commit 2e79b1bdb3
23 changed files with 1076 additions and 319 deletions

View File

@@ -122,9 +122,17 @@ public class ApacheShiroSecurityConfiguration extends AbstractAnnotationConfigSu
@Bean
public BeanFactoryPostProcessor shiroGemFireBeanFactoryPostProcessor() {
return configurableListableBeanFactory ->
SpringUtils.addDependsOn(configurableListableBeanFactory.getBeanDefinition("gemfireCache"),
"shiroSecurityManager");
return configurableListableBeanFactory -> {
if (configurableListableBeanFactory.containsBean("gemfireCache")) {
SpringUtils.addDependsOn(configurableListableBeanFactory.getBeanDefinition("gemfireCache"),
"shiroSecurityManager");
}
else if (configurableListableBeanFactory.containsBean("locatorApplication")) {
SpringUtils.addDependsOn(configurableListableBeanFactory.getBeanDefinition("locatorApplication"),
"shiroSecurityManager");
}
};
}
/**
@@ -251,9 +259,6 @@ public class ApacheShiroSecurityConfiguration extends AbstractAnnotationConfigSu
return environment.getProperty(SPRING_DATA_GEMFIRE_SECURITY_SHIRO_ENABLED, Boolean.class, true);
}
/**
* @inheritDoc
*/
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return isEnabled(context.getEnvironment()) && isApacheShiroPresent(context);

View File

@@ -0,0 +1,175 @@
/*
* Copyright 2022 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.data.gemfire.config.annotation;
import java.lang.annotation.Annotation;
import java.util.function.Supplier;
import org.apache.shiro.util.Assert;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportAware;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.data.gemfire.config.annotation.support.AbstractAnnotationConfigSupport;
import org.springframework.data.gemfire.config.annotation.support.Authentication;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
/**
* Spring {@link Configuration} class used to configure and register an {@link Authentication} object
* based on security (auth) configuration metadata supplied in the {@link EnableSecurity} annotation.
*
* @author John Blum
* @see java.lang.annotation.Annotation
* @see org.springframework.beans.factory.config.BeanDefinition
* @see org.springframework.beans.factory.support.BeanDefinitionBuilder
* @see org.springframework.beans.factory.support.BeanDefinitionRegistry
* @see org.springframework.beans.factory.support.BeanNameGenerator
* @see org.springframework.context.annotation.Configuration
* @see org.springframework.context.annotation.ImportAware
* @see org.springframework.data.gemfire.config.annotation.support.AbstractAnnotationConfigSupport
* @see org.springframework.data.gemfire.config.annotation.support.Authentication
* @since 1.0.0
*/
@Configuration
public class AuthenticationBeanConfiguration extends AbstractAnnotationConfigSupport implements ImportAware {
private static final char[] EMPTY_CHAR_ARRAY = {};
private String username;
private String password;
@Override
protected Class<? extends Annotation> getAnnotationType() {
return EnableSecurity.class;
}
protected void setUsername(@Nullable String username) {
this.username = username;
}
private @Nullable String getUsername() {
return this.username;
}
protected void setPassword(@Nullable String password) {
this.password = password;
}
private @Nullable String getPassword() {
return this.password;
}
@Override
public void setImportMetadata(@NonNull AnnotationMetadata importMetadata) {
if (isAnnotationPresent(importMetadata)) {
AnnotationAttributes enableSecurityAttributes = getAnnotationAttributes(importMetadata);
setUsername(resolveProperty(securityProperty("username"), String.class,
enableSecurityAttributes.getString("securityUsername")));
setPassword(resolveProperty(securityProperty("password"), String.class,
enableSecurityAttributes.getString("securityPassword")));
}
}
@Bean
@SuppressWarnings("unused")
public @NonNull Authentication<String, String> springDataGeodeAuthentication() {
return SpringDataGeodeAuthentication.from(this::getUsername, this::getPassword);
}
private void registerAuthenticationBean(@NonNull BeanDefinitionRegistry registry,
@NonNull BeanNameGenerator beanNameGenerator) {
if (isAuthenticationCredentialsSet(getUsername(), nullSafeToCharArray(getPassword()))) {
BeanDefinition authenticationBean =
BeanDefinitionBuilder.rootBeanDefinition(SpringDataGeodeAuthentication.class)
.addConstructorArgValue(getUsername())
.addConstructorArgValue(getPassword())
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE)
.getBeanDefinition();
String beanName = beanNameGenerator.generateBeanName(authenticationBean, registry);
registry.registerBeanDefinition(beanName, authenticationBean);
}
}
protected boolean isAuthenticationCredentialsSet(String username, char[] password) {
return StringUtils.hasText(username) && nullSafeCharArray(password).length > 0;
}
private @NonNull char[] nullSafeCharArray(@Nullable char[] array) {
return array != null ? array : EMPTY_CHAR_ARRAY;
}
private @NonNull char[] nullSafeToCharArray(@Nullable String value) {
return value != null ? value.trim().toCharArray() : EMPTY_CHAR_ARRAY;
}
protected static class SpringDataGeodeAuthentication implements Authentication<String, String> {
public static @NonNull SpringDataGeodeAuthentication from(@NonNull Supplier<String> username,
@NonNull Supplier<String> password) {
return new SpringDataGeodeAuthentication(username, password);
}
private final Supplier<String> username;
private final Supplier<String> password;
protected SpringDataGeodeAuthentication(@NonNull Supplier<String> username, @NonNull Supplier<String> password) {
this.username = require(username, "Username [%s] is required", username);
this.password = require(password, "Password [%s] is required", password);
}
private <T> T require(T target, String message, Object... arguments) {
Assert.notNull(target, String.format(message, arguments));
return target;
}
@Override
public boolean isRequested() {
return StringUtils.hasText(getPrincipal()) && StringUtils.hasText(getCredentials());
}
@Override
public @NonNull String getPrincipal() {
return this.username.get();
}
@Override
public @NonNull String getCredentials() {
return this.password.get();
}
@Override
public String toString() {
return getPrincipal();
}
}
}

View File

@@ -22,113 +22,152 @@ import java.net.URI;
import java.util.Optional;
import java.util.Properties;
import org.apache.geode.security.AuthInitialize;
import org.apache.shiro.util.Assert;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
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.context.annotation.Import;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.context.annotation.ConfigurationCondition;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.data.gemfire.GemFireProperties;
import org.springframework.data.gemfire.config.annotation.support.Authentication;
import org.springframework.data.gemfire.config.annotation.support.AutoConfiguredAuthenticationInitializer;
import org.springframework.data.gemfire.config.support.RestTemplateConfigurer;
import org.springframework.data.gemfire.util.CollectionUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 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.
* The {@link AutoConfiguredAuthenticationConfiguration} class is a Spring {@link Configuration} class
* that auto-configures Apache Geode Authentication by providing an implementation of the {@link AuthInitialize}
* interface along with setting the necessary Apache Geode {@link Properties}.
*
* @author John Blum
* @see java.net.Authenticator
* @see java.net.PasswordAuthentication
* @see java.util.Properties
* @see org.apache.geode.security.AuthInitialize
* @see org.springframework.beans.factory.config.ConfigurableListableBeanFactory
* @see org.springframework.context.annotation.Bean
* @see org.springframework.context.annotation.Condition
* @see org.springframework.context.annotation.Conditional
* @see org.springframework.context.annotation.Configuration
* @see org.springframework.context.annotation.Import
* @see org.springframework.context.annotation.ConfigurationCondition
* @see org.springframework.core.env.Environment
* @see org.springframework.data.gemfire.GemFireProperties
* @see org.springframework.data.gemfire.config.annotation.EnableBeanFactoryLocator
* @see org.springframework.data.gemfire.config.annotation.support.Authentication
* @see org.springframework.data.gemfire.config.annotation.support.AutoConfiguredAuthenticationInitializer
* @see org.springframework.data.gemfire.config.support.RestTemplateConfigurer
* @see org.springframework.data.gemfire.util.PropertiesBuilder
* @see org.springframework.http.client.ClientHttpRequestInterceptor
* @since 2.0.0
*/
@Configuration
@Import(BeanFactoryLocatorConfiguration.class)
@Conditional(AutoConfiguredAuthenticationConfiguration.AutoConfiguredAuthenticationCondition.class)
@EnableBeanFactoryLocator
@Conditional(AutoConfiguredAuthenticationConfiguration.AuthenticationAutoConfigurationEnabledCondition.class)
@SuppressWarnings("unused")
public class AutoConfiguredAuthenticationConfiguration {
private static final char[] EMPTY_CHAR_ARRAY = {};
protected static final String AUTO_CONFIGURED_AUTH_INIT_STATIC_FACTORY_METHOD =
AutoConfiguredAuthenticationInitializer.class.getName().concat(".newAuthenticationInitializer");
protected static final String DEFAULT_USERNAME = "test";
protected static final String DEFAULT_PASSWORD = DEFAULT_USERNAME;
protected static final String HTTP_PROTOCOL = "HTTP";
protected static final String SECURITY_CLIENT_AUTH_INIT = "security-client-auth-init";
protected static final String SECURITY_PASSWORD = "security-password";
protected static final String SECURITY_PEER_AUTH_INIT = "security-peer-auth-init";
protected static final String SECURITY_USERNAME = "security-username";
protected static final String PROPERTY_SOURCE_NAME = AutoConfiguredAuthenticationConfiguration.class.getName();
protected static final String SECURITY_CLIENT_AUTH_INIT = GemFireProperties.SECURITY_CLIENT_AUTH_INIT.getName();
protected static final String SECURITY_PEER_AUTH_INIT = GemFireProperties.SECURITY_PEER_AUTH_INIT.getName();
protected static final String SECURITY_USERNAME = AutoConfiguredAuthenticationInitializer.SECURITY_USERNAME_PROPERTY;
protected static final String SECURITY_PASSWORD = AutoConfiguredAuthenticationInitializer.SECURITY_PASSWORD_PROPERTY;
private Logger logger = LoggerFactory.getLogger(getClass());
private final Logger logger = LoggerFactory.getLogger(getClass());
protected @NonNull Logger getLogger() {
return this.logger;
}
protected void logDebug(String message, Object... args) {
Logger logger = getLogger();
if (logger.isDebugEnabled()) {
logger.debug(message, args);
}
}
@Bean("GemFireSecurityAuthenticator")
public Authenticator authenticator(Environment environment) {
public @Nullable Authenticator authenticator(
@Autowired(required = false) @Lazy Authentication<String, String> authentication) {
Authenticator authenticator = new Authenticator() {
return Optional.ofNullable(authentication)
.filter(Authentication::isRequested)
.map(this::newAuthenticator)
.map(this::registerAuthenticator)
.orElse(null);
}
private @NonNull Authenticator newAuthenticator(@NonNull Authentication<String, String> authentication) {
Assert.notNull(authentication, "Authentication must not be null");
Assert.state(authentication.isRequested(), "Authentication was not requested");
return new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
String username =
environment.getProperty(AutoConfiguredAuthenticationInitializer.SDG_SECURITY_USERNAME_PROPERTY,
DEFAULT_USERNAME);
String password =
environment.getProperty(AutoConfiguredAuthenticationInitializer.SDG_SECURITY_PASSWORD_PROPERTY,
DEFAULT_PASSWORD);
String username = authentication.getPrincipal();
String password = authentication.getCredentials();
return new PasswordAuthentication(username, password.toCharArray());
}
};
}
Authenticator.setDefault(authenticator);
private @NonNull Authenticator registerAuthenticator(@NonNull Authenticator authenticator) {
if (authenticator != null) {
Authenticator.setDefault(authenticator);
}
return authenticator;
}
ClientHttpRequestInterceptor loggingAwareClientHttpRequestInterceptor() {
@NonNull ClientHttpRequestInterceptor loggingAwareClientHttpRequestInterceptor() {
return (request, body, execution) -> {
logger.debug("HTTP Request URI [{}]", request.getURI());
logDebug("HTTP Request URI [{}]", request.getURI());
HttpHeaders httpHeaders = request.getHeaders();
CollectionUtils.nullSafeSet(httpHeaders.keySet()).forEach(httpHeaderName ->
logger.debug("HTTP Request Header Name [{}] Value [{}]",
logDebug("HTTP Request Header Name [{}] Value [{}]",
httpHeaderName, httpHeaders.get(httpHeaderName)));
ClientHttpResponse response = execution.execute(request, body);
if (this.logger.isDebugEnabled()) {
try {
this.logger.debug("HTTP Response Status Code [{}] Message [{}]",
response.getRawStatusCode(), response.getStatusText());
}
catch (IOException cause) {
this.logger.debug("Error occurred getting HTTP Response Status Code and Message", cause);
}
try {
logDebug("HTTP Response Status Code [{}] Message [{}]",
response.getStatusCode().value(), response.getStatusText());
}
catch (IOException cause) {
logDebug("Error occurred getting HTTP Response Status Code and Message", cause);
}
return response;
@@ -136,12 +175,11 @@ public class AutoConfiguredAuthenticationConfiguration {
}
@Bean
@Order(Ordered.LOWEST_PRECEDENCE)
public RestTemplateConfigurer loggingAwareRestTemplateConfigurer() {
return restTemplate -> restTemplate.getInterceptors().add(loggingAwareClientHttpRequestInterceptor());
return restTemplate -> restTemplate.getInterceptors().add(loggingAwareClientHttpRequestInterceptor());
}
ClientHttpRequestInterceptor securityAwareClientHttpRequestInterceptor() {
@NonNull ClientHttpRequestInterceptor securityAwareClientHttpRequestInterceptor() {
return (request, body, execution) -> {
@@ -151,15 +189,18 @@ public class AutoConfiguredAuthenticationConfiguration {
Authenticator.requestPasswordAuthentication(uri.getHost(), null, uri.getPort(),
HTTP_PROTOCOL, null, uri.getScheme());
String username = passwordAuthentication.getUserName();
char[] password = passwordAuthentication.getPassword();
if (passwordAuthentication != null) {
if (isAuthenticationEnabled(username, password)) {
String username = passwordAuthentication.getUserName();
char[] password = passwordAuthentication.getPassword();
HttpHeaders requestHeaders = request.getHeaders();
if (isAuthenticationCredentialsSet(username, password)) {
requestHeaders.add(SECURITY_USERNAME, username);
requestHeaders.add(SECURITY_PASSWORD, String.valueOf(password));
HttpHeaders requestHeaders = request.getHeaders();
requestHeaders.add(SECURITY_USERNAME, username);
requestHeaders.add(SECURITY_PASSWORD, String.valueOf(password));
}
}
return execution.execute(request, body);
@@ -167,58 +208,78 @@ public class AutoConfiguredAuthenticationConfiguration {
}
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public RestTemplateConfigurer securityAwareRestTemplateConfigurer(Authenticator authenticator) {
public RestTemplateConfigurer securityAwareRestTemplateConfigurer() {
return restTemplate -> restTemplate.getInterceptors().add(securityAwareClientHttpRequestInterceptor());
}
private boolean isAuthenticationEnabled(String username, char[] password) {
return StringUtils.hasText(username) && password != null && password.length > 0;
private boolean isAuthenticationCredentialsSet(String username, char[] password) {
return StringUtils.hasText(username) && nullSafeCharArray(password).length > 0;
}
private @NonNull char[] nullSafeCharArray(@Nullable char[] array) {
return array != null ? array : EMPTY_CHAR_ARRAY;
}
@Bean
public ClientCacheConfigurer authenticationCredentialsSettingClientCacheConfigurer(Environment environment) {
return (beanName, beanFactory) -> setAuthenticationCredentials(beanFactory.getProperties(), environment);
public ClientCacheConfigurer authenticationInitializingClientCacheConfigurer(
@Autowired(required = false) @Lazy Authentication<String, String> authentication) {
return (beanName, clientCacheFactoryBean) ->
initializeMemberAuthentication(clientCacheFactoryBean.getProperties(), authentication);
}
@Bean
public PeerCacheConfigurer authenticationCredentialsSettingPeerCacheConfigurer(Environment environment) {
return (beanName, beanFactory) -> setAuthenticationCredentials(beanFactory.getProperties(), environment);
public LocatorConfigurer authenticationInitializingLocatorConfigurer(
@Autowired(required = false) @Lazy Authentication<String, String> authentication) {
return (beanName, locatorFactoryBean) ->
initializeMemberAuthentication(locatorFactoryBean.getGemFireProperties(), authentication);
}
private void setAuthenticationCredentials(Properties gemfireProperties, Environment environment) {
@Bean
public PeerCacheConfigurer authenticationInitializingPeerCacheConfigurer(
@Autowired(required = false) @Lazy Authentication<String, String> authentication) {
return (beanName, cacheFactoryBean) ->
initializeMemberAuthentication(cacheFactoryBean.getProperties(), authentication);
}
private void initializeMemberAuthentication(Properties gemfireProperties,
@Nullable Authentication<String, String> authentication) {
Optional.ofNullable(gemfireProperties)
.filter(properties -> isMatch(environment))
.filter(properties -> isAuthenticationRequested(authentication))
.ifPresent(properties -> {
properties.setProperty(SECURITY_CLIENT_AUTH_INIT, AUTO_CONFIGURED_AUTH_INIT_STATIC_FACTORY_METHOD);
properties.setProperty(SECURITY_PEER_AUTH_INIT, AUTO_CONFIGURED_AUTH_INIT_STATIC_FACTORY_METHOD);
});
}
private static boolean isMatch(Environment environment) {
return Optional.ofNullable(environment)
.map(env -> env.getProperty(AutoConfiguredAuthenticationInitializer.SDG_SECURITY_USERNAME_PROPERTY))
.map(StringUtils::hasText)
.isPresent();
private boolean isAuthenticationRequested(@Nullable Authentication<?, ?> authentication) {
return authentication != null && authentication.isRequested();
}
public static class AutoConfiguredAuthenticationCondition implements Condition {
public static class AuthenticationAutoConfigurationEnabledCondition implements ConfigurationCondition {
public static final String SPRING_DATA_GEMFIRE_SECURITY_AUTH_ENABLED =
"spring.data.gemfire.security.auth.auto-configure.enabled";
public static final boolean DEFAULT_ENABLED = true;
private static boolean isEnabled(Environment environment) {
return environment.getProperty(SPRING_DATA_GEMFIRE_SECURITY_AUTH_ENABLED, Boolean.class, true);
public static final String SECURITY_AUTH_AUTO_CONFIGURATION_ENABLED =
"spring.data.gemfire.security.auth.auto-configuration-enabled";
private static boolean isEnabled(@NonNull Environment environment) {
return environment.getProperty(SECURITY_AUTH_AUTO_CONFIGURATION_ENABLED, Boolean.class, DEFAULT_ENABLED);
}
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
public ConfigurationPhase getConfigurationPhase() {
return ConfigurationPhase.PARSE_CONFIGURATION;
}
Environment environment = conditionContext.getEnvironment();
@Override
public boolean matches(@NonNull ConditionContext conditionContext,
@NonNull AnnotatedTypeMetadata annotatedTypeMetadata) {
return isEnabled(environment) && isMatch(environment);
return isEnabled(conditionContext.getEnvironment());
}
}
}

View File

@@ -40,6 +40,8 @@ import org.springframework.context.annotation.Import;
* @see org.apache.geode.security.PostProcessor
* @see org.springframework.context.annotation.Import
* @see org.springframework.data.gemfire.config.annotation.ApacheShiroSecurityConfiguration
* @see org.springframework.data.gemfire.config.annotation.AuthenticationBeanConfiguration
* @see org.springframework.data.gemfire.config.annotation.AutoConfiguredAuthenticationConfiguration
* @see org.springframework.data.gemfire.config.annotation.GeodeIntegratedSecurityConfiguration
* @since 1.0.0
*/
@@ -49,6 +51,7 @@ import org.springframework.context.annotation.Import;
@Documented
@Import({
ApacheShiroSecurityConfiguration.class,
AuthenticationBeanConfiguration.class,
AutoConfiguredAuthenticationConfiguration.class,
GeodeIntegratedSecurityConfiguration.class
})

View File

@@ -20,6 +20,7 @@ import java.lang.annotation.Annotation;
import java.util.Map;
import java.util.Properties;
import org.springframework.data.gemfire.GemFireProperties;
import org.springframework.data.gemfire.config.annotation.support.EmbeddedServiceConfigurationSupport;
import org.springframework.data.gemfire.util.PropertiesBuilder;
@@ -34,11 +35,11 @@ import org.springframework.data.gemfire.util.PropertiesBuilder;
@SuppressWarnings("unused")
public class GeodeIntegratedSecurityConfiguration extends EmbeddedServiceConfigurationSupport {
protected static final String SECURITY_CLIENT_AUTH_INIT = "security-client-auth-init";
protected static final String SECURITY_MANAGER = "security-manager";
protected static final String SECURITY_PEER_AUTH_INIT = "security-peer-auth-init";
protected static final String SECURITY_POST_PROCESSOR = "security-post-processor";
protected static final String SECURITY_SHIRO_INIT = "security-shiro-init";
protected static final String SECURITY_CLIENT_AUTH_INIT = GemFireProperties.SECURITY_CLIENT_AUTH_INIT.getName();
protected static final String SECURITY_MANAGER = GemFireProperties.SECURITY_MANAGER.getName();
protected static final String SECURITY_PEER_AUTH_INIT = GemFireProperties.SECURITY_PEER_AUTH_INIT.getName();
protected static final String SECURITY_POST_PROCESSOR = GemFireProperties.SECURITY_POST_PROCESSOR.getName();
protected static final String SECURITY_SHIRO_INIT = GemFireProperties.SECURITY_SHIRO_INIT.getName();
/**
* Returns the {@link EnableSecurity} {@link java.lang.annotation.Annotation} {@link Class} type.
@@ -52,11 +53,10 @@ public class GeodeIntegratedSecurityConfiguration extends EmbeddedServiceConfigu
}
/**
* Determines whether Pivotal GemFire/Apache Geode's Apache Shiro Security Framework support is enabled
* or available.
* Determines whether Apache Geode's Apache Shiro Security Framework support is enabled or available.
*
* @return a boolean value indicating whether Pivotal GemFire/Apache Geode's Apache Shiro Security Framework
* support is enabled or available.
* @return a boolean value indicating whether Apache Geode's Apache Shiro Security Framework support
* is enabled or available.
* @see #isShiroSecurityNotConfigured()
*/
protected boolean isShiroSecurityConfigured() {
@@ -71,11 +71,10 @@ public class GeodeIntegratedSecurityConfiguration extends EmbeddedServiceConfigu
}
/**
* Determines whether Pivotal GemFire/Apache Geode's Apache Shiro Security Framework support is enabled
* or available.
* Determines whether Apache Geode's Apache Shiro Security Framework support is enabled or available.
*
* @return a boolean value indicating whether Pivotal GemFire/Apache Geode's Apache Shiro Security Framework
* support is enabled or available.
* @return a boolean value indicating whether Apache Geode's Apache Shiro Security Framework support
* is enabled or available.
* @see #isShiroSecurityConfigured()
*/
protected boolean isShiroSecurityNotConfigured() {

View File

@@ -13,13 +13,13 @@
* 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.cache.Cache;
import org.apache.geode.distributed.DistributedMember;
import org.apache.geode.security.AuthInitialize;
import org.apache.geode.security.AuthenticationFailedException;
@@ -27,14 +27,16 @@ import org.apache.geode.security.AuthenticationFailedException;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.data.gemfire.support.WiringDeclarableSupport;
import org.springframework.lang.Nullable;
/**
* 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.
* Abstract class and basic implementation of the {@link AuthInitialize} interface used to authenticate a client
* or peer with a secure Apache Geode cluster.
*
* @author John Blum
* @see java.util.Properties
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.distributed.DistributedMember
* @see org.apache.geode.security.AuthInitialize
* @see org.springframework.context.EnvironmentAware
* @see org.springframework.core.env.Environment
@@ -44,6 +46,7 @@ import org.springframework.data.gemfire.support.WiringDeclarableSupport;
public abstract class AbstractAuthInitialize extends WiringDeclarableSupport
implements AuthInitialize, EnvironmentAware {
@Nullable
private Environment environment;
/**
@@ -54,28 +57,56 @@ public abstract class AbstractAuthInitialize extends WiringDeclarableSupport
*/
@Override
@SuppressWarnings("all")
public void setEnvironment(Environment environment) {
public void setEnvironment(@Nullable Environment environment) {
this.environment = environment;
}
/**
* Returns a reference to the configured Spring {@link Environment}.
* Get an {@link Optional} reference to the configured Spring {@link Environment}.
*
* @return a reference to the configured Spring {@link Environment}.
* @return an {@link Optional} reference to the configured Spring {@link Environment}.
* @see org.springframework.core.env.Environment
* @see java.util.Optional
*/
protected Optional<Environment> getEnvironment() {
return Optional.ofNullable(this.environment);
}
/**
* Initializes this Apache Geode component by auto-wiring (configuring) any dependencies managed by
* the Spring container required during authentication.
*
* @param systemLogWriter {@link LogWriter} for system output.
* @param securityLogWriter {@link LogWriter} for security output.
* @throws AuthenticationFailedException if this Apache Geode node could not be authenticated with
* the Apache Geode distributed system (cluster).
* @see #initialize(Cache, Properties)
* @see #doInit()
*/
@Override
@SuppressWarnings("deprecation")
public final void init(LogWriter logWriter, LogWriter logWriter1) throws AuthenticationFailedException {
public final void init(LogWriter systemLogWriter, LogWriter securityLogWriter) throws AuthenticationFailedException {
doInit();
}
protected void doInit() { }
/**
* Gets the security credentials used to authenticate this Apache Geode node
* with the Apache Geode distributed system (cluster).
*
* @param properties Apache Geode {@link Properties} to configure with authentication credentials
* used during the authentication request.
* @param distributedMember {@link DistributedMember} representing this Apache Geode node.
* @param isPeer boolean value indicating whether this Apache Geode node is joining the cluster
* as a peer or a client.
* @return the given Apache Geode {@link Properties}.
* @throws AuthenticationFailedException if this Apache Geode node could not be authenticated with
* the Apache Geode distributed system (cluster).
* @see org.apache.geode.distributed.DistributedMember
* @see #doGetCredentials(Properties)
* @see java.util.Properties
*/
@Override
public final Properties getCredentials(Properties properties, DistributedMember distributedMember, boolean isPeer)
throws AuthenticationFailedException {
@@ -85,7 +116,4 @@ public abstract class AbstractAuthInitialize extends WiringDeclarableSupport
protected abstract Properties doGetCredentials(Properties properties);
@Override
public void close() { }
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright 2022 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.data.gemfire.config.annotation.support;
import java.util.Objects;
/**
* Abstract Data Type (ADT) and {@link FunctionalInterface} defining a contract to model the details of
* a security {@literal authentication} request.
*
* This ADT is loosely modeled after Spring Security's {@code Authentication} interface.
*
* @author John Blum
* @param <PRINCIPAL> {@link Class type} modeling the {@literal principal} object.
* @param <CREDENTIALS> {@link Class type} modeling the {@literal credentials} object.
* @see java.lang.FunctionalInterface
* @since 3.0.0
*/
@FunctionalInterface
@SuppressWarnings("unused")
public interface Authentication<PRINCIPAL, CREDENTIALS> {
/**
* Determines whether the {@literal principal} has been successfully authenticated.
*
* @return a boolean value indicating whether the {@literal principal} has been successfully authenticated.
*/
default boolean isAuthenticated() {
return false;
}
/**
* Determines whether {@literal authentication} was actually requested.
*
* The default implementation determines whether {@literal authentication} was requested by
* the presence a {@link PRINCIPAL} and {@link CREDENTIALS}. However, even if {@literal authentication}
* was requested, it does not necessarily mean the {@link PRINCIPAL} successfully authenticated.
*
* @return a boolean valuing indicating whether {@literal authentication} was actually requested.
* @see #getCredentials()
* @see #getPrincipal()
*/
default boolean isRequested() {
return Objects.nonNull(getPrincipal()) && Objects.nonNull(getCredentials());
}
/**
* Returns an {@link Object} identifying the {@literal principal} being authenticated.
*
* @return an {@link Object} identifying the {@literal principal} being authenticated.
*/
PRINCIPAL getPrincipal();
/**
* Returns an {@link Object} with credentials that prove the {@literal principal} is correct
* and is who they say they are.
*
* @return Returns an {@link Object} with credentials that prove the {@literal principal} is correct.
*/
default CREDENTIALS getCredentials() {
return null;
}
}

View File

@@ -15,33 +15,40 @@
*/
package org.springframework.data.gemfire.config.annotation.support;
import java.util.Optional;
import java.util.Properties;
import org.apache.geode.security.AuthInitialize;
import org.springframework.util.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.gemfire.GemfireUtils;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
/**
* The {@link AutoConfiguredAuthenticationInitializer} class is an {@link AuthInitialize} implementation,
* which auto-configures security, and specifically authentication, for Apache Geode/Pivotal GemFire.
*
* @author John Blum
* @see java.util.Properties
* @see org.apache.geode.security.AuthInitialize
* @see org.springframework.data.gemfire.config.annotation.support.Authentication
* @since 2.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";
public static final String SECURITY_USERNAME_PROPERTY = SECURITY_USERNAME;
public static final String SECURITY_PASSWORD_PROPERTY = SECURITY_PASSWORD;
protected static final Properties NO_PARAMETERS = new Properties();
private Authentication<String, String> authentication;
/**
* Factory method used to construct a new instance of {@link AutoConfiguredAuthenticationInitializer}.
* Factory method used to construct a new instance of {@link AutoConfiguredAuthenticationInitializer}
* that will be auto-wired with a SDG {@link Authentication} providing Apache Geode Security and Auth
* were configured/requested.
*
* @return a new instance of {@link AutoConfiguredAuthenticationInitializer}.
* @see org.springframework.data.gemfire.config.annotation.support.AutoConfiguredAuthenticationInitializer
@@ -51,24 +58,28 @@ public class AutoConfiguredAuthenticationInitializer extends AbstractAuthInitial
AutoConfiguredAuthenticationInitializer authenticationInitializer =
new AutoConfiguredAuthenticationInitializer();
authenticationInitializer.init(NO_PARAMETERS);
authenticationInitializer.initialize(GemfireUtils.getCache(), NO_PARAMETERS);
return authenticationInitializer;
}
@Autowired(required = false)
public void setAuthentication(@Nullable Authentication<String, String> authentication) {
this.authentication = authentication;
}
protected Optional<Authentication<String, String>> getAuthentication() {
return Optional.ofNullable(this.authentication);
}
@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);
protected @NonNull Properties doGetCredentials(@NonNull Properties properties) {
getAuthentication()
.filter(Authentication::isRequested)
.ifPresent(authentication -> {
properties.setProperty(SECURITY_USERNAME_PROPERTY, authentication.getPrincipal());
properties.setProperty(SECURITY_PASSWORD_PROPERTY, authentication.getCredentials());
});
return properties;

View File

@@ -25,19 +25,21 @@ import org.springframework.beans.factory.BeanFactory;
import org.springframework.data.gemfire.PeerRegionFactoryBean;
/**
* Convenience class for Spring aware, Apache Geode {@link Declarable} components. Provides subclasses with a reference
* to the current Spring {@link BeanFactory} in order to perform Spring bean lookups or resource loading.
* Abstract base class for implementing Spring aware, Apache Geode {@link Declarable} components.
*
* Note, in most cases, the developer should just declare the same components as Spring beans in the Spring container,
* through {@link PeerRegionFactoryBean}, which gives access to the full Spring container capabilities and does not
* enforce the {@link Declarable} interface to be implemented.
* Provides subclasses with a reference to the current Spring {@link BeanFactory} in order to
* perform Spring bean lookups or resource loading.
*
* Note, in most cases, the developer should just declare the same Apache Geode components as Spring beans
* in the Spring container through the {@link PeerRegionFactoryBean}, which gives full access to the Spring container
* capabilities and does not enforce the {@link Declarable} interface to be implemented.
*
* @author Costin Leau
* @author John Blum
* @see org.springframework.beans.factory.BeanFactory
* @see org.springframework.data.gemfire.support.GemfireBeanFactoryLocator
* @see org.apache.geode.cache.CacheCallback
* @see org.apache.geode.cache.Declarable
* @see org.springframework.beans.factory.BeanFactory
* @see org.springframework.data.gemfire.support.GemfireBeanFactoryLocator
*/
@SuppressWarnings("unused")
public abstract class DeclarableSupport implements CacheCallback, Declarable {
@@ -98,9 +100,6 @@ public abstract class DeclarableSupport implements CacheCallback, Declarable {
return newBeanFactoryLocator().useBeanFactory(beanFactoryKey);
}
/**
* @inheritDoc
*/
@Override
public void close() { }

View File

@@ -19,6 +19,7 @@ package org.springframework.data.gemfire.support;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.CacheCallback;
import org.apache.geode.cache.CacheLoader;
import org.apache.geode.cache.Declarable;
@@ -31,20 +32,22 @@ import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.data.gemfire.util.SpringExtensions;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* The {@link LazyWiringDeclarableSupport} class is an implementation of GemFire's {@link Declarable} interface
* that enables support for wiring GemFire components with Spring bean dependencies defined in
* a Spring {@link ApplicationContext}.
* Implementation of Apache Geode's {@link Declarable} interface that enables support for wiring Apache Geode components
* with Spring bean dependencies defined in a Spring {@link ApplicationContext}.
*
* @author John Blum
* @see java.util.Properties
* @see org.apache.geode.cache.Cache
* @see org.apache.geode.cache.CacheCallback
* @see org.apache.geode.cache.Declarable
* @see org.springframework.beans.factory.BeanFactory
* @see org.springframework.beans.factory.DisposableBean
* @see org.springframework.beans.factory.config.ConfigurableListableBeanFactory
* @see org.springframework.context.ApplicationContext
* @see org.springframework.context.ApplicationListener
* @see org.springframework.context.event.ContextRefreshedEvent
@@ -56,8 +59,8 @@ import org.springframework.util.Assert;
public abstract class LazyWiringDeclarableSupport extends WiringDeclarableSupport
implements ApplicationListener<ContextRefreshedEvent>, DisposableBean {
// atomic reference to the parameters passed by GemFire when this Declarable object
// was constructed, configured and its init method called
// Atomic reference to the parameters passed by Apache Geode when this Declarable object
// was constructed, configured and its initialize(..) method called
private final AtomicReference<Properties> parametersReference = new AtomicReference<>();
// condition to determine the initialized state of this Declarable object
@@ -155,16 +158,14 @@ public abstract class LazyWiringDeclarableSupport extends WiringDeclarableSuppor
* @see java.util.Properties
*/
@Override
public final void init(Properties parameters) {
public final void initialize(@Nullable Cache cache, @NonNull Properties parameters) {
// Set a reference to the Apache Geode (configuration) Properties
setParameters(parameters);
try {
doInit(locateBeanFactory(), nullSafeGetParameters());
}
catch (IllegalStateException ignore) {
// BeanFactory does not exist, has been closed or the GemfireBeanFactoryLocator is not in use
}
// A Throwable maybe thrown iff the BeanFactory does not exist, has been closed
// or the GemfireBeanFactoryLocator is not in use.
SpringExtensions.safeDoOperation(() -> doInit(locateBeanFactory(), nullSafeGetParameters()));
}
/**
@@ -182,7 +183,7 @@ public abstract class LazyWiringDeclarableSupport extends WiringDeclarableSuppor
* @see #doPostInit(java.util.Properties)
* @see java.util.Properties
*/
synchronized void doInit(BeanFactory beanFactory, Properties parameters) {
synchronized void doInit(@NonNull BeanFactory beanFactory, @NonNull Properties parameters) {
this.initialized = isInitialized()
|| configureThis(beanFactory, parameters.getProperty(TEMPLATE_BEAN_NAME_PROPERTY));
@@ -200,7 +201,7 @@ public abstract class LazyWiringDeclarableSupport extends WiringDeclarableSuppor
* @see #doInit(BeanFactory, Properties)
* @see java.util.Properties
*/
protected void doPostInit(Properties parameters) { }
protected void doPostInit(@NonNull Properties parameters) { }
/**
* Null-safe operation to return the parameters passed to this {@link Declarable} object when created by GemFire
@@ -210,7 +211,7 @@ public abstract class LazyWiringDeclarableSupport extends WiringDeclarableSuppor
* (e.g. {@literal cache.xml}) and passed to this {@link Declarable} object.
* @see java.util.Properties
*/
protected Properties nullSafeGetParameters() {
protected @NonNull Properties nullSafeGetParameters() {
Properties parameters = this.parametersReference.get();
@@ -224,7 +225,7 @@ public abstract class LazyWiringDeclarableSupport extends WiringDeclarableSuppor
* configuration meta-data (e.g. {@literal cache.xml}) and passed to this {@link Declarable} object.
* @see java.util.Properties
*/
protected void setParameters(Properties parameters) {
protected void setParameters(@Nullable Properties parameters) {
this.parametersReference.set(parameters);
}
@@ -240,7 +241,7 @@ public abstract class LazyWiringDeclarableSupport extends WiringDeclarableSuppor
*/
@Override
@SuppressWarnings("all")
public final void onApplicationEvent(ContextRefreshedEvent event) {
public final void onApplicationEvent(@NonNull ContextRefreshedEvent event) {
ApplicationContext applicationContext = event.getApplicationContext();

View File

@@ -18,11 +18,14 @@ package org.springframework.data.gemfire.support;
import java.util.Properties;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.Declarable;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.wiring.BeanConfigurerSupport;
import org.springframework.beans.factory.wiring.BeanWiringInfo;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
@@ -47,44 +50,41 @@ public abstract class WiringDeclarableSupport extends DeclarableSupport {
protected static final String TEMPLATE_BEAN_NAME_PROPERTY = "bean-name";
/**
* @inheritDoc
*/
@Override
public void init(Properties parameters) {
public void initialize(@Nullable Cache cache, @NonNull Properties parameters) {
configureThis(parameters.getProperty(TEMPLATE_BEAN_NAME_PROPERTY));
}
/**
* Configures this {@link Declarable} object using the bean defined and identified in the Spring {@link BeanFactory}
* with the given {@code name} used as a template for auto-wiring purposes.
* Configures this {@link Declarable} object using a Spring bean defined and identified in the Spring
* {@link BeanFactory} with the given {@link String name} used as a template for the auto-wiring function.
*
* @param templateBeanName {@link String} containing the name of the Spring bean used as a template
* for auto-wiring purposes.
* @param templateBeanName {@link String} containing the {@literal name} of the Spring bean used as a template
* for the auto-wiring function.
* @return a boolean value indicating whether this {@link Declarable} object was successfully configured
* and initialized by the Spring container.
* @see org.springframework.beans.factory.wiring.BeanConfigurerSupport
* @see #configureThis(BeanFactory, String)
* @see #locateBeanFactory()
*/
protected boolean configureThis(String templateBeanName) {
protected boolean configureThis(@Nullable String templateBeanName) {
return configureThis(locateBeanFactory(), templateBeanName);
}
/**
* Configures this {@link Declarable} object using the bean defined and identified in the given
* Spring {@link BeanFactory} with the given {@code name} used as a template for auto-wiring purposes.
* Configures this {@link Declarable} object using a Spring bean defined and identified in the given Spring
* {@link BeanFactory} with the given {@link String name} used as a template for the auto-wiring function.
*
* @param beanFactory Spring {@link BeanFactory} used to configure, auto-wire
* and initialize this {@link Declarable} object.
* @param templateBeanName {@link String} containing the name of the Spring bean used as a template
* for auto-wiring purposes.
* @param beanFactory Spring {@link BeanFactory} used to auto-wire, configure and initialize
* this {@link Declarable} object; must not be {@literal null}
* @param templateBeanName {@link String} containing the {@literal name} of the Spring bean
* used as a template for the auto-wiring function.
* @return a boolean value indicating whether this {@link Declarable} object was successfully configured
* and initialized by the Spring container.
* @see org.springframework.beans.factory.wiring.BeanConfigurerSupport
* @see #newBeanConfigurer(BeanFactory, String)
*/
protected boolean configureThis(BeanFactory beanFactory, String templateBeanName) {
protected boolean configureThis(@NonNull BeanFactory beanFactory, @Nullable String templateBeanName) {
BeanConfigurerSupport beanConfigurer = newBeanConfigurer(beanFactory, templateBeanName);
@@ -95,34 +95,32 @@ public abstract class WiringDeclarableSupport extends DeclarableSupport {
}
/**
* Constructs a new, initialized instance of {@link BeanConfigurerSupport} configured with
* the given Spring {@link BeanFactory}.
* Constructs a new instance of {@link BeanConfigurerSupport} configured with the given Spring {@link BeanFactory}.
*
* @param beanFactory reference to the Spring {@link BeanFactory}.
* @return a new, initialized instance of {@link BeanConfigurerSupport} configured with
* the given Spring {@link BeanFactory}.
* @param beanFactory reference to the Spring {@link BeanFactory}; must not be {@literal null}.
* @return a new {@link BeanConfigurerSupport} configured with the given Spring {@link BeanFactory}.
* @see org.springframework.beans.factory.wiring.BeanConfigurerSupport
* @see org.springframework.beans.factory.BeanFactory
* @see #newBeanConfigurer(BeanFactory, String)
*/
protected BeanConfigurerSupport newBeanConfigurer(BeanFactory beanFactory) {
protected @NonNull BeanConfigurerSupport newBeanConfigurer(@NonNull BeanFactory beanFactory) {
return newBeanConfigurer(beanFactory, null);
}
/**
* Constructs a new, initialized instance of {@link BeanConfigurerSupport} configured with
* the given Spring {@link BeanFactory} and name of a Spring bean defined in the Spring {@link BeanFactory}
* used as a template to wire this {@link Declarable} object.
* Constructs a new instance of {@link BeanConfigurerSupport} configured with the given Spring {@link BeanFactory}
* and {@link String name} of a Spring bean defined in the Spring {@link BeanFactory} used as a template
* to auto-wire this {@link Declarable} object.
*
* @param beanFactory reference to the Spring {@link BeanFactory}.
* @param templateBeanName {@link String} containing the name of a Spring bean in the Spring {@link BeanFactory}
* used as a template to wire this {@link Declarable} object.
* @return a new, initialized instance of {@link BeanConfigurerSupport} configured with
* the given Spring {@link BeanFactory}.
* @param beanFactory reference to the Spring {@link BeanFactory}; must not be {@literal null}.
* @param templateBeanName {@link String} containing the {@literal name} of a Spring bean declared in
* the Spring {@link BeanFactory} used as a template to auto-wire this {@link Declarable} object.
* @return a new {@link BeanConfigurerSupport} configured with the given Spring {@link BeanFactory}.
* @see org.springframework.beans.factory.wiring.BeanConfigurerSupport
* @see org.springframework.beans.factory.BeanFactory
*/
protected BeanConfigurerSupport newBeanConfigurer(BeanFactory beanFactory, String templateBeanName) {
protected @NonNull BeanConfigurerSupport newBeanConfigurer(@NonNull BeanFactory beanFactory,
@Nullable String templateBeanName) {
BeanConfigurerSupport beanConfigurer = new BeanConfigurerSupport();

View File

@@ -64,10 +64,12 @@ import org.springframework.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import lombok.AccessLevel;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
/**
* Abstract base test class for implementing Apache Geode Integrated Security Integration Tests.
@@ -177,7 +179,7 @@ public abstract class AbstractGeodeSecurityIntegrationTests extends ForkingClien
User user = getUser();
return new PropertiesBuilder()
return PropertiesBuilder.create()
.setProperty(SECURITY_USERNAME_PROPERTY, user.getName())
.setProperty(SECURITY_PASSWORD_PROPERTY, user.getCredentials())
.build();
@@ -211,7 +213,6 @@ public abstract class AbstractGeodeSecurityIntegrationTests extends ForkingClien
ClientRegionFactoryBean<String, String> echoRegion = new ClientRegionFactoryBean<>();
echoRegion.setCache(gemfireCache);
echoRegion.setClose(false);
echoRegion.setShortcut(ClientRegionShortcut.PROXY);
return echoRegion;
@@ -277,8 +278,10 @@ public abstract class AbstractGeodeSecurityIntegrationTests extends ForkingClien
private final Set<Role> roles = new HashSet<>();
@NonNull
@lombok.NonNull
private final String name;
@Setter(AccessLevel.PROTECTED)
private String credentials;
public boolean hasPermission(ResourcePermission permission) {
@@ -296,33 +299,23 @@ public abstract class AbstractGeodeSecurityIntegrationTests extends ForkingClien
return this.roles.contains(role);
}
/**
* @inheritDoc
*/
@Override
public Iterator<Role> iterator() {
return Collections.unmodifiableSet(this.roles).iterator();
return Collections.unmodifiableSet(getRoles()).iterator();
}
/**
* @inheritDoc
*/
@Override
public String toString() {
return getName();
}
public User with(String credentials) {
this.credentials = credentials;
setCredentials(credentials);
return this;
}
public User with(Role... roles) {
Collections.addAll(this.roles, roles);
Collections.addAll(getRoles(), roles);
return this;
}
}

View File

@@ -19,91 +19,65 @@ 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 org.junit.After;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
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.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import org.springframework.data.gemfire.GemfireTemplate;
import org.springframework.data.gemfire.GemfireUtils;
import org.springframework.data.gemfire.LocalRegionFactoryBean;
import org.springframework.data.gemfire.client.ClientRegionFactoryBean;
import org.springframework.data.gemfire.tests.integration.ForkingClientServerIntegrationTestsSupport;
import org.springframework.mock.env.MockPropertySource;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
/**
* Integration Tests for auto-configured authentication configuration.
* Integration Tests for {@link AutoConfiguredAuthenticationConfiguration}.
*
* @author John Blum
* @see org.junit.Test
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.cache.Region
* @see org.springframework.data.gemfire.GemfireTemplate
* @see org.springframework.data.gemfire.config.annotation.AutoConfiguredAuthenticationConfiguration
* @see org.springframework.data.gemfire.tests.integration.ForkingClientServerIntegrationTestsSupport
* @see org.springframework.test.context.ContextConfiguration
* @see org.springframework.test.context.junit4.SpringRunner
* @since 1.9.0
*/
@RunWith(SpringRunner.class)
@ContextConfiguration(
classes = AutoConfiguredAuthenticationConfigurationIntegrationTests.TestGemFireClientConfiguration.class
)
@SuppressWarnings("unused")
public class AutoConfiguredAuthenticationConfigurationIntegrationTests
extends ForkingClientServerIntegrationTestsSupport {
private ConfigurableApplicationContext applicationContext;
@BeforeClass
public static void setupGemFireServer() throws Exception {
startGemFireServer(TestGemFireServerConfiguration.class);
}
@After
public void tearDown() {
closeApplicationContext(this.applicationContext);
}
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;
}
@Autowired
private GemfireTemplate echoTemplate;
@Test
@SuppressWarnings("unchecked")
public void clientAuthenticatesWithServer() {
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("Hello")).isEqualTo("Hello");
assertThat(echo.get("TEST")).isEqualTo("TEST");
assertThat(echo.get("Good-Bye")).isEqualTo("Good-Bye");
assertThat(this.echoTemplate.<String, String>get("Hello")).isEqualTo("Hello");
assertThat(this.echoTemplate.<String, String>get("TEST")).isEqualTo("TEST");
assertThat(this.echoTemplate.<String, String>get("Good-Bye")).isEqualTo("Good-Bye");
}
@ClientCacheApplication
@EnableSecurity
@EnableSecurity(securityUsername = SECURITY_USERNAME, securityPassword = SECURITY_PASSWORD)
static class TestGemFireClientConfiguration {
@Bean("Echo")
@@ -112,15 +86,19 @@ public class AutoConfiguredAuthenticationConfigurationIntegrationTests
ClientRegionFactoryBean<Object, Object> echoRegion = new ClientRegionFactoryBean<>();
echoRegion.setCache(gemfireCache);
echoRegion.setClose(false);
echoRegion.setShortcut(ClientRegionShortcut.PROXY);
return echoRegion;
}
@Bean
GemfireTemplate echoTemplate(GemFireCache cache) {
return new GemfireTemplate(cache.getRegion(GemfireUtils.toRegionPath("Echo")));
}
}
@CacheServerApplication
@EnableSecurity(securityManagerClassName = "org.springframework.data.gemfire.config.annotation.TestSecurityManager")
@EnableSecurity(securityManagerClass = TestSecurityManager.class)
static class TestGemFireServerConfiguration {
public static void main(String[] args) {
@@ -134,7 +112,6 @@ public class AutoConfiguredAuthenticationConfigurationIntegrationTests
echoRegion.setCache(gemfireCache);
echoRegion.setCacheLoader(newEchoCacheLoader());
echoRegion.setClose(false);
echoRegion.setPersistent(false);
return echoRegion;
@@ -142,7 +119,7 @@ public class AutoConfiguredAuthenticationConfigurationIntegrationTests
private CacheLoader<Object, Object> newEchoCacheLoader() {
return new CacheLoader<Object, Object>() {
return new CacheLoader<>() {
@Override
public Object load(LoaderHelper<Object, Object> loaderHelper) throws CacheLoaderException {
@@ -150,8 +127,8 @@ public class AutoConfiguredAuthenticationConfigurationIntegrationTests
}
@Override
public void close() {
}
public void close() { }
};
}
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright 2022 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.data.gemfire.config.annotation;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Map;
import java.util.Properties;
import org.junit.Test;
import org.springframework.data.gemfire.test.support.MapBuilder;
/**
* Unit Tests for {@link GeodeIntegratedSecurityConfiguration}.
*
* @author John Blum
* @see java.util.Properties
* @see org.junit.Test
* @see org.springframework.data.gemfire.config.annotation.GeodeIntegratedSecurityConfiguration
* @since 1.0.0
*/
public class GeodeIntegratedSecurityConfigurationUnitTests {
@Test
public void toGemFirePropertiesIsCorrect() {
Map<String, Object> annotationAttributes = MapBuilder.<String, Object>newMapBuilder()
.put("clientAuthenticationInitializer", "example.TestSecurityClientAuthenticationInitialization")
.put("securityManagerClass", TestSecurityManager.class)
.put("securityManagerClassName", " ")
.put("peerAuthenticationInitializer", "example.TestSecurityPeerAuthenticationInitializer")
.put("securityPostProcessorClass", TestSecurityPostProcessor.class)
.put("securityPostProcessorClassName", "")
.build();
GeodeIntegratedSecurityConfiguration configuration = new GeodeIntegratedSecurityConfiguration();
Properties gemfireProperties = configuration.toGemFireProperties(annotationAttributes);
assertThat(gemfireProperties).isNotNull();
assertThat(gemfireProperties).isNotEmpty();
assertThat(gemfireProperties.getProperty(GeodeIntegratedSecurityConfiguration.SECURITY_CLIENT_AUTH_INIT))
.isEqualTo("example.TestSecurityClientAuthenticationInitialization");
assertThat(gemfireProperties.getProperty(GeodeIntegratedSecurityConfiguration.SECURITY_MANAGER))
.isEqualTo(TestSecurityManager.class.getName());
assertThat(gemfireProperties).doesNotContainKey(GeodeIntegratedSecurityConfiguration.SECURITY_SHIRO_INIT);
assertThat(gemfireProperties.getProperty(GeodeIntegratedSecurityConfiguration.SECURITY_PEER_AUTH_INIT))
.isEqualTo("example.TestSecurityPeerAuthenticationInitializer");
assertThat(gemfireProperties.getProperty(GeodeIntegratedSecurityConfiguration.SECURITY_POST_PROCESSOR))
.isEqualTo(TestSecurityPostProcessor.class.getName());
}
private static class TestSecurityPostProcessor implements org.apache.geode.security.PostProcessor {
@Override
public Object processRegionValue(Object principal, String regionName, Object key, Object value) {
return value;
}
}
}

View File

@@ -29,6 +29,7 @@ import org.apache.geode.distributed.DistributedSystem;
import org.apache.geode.distributed.Locator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.gemfire.GemFireProperties;
import org.springframework.data.gemfire.GemfireUtils;
import org.springframework.data.gemfire.tests.integration.IntegrationTestsSupport;
import org.springframework.test.context.ContextConfiguration;
@@ -84,14 +85,15 @@ public class LocatorApplicationIntegrationTests extends IntegrationTestsSupport
try {
peerCache = new CacheFactory()
.set("name", LocatorApplicationIntegrationTests.class.getSimpleName())
.set("bind-address", distributedSystemProperties.getProperty("bind-address"))
.set("cache-xml-file", distributedSystemProperties.getProperty("cache-xml-file"))
.set("jmx-manager", distributedSystemProperties.getProperty("jmx-manager"))
.set("locators", distributedSystemProperties.getProperty("locators"))
.set(GemFireProperties.NAME.getName(), LocatorApplicationIntegrationTests.class.getSimpleName())
.set(GemFireProperties.BIND_ADDRESS.getName(), distributedSystemProperties.getProperty(GemFireProperties.BIND_ADDRESS.getName()))
.set(GemFireProperties.CACHE_XML_FILE.getName(), distributedSystemProperties.getProperty(GemFireProperties.CACHE_XML_FILE.getName()))
.set(GemFireProperties.JMX_MANAGER.getName(), distributedSystemProperties.getProperty(GemFireProperties.JMX_MANAGER.getName()))
.set(GemFireProperties.LOCATORS.getName(), distributedSystemProperties.getProperty(GemFireProperties.LOCATORS.getName()))
//.set("locators", "localhost[0]") // This locators configuration setting causes the test to fail
.set("log-file", distributedSystemProperties.getProperty("log-file"))
.set("log-level", distributedSystemProperties.getProperty("log-level"))
.set(GemFireProperties.LOG_FILE.getName(), distributedSystemProperties.getProperty(GemFireProperties.LOG_FILE.getName()))
.set(GemFireProperties.LOG_LEVEL.getName(), distributedSystemProperties.getProperty(GemFireProperties.LOG_LEVEL.getName()))
.set(GemFireProperties.USE_CLUSTER_CONFIGURATION.getName(), distributedSystemProperties.getProperty(GemFireProperties.USE_CLUSTER_CONFIGURATION.getName()))
.create();
assertThat(peerCache).isNotNull();

View File

@@ -84,8 +84,8 @@ public class SecureLocatorApplicationIntegrationTests extends IntegrationTestsSu
}
@LocatorApplication(port = 0)
//@EnableSecurity(securityManagerClass = TestSecurityManager.class)
@EnableSecurity(securityManagerClassName = "org.springframework.data.gemfire.config.annotation.TestSecurityManager")
@EnableSecurity(securityManagerClass = TestSecurityManager.class)
//@EnableSecurity(securityManagerClassName = "org.springframework.data.gemfire.config.annotation.TestSecurityManager")
static class TestConfiguration { }
static final class MockSecurityManager implements org.apache.geode.security.SecurityManager {

View File

@@ -0,0 +1,117 @@
/*
* Copyright 2022 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.data.gemfire.config.annotation;
import static org.assertj.core.api.Assertions.assertThat;
import java.io.IOException;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.apache.geode.distributed.DistributedSystem;
import org.apache.geode.distributed.Locator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.data.gemfire.tests.integration.ForkingClientServerIntegrationTestsSupport;
import org.springframework.data.gemfire.tests.process.ProcessWrapper;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
/**
* Apache Geode Security Integration Tests testing Apache Geode Locator to Locator (application) authentication.
*
* @author John Blum
* @see org.junit.Test
* @see org.apache.geode.distributed.DistributedSystem
* @see org.apache.geode.distributed.Locator
* @see org.springframework.context.annotation.Profile
* @see org.springframework.data.gemfire.LocatorFactoryBean
* @see org.springframework.data.gemfire.config.annotation.EnableSecurity
* @see org.springframework.data.gemfire.config.annotation.LocatorApplication
* @see org.springframework.data.gemfire.tests.integration.ForkingClientServerIntegrationTestsSupport
* @see org.springframework.test.context.ActiveProfiles
* @see org.springframework.test.context.ContextConfiguration
* @see org.springframework.test.context.junit4.SpringRunner
* @since 1.0.0
*/
@ActiveProfiles("locator-auth-client")
@ContextConfiguration(classes = SecurityManagerSecuredLocatorToLocatorApplicationIntegrationTests.LocatorAuthClient.class)
@RunWith(SpringRunner.class)
@SuppressWarnings("unused")
public class SecurityManagerSecuredLocatorToLocatorApplicationIntegrationTests
extends ForkingClientServerIntegrationTestsSupport {
private static ProcessWrapper locatorProcess;
@BeforeClass
public static void startGeodeLocator() throws IOException {
int locatorPort = findAndReserveAvailablePort();
locatorProcess= run(LocatorAuthServer.class,
"-Dspring.profiles.active=locator-auth-server",
String.format("-Dspring.data.gemfire.locator.port=%d", locatorPort));
waitForServerToStart("localhost", locatorPort);
System.setProperty("spring.data.gemfire.locators", String.format("localhost[%d]", locatorPort));
}
@AfterClass
public static void stopGeodeLocator() {
stop(locatorProcess);
System.clearProperty("spring.data.gemfire.locators");
}
@Autowired
private Locator locator;
@Test
public void locatorIsRunning() {
assertThat(this.locator).isNotNull();
DistributedSystem distributedSystem = this.locator.getDistributedSystem();
assertThat(distributedSystem).isNotNull();
assertThat(distributedSystem.isConnected()).isTrue();
assertThat(distributedSystem.getName()).isEqualTo("LocatorAuthClient");
assertThat(distributedSystem.getDistributedMember().getName()).isEqualTo("LocatorAuthClient");
assertThat(distributedSystem.getAllOtherMembers()).hasSize(1);
}
@LocatorApplication(name = "LocatorAuthServer")
@EnableSecurity(securityManagerClass = TestSecurityManager.class)
@Profile("locator-auth-server")
static class LocatorAuthServer {
public static void main(String[] args) {
runSpringApplication(LocatorAuthServer.class);
block();
}
}
@LocatorApplication(name = "LocatorAuthClient", port = 0)
@EnableSecurity(securityUsername = TestSecurityManager.SECURITY_USERNAME, securityPassword = TestSecurityManager.SECURITY_PASSWORD)
@Profile("locator-auth-client")
static class LocatorAuthClient { }
}

View File

@@ -0,0 +1,221 @@
/*
* Copyright 2022 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.data.gemfire.config.annotation;
import static org.assertj.core.api.Assertions.assertThat;
import java.io.IOException;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.apache.geode.cache.GemFireCache;
import org.apache.geode.cache.RegionShortcut;
import org.apache.geode.internal.security.shiro.GeodePermissionResolver;
import org.apache.shiro.realm.text.PropertiesRealm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Profile;
import org.springframework.data.annotation.Id;
import org.springframework.data.gemfire.GemfireTemplate;
import org.springframework.data.gemfire.GemfireUtils;
import org.springframework.data.gemfire.mapping.annotation.Region;
import org.springframework.data.gemfire.tests.integration.ForkingClientServerIntegrationTestsSupport;
import org.springframework.data.gemfire.tests.process.ProcessWrapper;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
/**
* Integration Tests for {@link LocatorApplication} and {@link LocatorApplicationConfiguration}
* with {@link EnableSecurity} using Apache Shiro.
*
* @author John Blum
* @see org.junit.Test
* @see org.apache.geode.cache.GemFireCache
* @see org.apache.geode.distributed.Locator
* @see org.springframework.context.annotation.Bean
* @see org.springframework.context.annotation.Configuration
* @see org.springframework.context.annotation.Import
* @see org.springframework.data.gemfire.tests.integration.ForkingClientServerIntegrationTestsSupport
* @see org.springframework.test.context.ContextConfiguration
* @see org.springframework.test.context.junit4.SpringRunner
* @since 1.0.0
*/
@SuppressWarnings("unused")
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = ShiroSecuredClusteredLocatorApplicationIntegrationTests.TestClientConfiguration.class)
public class ShiroSecuredClusteredLocatorApplicationIntegrationTests
extends ForkingClientServerIntegrationTestsSupport {
private static ProcessWrapper locatorProcessOne;
private static ProcessWrapper locatorProcessTwo;
private static final String CLUSTER_SECURITY_USERNAME = "root";
private static final String CLUSTER_SECURITY_PASSWORD = "s3c3rt!";
@BeforeClass
public static void assertApacheShiroSecurityEnabled() {
String propertyName = ApacheShiroSecurityConfiguration.ApacheShiroPresentCondition
.SPRING_DATA_GEMFIRE_SECURITY_SHIRO_ENABLED;
String apacheShiroEnabledValue = System.getProperty(propertyName, Boolean.TRUE.toString());
boolean apacheShiroEnabled = Boolean.parseBoolean(apacheShiroEnabledValue);
assertThat(apacheShiroEnabled).isTrue();
}
@BeforeClass
public static void startApacheGeodeCluster() throws IOException {
int locatorPort = findAndReserveAvailablePort();
String locatorBaseName = ShiroSecuredClusteredLocatorApplicationIntegrationTests.class.getSimpleName().concat("%s");
String locatorOneName = String.format(locatorBaseName, "LocatorOne");
String locatorTwoName = String.format(locatorBaseName, "LocatorTwo");
locatorProcessOne = run(createDirectory(locatorOneName), TestLocatorApplication.class,
"-Dspring.profiles.active=auth-server",
String.format("-Dspring.data.gemfire.locator.name=%s", locatorOneName),
String.format("-Dspring.data.gemfire.locator.port=%d", locatorPort));
waitForServerToStart("localhost", locatorPort);
locatorProcessTwo = run(createDirectory(locatorTwoName), TestLocatorApplication.class,
//String.format("-Dspring.data.gemfire.security.username=%s", CLUSTER_SECURITY_USERNAME),
//String.format("-Dspring.data.gemfire.security.password=%s", CLUSTER_SECURITY_PASSWORD),
String.format("-Dspring.data.gemfire.locator.name=%s", locatorTwoName),
String.format("-Dspring.data.gemfire.locators=localhost[%d]", locatorPort));
startGemFireServer(TestServerApplication.class,
//String.format("-Dspring.data.gemfire.security.username=%s", CLUSTER_SECURITY_USERNAME),
//String.format("-Dspring.data.gemfire.security.password=%s", CLUSTER_SECURITY_PASSWORD),
//String.format("-Dspring.data.gemfire.security.username=%s", "guest"),
//String.format("-Dspring.data.gemfire.security.password=%s", "guest"),
String.format("-Dspring.data.gemfire.locators=localhost[%d]", locatorPort));
}
@AfterClass
public static void shutdownApacheGeodeCluster() {
// NOTE: The Apache Geode CacheServer process will be stopped automatically by the STDG framework
// on test class (suite) teardown!
stop(locatorProcessOne);
stop(locatorProcessTwo);
}
@Autowired
private GemfireTemplate customersTemplate;
@Test
public void secureClientCacheCustomersRegionPutAndGetOperationsAreSuccess() {
Customer jonDoe = Customer.as("Jon Doe");
this.customersTemplate.put(jonDoe.getName(), jonDoe);
Customer jonDoeLoaded = this.customersTemplate.get(jonDoe.getName());
assertThat(jonDoeLoaded).isNotNull();
assertThat(jonDoeLoaded).isNotSameAs(jonDoe);
assertThat(jonDoeLoaded).isEqualTo(jonDoe);
}
@Configuration
@EnableEntityDefinedRegions(
basePackageClasses = Customer.class,
serverRegionShortcut = RegionShortcut.LOCAL,
includeFilters = @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = Customer.class)
)
@EnablePdx(includeDomainTypes = Customer.class)
static class TestApplicationConfiguration { }
@Configuration
@EnableSecurity(securityUsername = CLUSTER_SECURITY_USERNAME, securityPassword = CLUSTER_SECURITY_PASSWORD)
static class TestSecurityConfiguration {
@Bean
@Profile("auth-server")
public PropertiesRealm shiroRealm() {
PropertiesRealm propertiesRealm = new PropertiesRealm();
propertiesRealm.setResourcePath("classpath:shiro.properties");
propertiesRealm.setPermissionResolver(new GeodePermissionResolver());
return propertiesRealm;
}
}
@ClientCacheApplication(name = "ShiroSecuredLocatorApplicationIntegrationTestsClientCache")
@EnableSecurity(securityUsername = "scientist", securityPassword = "w0rk!ng4u")
@Import(TestApplicationConfiguration.class)
static class TestClientConfiguration {
@Bean
GemfireTemplate customersTemplate(GemFireCache cache) {
return new GemfireTemplate(cache.getRegion(GemfireUtils.toRegionPath("Customers")));
}
}
@LocatorApplication(name = "ShiroSecuredLocatorApplicationIntegrationTestsLocator", port = 0)
@Import(TestSecurityConfiguration.class)
static class TestLocatorApplication {
public static void main(String[] args) {
assertApacheShiroSecurityEnabled();
runSpringApplication(TestLocatorApplication.class, args);
block();
}
}
@CacheServerApplication(name = "ShiroSecuredLocatorApplicationIntegrationTestsCacheServer")
@Import({ TestApplicationConfiguration.class, TestSecurityConfiguration.class })
static class TestServerApplication {
public static void main(String[] args) {
assertApacheShiroSecurityEnabled();
runSpringApplication(TestServerApplication.class, args);
}
}
@Getter
@EqualsAndHashCode
@ToString(of = "name")
@Region("Customers")
@RequiredArgsConstructor(staticName = "as")
@SuppressWarnings("all")
static class Customer {
@Id @lombok.NonNull
private final String name;
}
}

View File

@@ -15,7 +15,6 @@
*/
package org.springframework.data.gemfire.config.annotation;
import static org.springframework.data.gemfire.config.annotation.TestSecurityManager.TestPrincipal.newPrincipal;
import static org.springframework.data.gemfire.util.RuntimeExceptionFactory.newIllegalArgumentException;
import java.security.Principal;
@@ -27,6 +26,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.apache.geode.security.AuthenticationFailedException;
import org.apache.geode.security.SecurityManager;
import org.springframework.util.StringUtils;
@@ -46,8 +46,8 @@ public final class TestSecurityManager implements org.apache.geode.security.Secu
public static final String SECURITY_USERNAME = "testUser";
public static final String SECURITY_PASSWORD = "&t35t9@55w0rd!";
public static final String SECURITY_USERNAME_PROPERTY = "security-username";
public static final String SECURITY_PASSWORD_PROPERTY = "security-password";
public static final String SECURITY_USERNAME_PROPERTY = SecurityManager.USER_NAME;
public static final String SECURITY_PASSWORD_PROPERTY = SecurityManager.PASSWORD;
private final ConcurrentMap<String, String> authorizedUsers;
@@ -56,7 +56,7 @@ public final class TestSecurityManager implements org.apache.geode.security.Secu
this.authorizedUsers.putIfAbsent(SECURITY_USERNAME, SECURITY_PASSWORD);
}
protected Map<String, String> getAuthorizedUsers() {
private Map<String, String> getAuthorizedUsers() {
return Collections.unmodifiableMap(this.authorizedUsers);
}
@@ -71,7 +71,7 @@ public final class TestSecurityManager implements org.apache.geode.security.Secu
}
private Principal identify(String username, String password) {
return isIdentified(username, password) ? newPrincipal(username) : null;
return isIdentified(username, password) ? TestPrincipal.newPrincipal(username) : null;
}
private boolean isIdentified(String username, String password) {
@@ -93,7 +93,8 @@ public final class TestSecurityManager implements org.apache.geode.security.Secu
}
public TestPrincipal(String name) {
this.name = Optional.ofNullable(name).filter(StringUtils::hasText)
this.name = Optional.ofNullable(name)
.filter(StringUtils::hasText)
.orElseThrow(() -> newIllegalArgumentException("Name is required"));
}

View File

@@ -109,7 +109,7 @@ public class UsingAnnotationConfigWithBeanDefinitionOverridingDisabledIntegratio
assertThat(gemfireProperties).isNotEmpty();
assertThat(gemfireProperties.getProperty(GemFireProperties.SECURITY_MANAGER.getName()))
.isEqualTo(String.valueOf(TestSecurityManager.class));
.isEqualTo(String.valueOf(TestSecurityManager.class.getName()));
assertThat(gemfireProperties.getProperty(GemFireProperties.SSL_KEYSTORE.getName()))
.isEqualTo("/path/to/test/keystore.jks");

View File

@@ -27,11 +27,14 @@ import org.junit.AfterClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.apache.geode.cache.Cache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.data.gemfire.repository.sample.User;
import org.springframework.data.gemfire.tests.integration.IntegrationTestsSupport;
import org.springframework.data.gemfire.tests.mock.CacheMockObjects;
import org.springframework.data.gemfire.tests.support.DataSourceAdapter;
import org.springframework.data.gemfire.util.PropertiesBuilder;
import org.springframework.test.context.ContextConfiguration;
@@ -42,9 +45,12 @@ import org.springframework.util.Assert;
* Integration Tests for {@link LazyWiringDeclarableSupport}.
*
* @author John Blum
* @see java.util.Properties
* @see javax.sql.DataSource
* @see org.junit.Test
* @see org.springframework.context.ApplicationContext
* @see org.springframework.data.gemfire.tests.integration.IntegrationTestsSupport
* @see org.springframework.data.gemfire.tests.mock.CacheMockObjects
* @see org.springframework.test.context.ContextConfiguration
* @see org.springframework.test.context.junit4.SpringRunner
* @since 1.3.4
@@ -66,12 +72,15 @@ public class LazyWiringDeclarableSupportIntegrationTests extends IntegrationTest
@Autowired
private ApplicationContext applicationContext;
private final Cache mockCache = CacheMockObjects
.mockPeerCache("MockCache", null, null);
@Test
public void autoWiringSuccessful() {
TestDeclarable declarable = new TestDeclarable();
declarable.init(createParameters("testParam", "testValue"));
declarable.initialize(this.mockCache, createParameters("testParam", "testValue"));
declarable.onApplicationEvent(new ContextRefreshedEvent(applicationContext));
declarable.assertInitialized();
@@ -85,7 +94,7 @@ public class LazyWiringDeclarableSupportIntegrationTests extends IntegrationTest
TestDeclarable declarable = new TestDeclarable();
declarable.init(createParameters(TEMPLATE_BEAN_NAME_PROPERTY, "declarableTemplateBean"));
declarable.initialize(this.mockCache, createParameters(TEMPLATE_BEAN_NAME_PROPERTY, "declarableTemplateBean"));
declarable.onApplicationEvent(new ContextRefreshedEvent(applicationContext));
declarable.assertInitialized();
@@ -101,7 +110,7 @@ public class LazyWiringDeclarableSupportIntegrationTests extends IntegrationTest
TestDeclarable declarable = new TestDeclarable();
declarable.init(createParameters(TEMPLATE_BEAN_NAME_PROPERTY, "nonExistingBeanTemplate"));
declarable.initialize(this.mockCache, createParameters(TEMPLATE_BEAN_NAME_PROPERTY, "nonExistingBeanTemplate"));
declarable.onApplicationEvent(new ContextRefreshedEvent(applicationContext));
}
catch (IllegalStateException expected) {

View File

@@ -17,10 +17,15 @@
package org.springframework.data.gemfire.support;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static org.springframework.data.gemfire.support.GemfireBeanFactoryLocator.newBeanFactoryLocator;
@@ -30,6 +35,11 @@ import java.util.concurrent.atomic.AtomicReference;
import org.junit.After;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.apache.geode.cache.Cache;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
@@ -42,14 +52,16 @@ import org.springframework.data.gemfire.util.PropertiesBuilder;
* Unit Tests for {@link LazyWiringDeclarableSupport}.
*
* @author John Blum
* @see org.junit.Rule
* @see java.util.Properties
* @see org.junit.Test
* @see org.mockito.Mockito
* @see org.mockito.junit.MockitoJUnitRunner
* @see org.springframework.beans.factory.BeanFactory
* @see org.springframework.data.gemfire.support.GemfireBeanFactoryLocator
* @see org.springframework.data.gemfire.support.LazyWiringDeclarableSupport
* @since 1.3.4
*/
@RunWith(MockitoJUnitRunner.class)
public class LazyWiringDeclarableSupportUnitTests {
private static void assertParameters(Properties parameters, String expectedKey, String expectedValue) {
@@ -63,56 +75,54 @@ public class LazyWiringDeclarableSupportUnitTests {
return PropertiesBuilder.create().setProperty(parameter, value).build();
}
@Mock
private Cache mockCache;
@After
public void tearDown() {
SpringContextBootstrappingInitializer.destroy();
verifyNoInteractions(this.mockCache);
}
@Test
public void assertInitialized() {
LazyWiringDeclarableSupport declarable = new TestLazyWiringDeclarableSupport() {
LazyWiringDeclarableSupport declarable = spy(new TestLazyWiringDeclarableSupport());
@Override
protected boolean isInitialized() {
return true;
}
};
doReturn(true).when(declarable).isInitialized();
try {
declarable.assertInitialized();
}
finally {
SpringContextBootstrappingInitializer.unregister(declarable);
verify(declarable, times(1)).assertInitialized();
verify(declarable, times(1)).isInitialized();
verifyNoMoreInteractions(declarable);
}
}
@Test(expected = IllegalStateException.class)
@Test
public void assertInitializedWhenUninitialized() {
LazyWiringDeclarableSupport declarable = new TestLazyWiringDeclarableSupport() {
LazyWiringDeclarableSupport declarable = spy(new TestLazyWiringDeclarableSupport());
@Override
protected boolean isInitialized() {
return false;
}
};
doReturn(false).when(declarable).isInitialized();
try {
declarable.assertInitialized();
}
catch (IllegalStateException expected) {
assertThat(expected)
.hasMessage("This Declarable object [%s] has not been properly configured and initialized",
declarable.getClass().getName());
assertThat(expected).hasNoCause();
throw expected;
assertThatIllegalStateException()
.isThrownBy(() -> declarable.assertInitialized())
.withMessage("This Declarable object [%s] has not been properly configured and initialized",
declarable.getClass().getName())
.withNoCause();
}
finally {
SpringContextBootstrappingInitializer.unregister(declarable);
verify(declarable, times(1)).assertInitialized();
verify(declarable, times(1)).isInitialized();
verifyNoMoreInteractions(declarable);
}
}
@@ -129,48 +139,43 @@ public class LazyWiringDeclarableSupportUnitTests {
}
}
@Test(expected = IllegalStateException.class)
@Test
public void assertUninitializedWhenInitialized() {
LazyWiringDeclarableSupport declarable = new TestLazyWiringDeclarableSupport() {
LazyWiringDeclarableSupport declarable = spy(new TestLazyWiringDeclarableSupport());
@Override
protected boolean isInitialized() {
return true;
}
};
doReturn(true).when(declarable).isInitialized();
try {
declarable.assertUninitialized();
}
catch (IllegalStateException expected) {
assertThat(expected)
.hasMessage("This Declarable object [%s] has already been configured and initialized",
declarable.getClass().getName());
assertThat(expected).hasNoCause();
throw expected;
assertThatIllegalStateException()
.isThrownBy(() -> declarable.assertUninitialized())
.withMessage("This Declarable object [%s] has already been configured and initialized",
declarable.getClass().getName())
.withNoCause();
}
finally {
SpringContextBootstrappingInitializer.unregister(declarable);
verify(declarable, times(1)).assertUninitialized();
verify(declarable, times(1)).isNotInitialized();
verify(declarable, times(1)).isInitialized();
verifyNoMoreInteractions(declarable);
}
}
@Test
public void init() {
public void initialize() {
LazyWiringDeclarableSupport declarable = new TestLazyWiringDeclarableSupport();
try {
assertThat(declarable.isInitialized()).isFalse();
declarable.init(createParameters("param", "value"));
declarable.initialize(this.mockCache, createParameters("param", "value"));
assertParameters(declarable.nullSafeGetParameters(), "param", "value");
declarable.init(createParameters("newParam", "newValue"));
declarable.initialize(this.mockCache, createParameters("newParam", "newValue"));
assertParameters(declarable.nullSafeGetParameters(), "newParam", "newValue");
assertThat(declarable.isInitialized()).isFalse();
@@ -216,7 +221,7 @@ public class LazyWiringDeclarableSupportUnitTests {
LazyWiringDeclarableSupport declarable = new TestLazyWiringDeclarableSupport();
try {
declarable.init(null);
declarable.initialize(this.mockCache, null);
Properties parameters = declarable.nullSafeGetParameters();
@@ -252,7 +257,7 @@ public class LazyWiringDeclarableSupportUnitTests {
Properties parameters = createParameters("param", "value");
try {
declarable.init(parameters);
declarable.initialize(this.mockCache, parameters);
declarable.onApplicationEvent(new ContextRefreshedEvent(mockApplicationContext));
declarable.assertBeanFactory(mockBeanFactory);
declarable.assertParameters(parameters);
@@ -323,7 +328,7 @@ public class LazyWiringDeclarableSupportUnitTests {
Properties parameters = createParameters("param", "value");
try {
declarable.init(parameters);
declarable.initialize(this.mockCache, parameters);
assertThat(declarable.isInitialized()).isFalse();
assertThat(declarable.nullSafeGetParameters()).isSameAs(parameters);
@@ -357,7 +362,7 @@ public class LazyWiringDeclarableSupportUnitTests {
}
@Test
public void initThenOnApplicationEventThenInitWhenInitialized() {
public void initializeThenOnApplicationEventThenInitializedWhenAlreadyInitialized() {
BeanFactory mockBeanFactory = mock(BeanFactory.class);
@@ -395,7 +400,7 @@ public class LazyWiringDeclarableSupportUnitTests {
assertThat(declarable.nullSafeGetParameters()).isNotSameAs(parameters);
assertThat(doPostInitCalled.get()).isFalse();
declarable.init(parameters);
declarable.initialize(this.mockCache, parameters);
declarable.assertBeanFactory(mockBeanFactory);
declarable.assertParameters(parameters);
@@ -417,7 +422,7 @@ public class LazyWiringDeclarableSupportUnitTests {
expectedValue.set("mockValue");
parameters = createParameters("mockKey", "mockValue");
declarable.init(parameters);
declarable.initialize(this.mockCache, parameters);
declarable.assertBeanFactory(mockBeanFactory);
declarable.assertParameters(parameters);

View File

@@ -17,6 +17,7 @@
# Apache Geode System Users and assigned Roles
user.root = s3c3rt!, ADMIN, DBA
user.dba = g01dD!gg3r4$, DBA
user.scientist = w0rk!ng4u, DATA_SCIENTIST
user.analyst = p@55w0rd, DATA_ANALYST
user.guest = guest, GUEST