Skip to content

Commit 480c7f7

Browse files
committed
Enable Locator applications bootstrapped with SDG to be configured with Security (Auth) using @EnableSecurity.
Closes spring-projects#621.
1 parent 46c229c commit 480c7f7

23 files changed

+1074
-317
lines changed

Diff for: spring-data-geode/src/main/java/org/springframework/data/gemfire/config/annotation/ApacheShiroSecurityConfiguration.java

+11-6
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,17 @@ protected ListableBeanFactory getListableBeanFactory() {
122122
@Bean
123123
public BeanFactoryPostProcessor shiroGemFireBeanFactoryPostProcessor() {
124124

125-
return configurableListableBeanFactory ->
126-
SpringUtils.addDependsOn(configurableListableBeanFactory.getBeanDefinition("gemfireCache"),
127-
"shiroSecurityManager");
125+
return configurableListableBeanFactory -> {
126+
127+
if (configurableListableBeanFactory.containsBean("gemfireCache")) {
128+
SpringUtils.addDependsOn(configurableListableBeanFactory.getBeanDefinition("gemfireCache"),
129+
"shiroSecurityManager");
130+
}
131+
else if (configurableListableBeanFactory.containsBean("locatorApplication")) {
132+
SpringUtils.addDependsOn(configurableListableBeanFactory.getBeanDefinition("locatorApplication"),
133+
"shiroSecurityManager");
134+
}
135+
};
128136
}
129137

130138
/**
@@ -251,9 +259,6 @@ private boolean isEnabled(Environment environment) {
251259
return environment.getProperty(SPRING_DATA_GEMFIRE_SECURITY_SHIRO_ENABLED, Boolean.class, true);
252260
}
253261

254-
/**
255-
* @inheritDoc
256-
*/
257262
@Override
258263
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
259264
return isEnabled(context.getEnvironment()) && isApacheShiroPresent(context);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
/*
2+
* Copyright 2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.gemfire.config.annotation;
17+
18+
import java.lang.annotation.Annotation;
19+
import java.util.function.Supplier;
20+
21+
import org.apache.shiro.util.Assert;
22+
23+
import org.springframework.beans.factory.config.BeanDefinition;
24+
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
25+
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
26+
import org.springframework.beans.factory.support.BeanNameGenerator;
27+
import org.springframework.context.annotation.Bean;
28+
import org.springframework.context.annotation.Configuration;
29+
import org.springframework.context.annotation.ImportAware;
30+
import org.springframework.core.annotation.AnnotationAttributes;
31+
import org.springframework.core.type.AnnotationMetadata;
32+
import org.springframework.data.gemfire.config.annotation.support.AbstractAnnotationConfigSupport;
33+
import org.springframework.data.gemfire.config.annotation.support.Authentication;
34+
import org.springframework.lang.NonNull;
35+
import org.springframework.lang.Nullable;
36+
import org.springframework.util.StringUtils;
37+
38+
/**
39+
* Spring {@link Configuration} class used to configure and register an {@link Authentication} object
40+
* based on security (auth) configuration metadata supplied in the {@link EnableSecurity} annotation.
41+
*
42+
* @author John Blum
43+
* @see java.lang.annotation.Annotation
44+
* @see org.springframework.beans.factory.config.BeanDefinition
45+
* @see org.springframework.beans.factory.support.BeanDefinitionBuilder
46+
* @see org.springframework.beans.factory.support.BeanDefinitionRegistry
47+
* @see org.springframework.beans.factory.support.BeanNameGenerator
48+
* @see org.springframework.context.annotation.Configuration
49+
* @see org.springframework.context.annotation.ImportAware
50+
* @see org.springframework.data.gemfire.config.annotation.support.AbstractAnnotationConfigSupport
51+
* @see org.springframework.data.gemfire.config.annotation.support.Authentication
52+
* @since 1.0.0
53+
*/
54+
@Configuration
55+
public class AuthenticationBeanConfiguration extends AbstractAnnotationConfigSupport implements ImportAware {
56+
57+
private static final char[] EMPTY_CHAR_ARRAY = {};
58+
59+
private String username;
60+
private String password;
61+
62+
@Override
63+
protected Class<? extends Annotation> getAnnotationType() {
64+
return EnableSecurity.class;
65+
}
66+
67+
protected void setUsername(@Nullable String username) {
68+
this.username = username;
69+
}
70+
71+
private @Nullable String getUsername() {
72+
return this.username;
73+
}
74+
75+
protected void setPassword(@Nullable String password) {
76+
this.password = password;
77+
}
78+
79+
private @Nullable String getPassword() {
80+
return this.password;
81+
}
82+
83+
@Override
84+
public void setImportMetadata(@NonNull AnnotationMetadata importMetadata) {
85+
86+
if (isAnnotationPresent(importMetadata)) {
87+
88+
AnnotationAttributes enableSecurityAttributes = getAnnotationAttributes(importMetadata);
89+
90+
setUsername(resolveProperty(securityProperty("username"), String.class,
91+
enableSecurityAttributes.getString("securityUsername")));
92+
93+
setPassword(resolveProperty(securityProperty("password"), String.class,
94+
enableSecurityAttributes.getString("securityPassword")));
95+
}
96+
}
97+
98+
@Bean
99+
@SuppressWarnings("unused")
100+
public @NonNull Authentication<String, String> springDataGeodeAuthentication() {
101+
return SpringDataGeodeAuthentication.from(this::getUsername, this::getPassword);
102+
}
103+
104+
private void registerAuthenticationBean(@NonNull BeanDefinitionRegistry registry,
105+
@NonNull BeanNameGenerator beanNameGenerator) {
106+
107+
if (isAuthenticationCredentialsSet(getUsername(), nullSafeToCharArray(getPassword()))) {
108+
109+
BeanDefinition authenticationBean =
110+
BeanDefinitionBuilder.rootBeanDefinition(SpringDataGeodeAuthentication.class)
111+
.addConstructorArgValue(getUsername())
112+
.addConstructorArgValue(getPassword())
113+
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE)
114+
.getBeanDefinition();
115+
116+
String beanName = beanNameGenerator.generateBeanName(authenticationBean, registry);
117+
118+
registry.registerBeanDefinition(beanName, authenticationBean);
119+
}
120+
}
121+
122+
protected boolean isAuthenticationCredentialsSet(String username, char[] password) {
123+
return StringUtils.hasText(username) && nullSafeCharArray(password).length > 0;
124+
}
125+
126+
private @NonNull char[] nullSafeCharArray(@Nullable char[] array) {
127+
return array != null ? array : EMPTY_CHAR_ARRAY;
128+
}
129+
130+
private @NonNull char[] nullSafeToCharArray(@Nullable String value) {
131+
return value != null ? value.trim().toCharArray() : EMPTY_CHAR_ARRAY;
132+
}
133+
134+
protected static class SpringDataGeodeAuthentication implements Authentication<String, String> {
135+
136+
public static @NonNull SpringDataGeodeAuthentication from(@NonNull Supplier<String> username,
137+
@NonNull Supplier<String> password) {
138+
139+
return new SpringDataGeodeAuthentication(username, password);
140+
}
141+
142+
private final Supplier<String> username;
143+
private final Supplier<String> password;
144+
145+
protected SpringDataGeodeAuthentication(@NonNull Supplier<String> username, @NonNull Supplier<String> password) {
146+
this.username = require(username, "Username [%s] is required", username);
147+
this.password = require(password, "Password [%s] is required", password);
148+
}
149+
150+
private <T> T require(T target, String message, Object... arguments) {
151+
Assert.notNull(target, String.format(message, arguments));
152+
return target;
153+
}
154+
155+
@Override
156+
public boolean isRequested() {
157+
return StringUtils.hasText(getPrincipal()) && StringUtils.hasText(getCredentials());
158+
}
159+
160+
@Override
161+
public @NonNull String getPrincipal() {
162+
return this.username.get();
163+
}
164+
165+
@Override
166+
public @NonNull String getCredentials() {
167+
return this.password.get();
168+
}
169+
170+
@Override
171+
public String toString() {
172+
return getPrincipal();
173+
}
174+
}
175+
}

0 commit comments

Comments
 (0)