Skip to content

Commit 0699fdc

Browse files
committed
Polish 'Allow remote devtools access with Spring Security'
See gh-25868
1 parent 9b2e13a commit 0699fdc

File tree

2 files changed

+18
-75
lines changed

2 files changed

+18
-75
lines changed

spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/security/servlet/ManagementWebSecurityAutoConfiguration.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2020 the original author or authors.
2+
* Copyright 2012-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -48,6 +48,7 @@
4848
* of the custom security configuration.
4949
*
5050
* @author Madhura Bhave
51+
* @author Hatef Palizgar
5152
* @since 2.1.0
5253
*/
5354
@Configuration(proxyBeanMethods = false)

spring-boot-project/spring-boot-actuator-autoconfigure/src/test/java/org/springframework/boot/actuate/autoconfigure/security/servlet/ManagementWebSecurityAutoConfigurationTests.java

Lines changed: 16 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,11 @@
1717
package org.springframework.boot.actuate.autoconfigure.security.servlet;
1818

1919
import java.io.IOException;
20-
import java.util.Arrays;
21-
import java.util.Comparator;
2220
import java.util.List;
23-
import java.util.Map;
24-
import java.util.Optional;
25-
import java.util.concurrent.ConcurrentHashMap;
2621
import java.util.stream.Collectors;
2722

28-
import org.jetbrains.annotations.NotNull;
2923
import org.junit.jupiter.api.Test;
3024

31-
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
32-
import org.springframework.beans.factory.config.BeanDefinitionHolder;
3325
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
3426
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration;
3527
import org.springframework.boot.actuate.autoconfigure.env.EnvironmentEndpointAutoConfiguration;
@@ -44,13 +36,8 @@
4436
import org.springframework.boot.test.context.FilteredClassLoader;
4537
import org.springframework.boot.test.context.assertj.AssertableWebApplicationContext;
4638
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
47-
import org.springframework.context.ConfigurableApplicationContext;
4839
import org.springframework.context.annotation.Bean;
4940
import org.springframework.context.annotation.Configuration;
50-
import org.springframework.core.Ordered;
51-
import org.springframework.core.ResolvableType;
52-
import org.springframework.core.annotation.AnnotationAttributes;
53-
import org.springframework.core.annotation.AnnotationUtils;
5441
import org.springframework.core.annotation.Order;
5542
import org.springframework.http.HttpStatus;
5643
import org.springframework.mock.web.MockFilterChain;
@@ -71,6 +58,7 @@
7158
* Tests for {@link ManagementWebSecurityAutoConfiguration}.
7259
*
7360
* @author Madhura Bhave
61+
* @author Hatef Palizgar
7462
*/
7563
class ManagementWebSecurityAutoConfigurationTests {
7664

@@ -139,7 +127,7 @@ void backOffIfCustomSecurityIsAdded() {
139127
@Test
140128
void backsOffIfSecurityFilterChainBeanIsPresent() {
141129
this.contextRunner.withUserConfiguration(TestSecurityFilterChainConfig.class).run((context) -> {
142-
assertThat(context.getBeansOfType(SecurityFilterChain.class)).isNotEmpty();
130+
assertThat(context.getBeansOfType(SecurityFilterChain.class)).hasSize(1);
143131
assertThat(context.containsBean("testSecurityFilterChain")).isTrue();
144132
});
145133
}
@@ -166,26 +154,19 @@ void backOffIfSaml2RelyingPartyAutoConfigurationPresent() {
166154

167155
@Test
168156
void backOffIfRemoteDevToolsSecurityFilterChainIsPresent() {
169-
this.contextRunner.withUserConfiguration(TestSecurityFilterChainConfig.class).run((context) -> {
170-
List<String> beanNames = getOrderedBeanNames(context);
171-
172-
assertThat(beanNames).containsExactly("testRemoteDevToolsSecurityFilterChain", "testSecurityFilterChain");
173-
assertThat(context.getBeansOfType(SecurityFilterChain.class).size()).isEqualTo(2);
157+
this.contextRunner.withUserConfiguration(TestRemoteDevToolsSecurityFilterChainConfig.class).run((context) -> {
158+
SecurityFilterChain testSecurityFilterChain = context.getBean("testSecurityFilterChain",
159+
SecurityFilterChain.class);
160+
SecurityFilterChain testRemoteDevToolsSecurityFilterChain = context
161+
.getBean("testRemoteDevToolsSecurityFilterChain", SecurityFilterChain.class);
162+
List<SecurityFilterChain> orderedSecurityFilterChains = context.getBeanProvider(SecurityFilterChain.class)
163+
.orderedStream().collect(Collectors.toList());
164+
assertThat(orderedSecurityFilterChains).containsExactly(testRemoteDevToolsSecurityFilterChain,
165+
testSecurityFilterChain);
174166
assertThat(context).doesNotHaveBean(ManagementWebSecurityAutoConfiguration.class);
175-
assertThat(context.containsBean("testRemoteDevToolsSecurityFilterChain")).isTrue();
176167
});
177168
}
178169

179-
@NotNull
180-
private List<String> getOrderedBeanNames(AssertableWebApplicationContext context) {
181-
return Arrays.stream(context.getBeanNamesForType(SecurityFilterChain.class))
182-
.map((beanName) -> Optional.of(context).map(ConfigurableApplicationContext::getBeanFactory)
183-
.map((beanFactory) -> beanFactory.getBeanDefinition(beanName))
184-
.map((beanDefinition) -> new BeanDefinitionHolder(beanDefinition, beanName)).orElse(null))
185-
.sorted(OrderAnnotatedBeanDefinitionComparator.INSTANCE).map(BeanDefinitionHolder::getBeanName)
186-
.collect(Collectors.toList());
187-
}
188-
189170
private HttpStatus getResponseStatus(AssertableWebApplicationContext context, String path)
190171
throws IOException, javax.servlet.ServletException {
191172
FilterChainProxy filterChainProxy = context.getBean(FilterChainProxy.class);
@@ -223,6 +204,11 @@ SecurityFilterChain testSecurityFilterChain(HttpSecurity http) throws Exception
223204
.build();
224205
}
225206

207+
}
208+
209+
@Configuration(proxyBeanMethods = false)
210+
static class TestRemoteDevToolsSecurityFilterChainConfig extends TestSecurityFilterChainConfig {
211+
226212
@Bean
227213
@Order(SecurityProperties.BASIC_AUTH_ORDER - 1)
228214
SecurityFilterChain testRemoteDevToolsSecurityFilterChain(HttpSecurity http) throws Exception {
@@ -232,48 +218,4 @@ SecurityFilterChain testRemoteDevToolsSecurityFilterChain(HttpSecurity http) thr
232218

233219
}
234220

235-
static class OrderAnnotatedBeanDefinitionComparator implements Comparator<BeanDefinitionHolder> {
236-
237-
static final OrderAnnotatedBeanDefinitionComparator INSTANCE = new OrderAnnotatedBeanDefinitionComparator();
238-
239-
private final Map<String, Integer> beanNameToOrder = new ConcurrentHashMap<>();
240-
241-
@Override
242-
public int compare(BeanDefinitionHolder beanOne, BeanDefinitionHolder beanTwo) {
243-
return getOrder(beanOne).compareTo(getOrder(beanTwo));
244-
}
245-
246-
private Integer getOrder(BeanDefinitionHolder bean) {
247-
return this.beanNameToOrder.computeIfAbsent(bean.getBeanName(),
248-
(beanName) -> Optional.of(bean).map(BeanDefinitionHolder::getBeanDefinition)
249-
.filter(AnnotatedBeanDefinition.class::isInstance).map(AnnotatedBeanDefinition.class::cast)
250-
.map(this::getOrderAnnotationAttributesFromFactoryMethod).map(this::getOrder)
251-
.orElse(Ordered.LOWEST_PRECEDENCE));
252-
}
253-
254-
private Integer getOrder(AnnotationAttributes annotationAttributes) {
255-
return Optional.ofNullable(annotationAttributes)
256-
.map((it) -> it.getOrDefault("value", Ordered.LOWEST_PRECEDENCE)).map(Integer.class::cast)
257-
.orElse(Ordered.LOWEST_PRECEDENCE);
258-
}
259-
260-
private AnnotationAttributes getOrderAnnotationAttributesFromFactoryMethod(
261-
AnnotatedBeanDefinition beanDefinition) {
262-
return Optional.of(beanDefinition).map(AnnotatedBeanDefinition::getFactoryMethodMetadata)
263-
.filter((methodMetadata) -> methodMetadata.isAnnotated(Order.class.getName()))
264-
.map((methodMetadata) -> methodMetadata.getAnnotationAttributes(Order.class.getName()))
265-
.map(AnnotationAttributes::fromMap)
266-
.orElseGet(() -> getOrderAnnotationAttributesFromBeanClass(beanDefinition));
267-
}
268-
269-
private AnnotationAttributes getOrderAnnotationAttributesFromBeanClass(AnnotatedBeanDefinition beanDefinition) {
270-
return Optional.of(beanDefinition).map(AnnotatedBeanDefinition::getResolvableType)
271-
.map(ResolvableType::resolve).filter((beanType) -> beanType.isAnnotationPresent(Order.class))
272-
.map((beanType) -> beanType.getAnnotation(Order.class))
273-
.map(AnnotationUtils::getAnnotationAttributes).map(AnnotationAttributes::fromMap).orElse(null);
274-
275-
}
276-
277-
}
278-
279221
}

0 commit comments

Comments
 (0)