Commit 29ab9fd5 authored by Madhura Bhave's avatar Madhura Bhave

Merge branch '2.4.x'

Closes gh-25351
parents df07bc5e b5e17876
...@@ -17,12 +17,14 @@ ...@@ -17,12 +17,14 @@
package org.springframework.boot.devtools.autoconfigure; package org.springframework.boot.devtools.autoconfigure;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.security.SecurityProperties; import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order; import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
...@@ -36,12 +38,9 @@ import org.springframework.security.web.util.matcher.AntPathRequestMatcher; ...@@ -36,12 +38,9 @@ import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
@Configuration(proxyBeanMethods = false) @Configuration(proxyBeanMethods = false)
class RemoteDevtoolsSecurityConfiguration { class RemoteDevtoolsSecurityConfiguration {
@Configuration
static class SecurityConfiguration {
private final String url; private final String url;
SecurityConfiguration(DevToolsProperties devToolsProperties, ServerProperties serverProperties) { RemoteDevtoolsSecurityConfiguration(DevToolsProperties devToolsProperties, ServerProperties serverProperties) {
ServerProperties.Servlet servlet = serverProperties.getServlet(); ServerProperties.Servlet servlet = serverProperties.getServlet();
String servletContextPath = (servlet.getContextPath() != null) ? servlet.getContextPath() : ""; String servletContextPath = (servlet.getContextPath() != null) ? servlet.getContextPath() : "";
this.url = servletContextPath + devToolsProperties.getRemote().getContextPath() + "/restart"; this.url = servletContextPath + devToolsProperties.getRemote().getContextPath() + "/restart";
...@@ -49,12 +48,11 @@ class RemoteDevtoolsSecurityConfiguration { ...@@ -49,12 +48,11 @@ class RemoteDevtoolsSecurityConfiguration {
@Bean @Bean
@Order(SecurityProperties.BASIC_AUTH_ORDER - 1) @Order(SecurityProperties.BASIC_AUTH_ORDER - 1)
SecurityFilterChain configure(HttpSecurity http) throws Exception { @ConditionalOnMissingBean(WebSecurityConfigurerAdapter.class)
SecurityFilterChain devtoolsSecurityFilterChain(HttpSecurity http) throws Exception {
http.requestMatcher(new AntPathRequestMatcher(this.url)).authorizeRequests().anyRequest().anonymous().and() http.requestMatcher(new AntPathRequestMatcher(this.url)).authorizeRequests().anyRequest().anonymous().and()
.csrf().disable(); .csrf().disable();
return http.build(); return http.build();
} }
}
} }
...@@ -45,6 +45,8 @@ import org.springframework.mock.web.MockHttpServletRequest; ...@@ -45,6 +45,8 @@ import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.mock.web.MockServletContext; import org.springframework.mock.web.MockServletContext;
import org.springframework.security.config.BeanIds; import org.springframework.security.config.BeanIds;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.test.web.servlet.setup.MockMvcBuilders;
...@@ -157,6 +159,7 @@ class RemoteDevToolsAutoConfigurationTests { ...@@ -157,6 +159,7 @@ class RemoteDevToolsAutoConfigurationTests {
mockMvc.perform(MockMvcRequestBuilders.get(DEFAULT_CONTEXT_PATH + "/restart").header(DEFAULT_SECRET_HEADER_NAME, mockMvc.perform(MockMvcRequestBuilders.get(DEFAULT_CONTEXT_PATH + "/restart").header(DEFAULT_SECRET_HEADER_NAME,
"supersecret")).andExpect(status().isOk()); "supersecret")).andExpect(status().isOk());
assertRestartInvoked(true); assertRestartInvoked(true);
assertThat(this.context.containsBean("devtoolsSecurityFilterChain")).isTrue();
} }
@Test @Test
...@@ -182,6 +185,25 @@ class RemoteDevToolsAutoConfigurationTests { ...@@ -182,6 +185,25 @@ class RemoteDevToolsAutoConfigurationTests {
mockMvc.perform(MockMvcRequestBuilders.get("/my-path")).andExpect(status().isUnauthorized()); mockMvc.perform(MockMvcRequestBuilders.get("/my-path")).andExpect(status().isUnauthorized());
} }
@Test
void securityConfigurationWhenWebSecurityConfigurerAdapterIsFound2() throws Exception {
this.context = getContext(() -> {
AnnotationConfigServletWebApplicationContext context = new AnnotationConfigServletWebApplicationContext();
context.setServletContext(new MockServletContext());
context.register(Config.class, PropertyPlaceholderAutoConfiguration.class,
TestWebSecurityConfigurerAdapter.class);
TestPropertyValues.of("spring.devtools.remote.secret:supersecret").applyTo(context);
context.refresh();
return context;
});
DispatcherFilter filter = this.context.getBean(DispatcherFilter.class);
MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context).apply(springSecurity()).addFilter(filter)
.build();
mockMvc.perform(MockMvcRequestBuilders.get(DEFAULT_CONTEXT_PATH + "/restart").header(DEFAULT_SECRET_HEADER_NAME,
"supersecret")).andExpect(status().isOk());
assertRestartInvoked(true);
}
@Test @Test
void disableRestart() throws Exception { void disableRestart() throws Exception {
this.context = getContext(() -> loadContext("spring.devtools.remote.secret:supersecret", this.context = getContext(() -> loadContext("spring.devtools.remote.secret:supersecret",
...@@ -250,6 +272,16 @@ class RemoteDevToolsAutoConfigurationTests { ...@@ -250,6 +272,16 @@ class RemoteDevToolsAutoConfigurationTests {
} }
@Configuration(proxyBeanMethods = false)
static class TestWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/foo/**").authorizeRequests().anyRequest().authenticated().and().httpBasic();
}
}
/** /**
* Mock {@link HttpRestartServer} implementation. * Mock {@link HttpRestartServer} implementation.
*/ */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment