diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/CrshAutoConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/CrshAutoConfiguration.java index 9916efe17f4..f10d74ab806 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/CrshAutoConfiguration.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/CrshAutoConfiguration.java @@ -44,8 +44,7 @@ import org.crsh.vfs.spi.AbstractFSDriver; import org.crsh.vfs.spi.FSDriver; import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.actuate.properties.SecurityProperties; -import org.springframework.boot.actuate.properties.SecurityProperties.Management; +import org.springframework.boot.actuate.properties.ManagementServerProperties; import org.springframework.boot.actuate.properties.ShellProperties; import org.springframework.boot.actuate.properties.ShellProperties.CrshShellAuthenticationProperties; import org.springframework.boot.actuate.properties.ShellProperties.CrshShellProperties; @@ -96,8 +95,9 @@ import org.springframework.util.StringUtils; * Security. This authentication method will get enabled if shell.auth is set * to spring or if no explicit shell.auth is provided and a * {@link AuthenticationManager} is available. In the latter case shell access will be - * restricted to users having roles that match those configured in {@link Management}. - * Required roles can be overridden by shell.auth.spring.roles. + * restricted to users having roles that match those configured in + * {@link ManagementServerProperties}. Required roles can be overridden by + * shell.auth.spring.roles. * *

* To add customizations to the shell simply define beans of type {@link CRaSHPlugin} in @@ -119,7 +119,7 @@ import org.springframework.util.StringUtils; @Configuration @ConditionalOnClass({ PluginLifeCycle.class }) @EnableConfigurationProperties({ ShellProperties.class }) -@AutoConfigureAfter(SecurityAutoConfiguration.class) +@AutoConfigureAfter(ManagementSecurityAutoConfiguration.class) public class CrshAutoConfiguration { @Autowired @@ -170,7 +170,7 @@ public class CrshAutoConfiguration { public static class AuthenticationManagerAdapterAutoConfiguration { @Autowired(required = false) - private SecurityProperties securityProperties; + private ManagementServerProperties management; @Bean public CRaSHPlugin shellAuthenticationManager() { @@ -184,9 +184,9 @@ public class CrshAutoConfiguration { // In case no shell.auth property is provided fall back to Spring Security // based authentication and get role to access shell from SecurityProperties. SpringAuthenticationProperties authenticationProperties = new SpringAuthenticationProperties(); - if (this.securityProperties != null) { - authenticationProperties.setRoles(new String[] { this.securityProperties - .getManagement().getRole() }); + if (this.management != null) { + authenticationProperties.setRoles(new String[] { this.management + .getSecurity().getRole() }); } return authenticationProperties; } diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfiguration.java index 02cb85414f5..c1db1f57061 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfiguration.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointWebMvcAutoConfiguration.java @@ -52,7 +52,6 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.event.ContextClosedEvent; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.web.context.WebApplicationContext; -import org.springframework.web.context.support.GenericWebApplicationContext; import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.servlet.DispatcherServlet; @@ -182,7 +181,7 @@ public class EndpointWebMvcAutoConfiguration implements ApplicationContextAware, if (DISABLED_PORT.equals(managementServerProperties.getPort())) { return DISABLE; } - if (!(beanFactory instanceof GenericWebApplicationContext)) { + if (!(beanFactory instanceof WebApplicationContext)) { // Current context is no a a webapp return DIFFERENT; } diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ManagementSecurityAutoConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ManagementSecurityAutoConfiguration.java new file mode 100644 index 00000000000..7de8a357d9a --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ManagementSecurityAutoConfiguration.java @@ -0,0 +1,223 @@ +/* + * Copyright 2012-2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.actuate.autoconfigure; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.annotation.PostConstruct; +import javax.servlet.Filter; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.actuate.endpoint.Endpoint; +import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping; +import org.springframework.boot.actuate.properties.ManagementServerProperties; +import org.springframework.boot.actuate.web.ErrorController; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.security.AuthenticationManagerConfiguration; +import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration; +import org.springframework.boot.autoconfigure.security.SecurityPrequisite; +import org.springframework.boot.autoconfigure.security.SecurityProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.SecurityConfigurer; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.builders.WebSecurity; +import org.springframework.security.config.annotation.web.builders.WebSecurity.IgnoredRequestConfigurer; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.web.AuthenticationEntryPoint; +import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint; + +/** + * {@link EnableAutoConfiguration Auto-configuration} for security of framework endpoints. + * Many aspects of the behavior can be controller with {@link ManagementServerProperties} + * via externalized application properties (or via an bean definition of that type to set + * the defaults). + * + *

+ * The framework {@link Endpoint}s (used to expose application information to operations) + * include a {@link Endpoint#isSensitive() sensitive} configuration option which will be + * used as a security hint by the filter created here. + * + * @author Dave Syer + */ +@Configuration +@ConditionalOnClass({ EnableWebSecurity.class }) +@AutoConfigureAfter(SecurityAutoConfiguration.class) +@EnableConfigurationProperties +public class ManagementSecurityAutoConfiguration { + + private static final String[] NO_PATHS = new String[0]; + + @Bean + @ConditionalOnMissingBean({ IgnoredPathsWebSecurityConfigurerAdapter.class }) + public SecurityConfigurer ignoredPathsWebSecurityConfigurerAdapter() { + return new IgnoredPathsWebSecurityConfigurerAdapter(); + } + + @Configuration + protected static class ManagementSecurityPropertiesConfiguration implements + SecurityPrequisite { + + @Autowired(required = false) + private SecurityProperties security; + + @Autowired(required = false) + private ManagementServerProperties management; + + @PostConstruct + public void init() { + if (this.management != null && this.security != null) { + this.security.getUser().getRole() + .add(this.management.getSecurity().getRole()); + } + } + + } + + // Get the ignored paths in early + @Order(Ordered.HIGHEST_PRECEDENCE + 1) + private static class IgnoredPathsWebSecurityConfigurerAdapter implements + SecurityConfigurer { + + @Autowired(required = false) + private ErrorController errorController; + + @Autowired(required = false) + private EndpointHandlerMapping endpointHandlerMapping; + + @Autowired + private ManagementServerProperties management; + + @Autowired + private SecurityProperties security; + + @Override + public void configure(WebSecurity builder) throws Exception { + } + + @Override + public void init(WebSecurity builder) throws Exception { + IgnoredRequestConfigurer ignoring = builder.ignoring(); + // The ignores are not cumulative, so to prevent overwriting the defaults we + // add them back. + List ignored = SecurityAutoConfiguration.getIgnored(this.security); + ignored.addAll(Arrays.asList(getEndpointPaths(this.endpointHandlerMapping, + false))); + if (!this.management.getSecurity().isEnabled()) { + ignored.addAll(Arrays.asList(getEndpointPaths( + this.endpointHandlerMapping, true))); + } + if (ignored.contains("none")) { + ignored.remove("none"); + } + if (this.errorController != null) { + ignored.add(this.errorController.getErrorPath()); + } + ignoring.antMatchers(ignored.toArray(new String[0])); + } + + } + + @Configuration + @ConditionalOnMissingBean({ ManagementWebSecurityConfigurerAdapter.class }) + @ConditionalOnExpression("${management.security.enabled:true}") + @EnableWebSecurity + // Give user-supplied filters a chance to be last in line + @Order(Ordered.LOWEST_PRECEDENCE - 10) + protected static class ManagementWebSecurityConfigurerAdapter extends + WebSecurityConfigurerAdapter { + + @Autowired + private SecurityProperties security; + + @Autowired + private ManagementServerProperties management; + + @Autowired(required = false) + private EndpointHandlerMapping endpointHandlerMapping; + + @Override + protected void configure(HttpSecurity http) throws Exception { + + // secure endpoints + String[] paths = getEndpointPaths(this.endpointHandlerMapping, true); + if (paths.length > 0 && this.management.getSecurity().isEnabled()) { + // Always protect them if present + if (this.security.isRequireSsl()) { + http.requiresChannel().anyRequest().requiresSecure(); + } + http.exceptionHandling().authenticationEntryPoint(entryPoint()); + http.requestMatchers().antMatchers(paths); + http.authorizeRequests().anyRequest() + .hasRole(this.management.getSecurity().getRole()) // + .and().httpBasic() // + .and().anonymous().disable(); + + // No cookies for management endpoints by default + http.csrf().disable(); + http.sessionManagement().sessionCreationPolicy( + this.management.getSecurity().getSessions()); + + SecurityAutoConfiguration.configureHeaders(http.headers(), + this.security.getHeaders()); + + } + + } + + private AuthenticationEntryPoint entryPoint() { + BasicAuthenticationEntryPoint entryPoint = new BasicAuthenticationEntryPoint(); + entryPoint.setRealmName(this.security.getBasic().getRealm()); + return entryPoint; + } + + @Configuration + @ConditionalOnMissingBean(AuthenticationManager.class) + protected static class ManagementAuthenticationManagerConfiguration extends + AuthenticationManagerConfiguration { + } + + } + + private static String[] getEndpointPaths( + EndpointHandlerMapping endpointHandlerMapping, boolean secure) { + if (endpointHandlerMapping == null) { + return NO_PATHS; + } + + List> endpoints = endpointHandlerMapping.getEndpoints(); + List paths = new ArrayList(endpoints.size()); + for (Endpoint endpoint : endpoints) { + if (endpoint.isSensitive() == secure) { + paths.add(endpoint.getPath()); + } + } + return paths.toArray(new String[paths.size()]); + } + +} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ManagementServerPropertiesAutoConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ManagementServerPropertiesAutoConfiguration.java index 816149ca338..2512e3c86de 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ManagementServerPropertiesAutoConfiguration.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/ManagementServerPropertiesAutoConfiguration.java @@ -38,7 +38,7 @@ public class ManagementServerPropertiesAutoConfiguration { @Bean(name = "org.springframework.actuate.properties.ManagementServerProperties") @ConditionalOnMissingBean - public ManagementServerProperties serverProperties() { + public ManagementServerProperties managementServerProperties() { return new ManagementServerProperties(); } diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/properties/ManagementServerProperties.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/properties/ManagementServerProperties.java index cfaf790f21f..2f0f2638208 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/properties/ManagementServerProperties.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/properties/ManagementServerProperties.java @@ -20,8 +20,11 @@ import java.net.InetAddress; import javax.validation.constraints.NotNull; +import org.springframework.boot.autoconfigure.security.SecurityPrequisite; import org.springframework.boot.context.embedded.properties.ServerProperties; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.util.ClassUtils; /** * Properties for the management server (e.g. port and path settings). @@ -30,7 +33,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; * @see ServerProperties */ @ConfigurationProperties(name = "management", ignoreUnknownFields = false) -public class ManagementServerProperties { +public class ManagementServerProperties implements SecurityPrequisite { private Integer port; @@ -41,6 +44,8 @@ public class ManagementServerProperties { private boolean allowShutdown = false; + private Security security = maybeCreateSecurity(); + public boolean isAllowShutdown() { return this.allowShutdown; } @@ -82,4 +87,50 @@ public class ManagementServerProperties { this.contextPath = contextPath; } + public Security getSecurity() { + return this.security; + } + + public static class Security { + + private boolean enabled = true; + + private String role = "ADMIN"; + + private SessionCreationPolicy sessions = SessionCreationPolicy.STATELESS; + + public SessionCreationPolicy getSessions() { + return this.sessions; + } + + public void setSessions(SessionCreationPolicy sessions) { + this.sessions = sessions; + } + + public void setRole(String role) { + this.role = role; + } + + public String getRole() { + return this.role; + } + + public boolean isEnabled() { + return this.enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + } + + private static Security maybeCreateSecurity() { + if (ClassUtils + .isPresent("org.springframework.security.core.Authentication", null)) { + return new Security(); + } + return null; + } + } diff --git a/spring-boot-actuator/src/main/resources/META-INF/spring.factories b/spring-boot-actuator/src/main/resources/META-INF/spring.factories index 89736607c7d..18e4f406314 100644 --- a/spring-boot-actuator/src/main/resources/META-INF/spring.factories +++ b/spring-boot-actuator/src/main/resources/META-INF/spring.factories @@ -7,6 +7,6 @@ org.springframework.boot.actuate.autoconfigure.ErrorMvcAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.ManagementServerPropertiesAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.MetricFilterAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.MetricRepositoryAutoConfiguration,\ -org.springframework.boot.actuate.autoconfigure.SecurityAutoConfiguration,\ +org.springframework.boot.actuate.autoconfigure.ManagementSecurityAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.TraceRepositoryAutoConfiguration,\ org.springframework.boot.actuate.autoconfigure.TraceWebFilterAutoConfiguration diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/CrshAutoConfigurationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/CrshAutoConfigurationTests.java index 2e0d3a811b2..bf5d4a2242b 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/CrshAutoConfigurationTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/CrshAutoConfigurationTests.java @@ -33,6 +33,7 @@ import org.crsh.processor.term.ProcessorIOHandler; import org.crsh.vfs.Resource; import org.junit.After; import org.junit.Test; +import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.mock.env.MockEnvironment; diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/ManagementSecurityAutoConfigurationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/ManagementSecurityAutoConfigurationTests.java new file mode 100644 index 00000000000..8b34ecbdd39 --- /dev/null +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/ManagementSecurityAutoConfigurationTests.java @@ -0,0 +1,177 @@ +/* + * Copyright 2012-2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.actuate.autoconfigure; + +import org.junit.Test; +import org.springframework.boot.TestUtils; +import org.springframework.boot.autoconfigure.AutoConfigurationReportLoggingInitializer; +import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration; +import org.springframework.boot.context.initializer.LoggingApplicationContextInitializer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.mock.web.MockServletContext; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.ProviderManager; +import org.springframework.security.authentication.TestingAuthenticationToken; +import org.springframework.security.authentication.dao.DaoAuthenticationProvider; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.authority.AuthorityUtils; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.web.FilterChainProxy; +import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +/** + * Tests for {@link ManagementSecurityAutoConfiguration}. + * + * @author Dave Syer + */ +public class ManagementSecurityAutoConfigurationTests { + + private AnnotationConfigWebApplicationContext context; + + @Test + public void testWebConfiguration() throws Exception { + this.context = new AnnotationConfigWebApplicationContext(); + this.context.setServletContext(new MockServletContext()); + this.context.register(SecurityAutoConfiguration.class, + ManagementSecurityAutoConfiguration.class, + EndpointAutoConfiguration.class, EndpointWebMvcAutoConfiguration.class, + ManagementServerPropertiesAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class); + this.context.refresh(); + assertNotNull(this.context.getBean(AuthenticationManager.class)); + // 6 for static resources, one for management endpoints and one for the rest + assertEquals(8, this.context.getBean(FilterChainProxy.class).getFilterChains() + .size()); + } + + @Test + public void testWebConfigurationWithExtraRole() throws Exception { + this.context = new AnnotationConfigWebApplicationContext(); + this.context.setServletContext(new MockServletContext()); + this.context.register(EndpointAutoConfiguration.class, + EndpointWebMvcAutoConfiguration.class, + ManagementServerPropertiesAutoConfiguration.class, + SecurityAutoConfiguration.class, + ManagementSecurityAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class); + debugRefresh(this.context); + UserDetails user = getUser(); + assertTrue(user.getAuthorities().containsAll( + AuthorityUtils + .commaSeparatedStringToAuthorityList("ROLE_USER,ROLE_ADMIN"))); + } + + private UserDetails getUser() { + ProviderManager manager = this.context.getBean(ProviderManager.class); + DaoAuthenticationProvider provider = (DaoAuthenticationProvider) manager + .getProviders().get(0); + UserDetailsService service = (UserDetailsService) ReflectionTestUtils.getField( + provider, "userDetailsService"); + UserDetails user = service.loadUserByUsername("user"); + return user; + } + + @Test + public void testDisableIgnoredStaticApplicationPaths() throws Exception { + this.context = new AnnotationConfigWebApplicationContext(); + this.context.setServletContext(new MockServletContext()); + this.context.register(SecurityAutoConfiguration.class, + ManagementSecurityAutoConfiguration.class, + EndpointAutoConfiguration.class, + ManagementServerPropertiesAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class); + TestUtils.addEnviroment(this.context, "security.ignored:none"); + this.context.refresh(); + // Just the application and management endpoints now + assertEquals(2, this.context.getBean(FilterChainProxy.class).getFilterChains() + .size()); + } + + @Test + public void testDisableBasicAuthOnApplicationPaths() throws Exception { + this.context = new AnnotationConfigWebApplicationContext(); + this.context.setServletContext(new MockServletContext()); + this.context.register(SecurityAutoConfiguration.class, + ManagementSecurityAutoConfiguration.class, + EndpointAutoConfiguration.class, EndpointWebMvcAutoConfiguration.class, + ManagementServerPropertiesAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class); + TestUtils.addEnviroment(this.context, "security.basic.enabled:false"); + this.context.refresh(); + // Just the management endpoints (one filter) and ignores now + assertEquals(7, this.context.getBean(FilterChainProxy.class).getFilterChains() + .size()); + } + + @Test + public void testOverrideAuthenticationManager() throws Exception { + this.context = new AnnotationConfigWebApplicationContext(); + this.context.setServletContext(new MockServletContext()); + this.context.register(TestConfiguration.class, + + SecurityAutoConfiguration.class, ManagementSecurityAutoConfiguration.class, + EndpointAutoConfiguration.class, + ManagementServerPropertiesAutoConfiguration.class, + PropertyPlaceholderAutoConfiguration.class); + this.context.refresh(); + assertEquals(this.context.getBean(TestConfiguration.class).authenticationManager, + this.context.getBean(AuthenticationManager.class)); + } + + private static AnnotationConfigWebApplicationContext debugRefresh( + AnnotationConfigWebApplicationContext context) { + TestUtils.addEnviroment(context, "debug:true"); + LoggingApplicationContextInitializer logging = new LoggingApplicationContextInitializer(); + logging.initialize(context); + AutoConfigurationReportLoggingInitializer initializer = new AutoConfigurationReportLoggingInitializer(); + initializer.initialize(context); + context.refresh(); + initializer.onApplicationEvent(new ContextRefreshedEvent(context)); + return context; + } + + @Configuration + protected static class TestConfiguration { + + private AuthenticationManager authenticationManager; + + @Bean + public AuthenticationManager myAuthenticationManager() { + this.authenticationManager = new AuthenticationManager() { + + @Override + public Authentication authenticate(Authentication authentication) + throws AuthenticationException { + return new TestingAuthenticationToken("foo", "bar"); + } + }; + return this.authenticationManager; + } + + } + +} diff --git a/spring-boot-autoconfigure/pom.xml b/spring-boot-autoconfigure/pom.xml index 6ec743b3506..72904f87364 100644 --- a/spring-boot-autoconfigure/pom.xml +++ b/spring-boot-autoconfigure/pom.xml @@ -126,6 +126,16 @@ spring-security-acl true + + org.springframework.security + spring-security-web + true + + + org.springframework.security + spring-security-config + true + org.springframework.amqp spring-rabbit diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/AuthenticationManagerConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/AuthenticationManagerConfiguration.java new file mode 100644 index 00000000000..fab39e5372f --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/AuthenticationManagerConfiguration.java @@ -0,0 +1,69 @@ +/* + * Copyright 2012-2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.security; + +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.security.SecurityProperties.User; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.ObjectPostProcessor; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.authentication.configurers.provisioning.InMemoryUserDetailsManagerConfigurer; + +@Configuration +public class AuthenticationManagerConfiguration { + + private static Log logger = LogFactory + .getLog(AuthenticationManagerConfiguration.class); + + @Autowired + private SecurityProperties security; + + @Autowired + private List dependencies; + + @Bean + public AuthenticationManager authenticationManager( + ObjectPostProcessor objectPostProcessor) throws Exception { + + InMemoryUserDetailsManagerConfigurer builder = new AuthenticationManagerBuilder( + objectPostProcessor).inMemoryAuthentication(); + User user = this.security.getUser(); + + if (user.isDefaultPassword()) { + logger.info("\n\nUsing default password for application endpoints: " + + user.getPassword() + "\n\n"); + } + + // TODO: Add the management role... + Set roles = new LinkedHashSet(user.getRole()); + + builder.withUser(user.getName()).password(user.getPassword()) + .roles(roles.toArray(new String[roles.size()])); + + return builder.and().build(); + + } + +} \ No newline at end of file diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/SecurityAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/SecurityAutoConfiguration.java similarity index 56% rename from spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/SecurityAutoConfiguration.java rename to spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/SecurityAutoConfiguration.java index 844c4b5cdf7..c98562c9617 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/SecurityAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/SecurityAutoConfiguration.java @@ -14,30 +14,21 @@ * limitations under the License. */ -package org.springframework.boot.actuate.autoconfigure; +package org.springframework.boot.autoconfigure.security; import java.util.ArrayList; import java.util.Arrays; -import java.util.LinkedHashSet; import java.util.List; -import java.util.Set; import javax.servlet.Filter; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.actuate.endpoint.Endpoint; -import org.springframework.boot.actuate.endpoint.mvc.EndpointHandlerMapping; -import org.springframework.boot.actuate.properties.ManagementServerProperties; -import org.springframework.boot.actuate.properties.SecurityProperties; -import org.springframework.boot.actuate.properties.SecurityProperties.Headers; -import org.springframework.boot.actuate.properties.SecurityProperties.User; -import org.springframework.boot.actuate.web.ErrorController; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.security.SecurityProperties.Headers; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -47,10 +38,7 @@ import org.springframework.security.authentication.AuthenticationEventPublisher; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.DefaultAuthenticationEventPublisher; import org.springframework.security.authentication.ProviderManager; -import org.springframework.security.config.annotation.ObjectPostProcessor; import org.springframework.security.config.annotation.SecurityConfigurer; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; -import org.springframework.security.config.annotation.authentication.configurers.provisioning.InMemoryUserDetailsManagerConfigurer; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity.IgnoredRequestConfigurer; @@ -76,11 +64,6 @@ import org.springframework.security.web.util.matcher.AnyRequestMatcher; * events. * *

- * The framework {@link Endpoint}s (used to expose application information to operations) - * include a {@link Endpoint#isSensitive() sensitive} configuration option which will be - * used as a security hint by the filter created here. - * - *

* Some common simple customizations: *

    *
  • Switch off security completely and permanently: remove Spring Security from the @@ -95,14 +78,15 @@ import org.springframework.security.web.util.matcher.AnyRequestMatcher; * @author Dave Syer */ @Configuration +@EnableConfigurationProperties @ConditionalOnClass({ EnableWebSecurity.class }) @ConditionalOnMissingBean(annotation = EnableWebSecurity.class) -@EnableConfigurationProperties public class SecurityAutoConfiguration { - private static final String[] NO_PATHS = new String[0]; + private static List DEFAULT_IGNORED = Arrays.asList("/css/**", "/js/**", + "/images/**", "/**/favicon.ico"); - @Bean(name = "org.springframework.actuate.properties.SecurityProperties") + @Bean(name = "org.springframework.autoconfigure.security.SecurityProperties") @ConditionalOnMissingBean public SecurityProperties securityProperties() { return new SecurityProperties(); @@ -114,41 +98,18 @@ public class SecurityAutoConfiguration { return new DefaultAuthenticationEventPublisher(); } - @Bean - @ConditionalOnMissingBean({ ApplicationWebSecurityConfigurerAdapter.class }) - @ConditionalOnExpression("${security.basic.enabled:true}") - public WebSecurityConfigurerAdapter applicationWebSecurityConfigurerAdapter() { - return new ApplicationWebSecurityConfigurerAdapter(); - } - - @Bean - @ConditionalOnMissingBean({ ManagementWebSecurityConfigurerAdapter.class }) - @ConditionalOnExpression("${security.management.enabled:true}") - public WebSecurityConfigurerAdapter managementWebSecurityConfigurerAdapter() { - return new ManagementWebSecurityConfigurerAdapter(); - } - @Bean @ConditionalOnMissingBean({ IgnoredPathsWebSecurityConfigurerAdapter.class }) + @ConditionalOnBean(annotation = EnableWebSecurity.class) public SecurityConfigurer ignoredPathsWebSecurityConfigurerAdapter() { return new IgnoredPathsWebSecurityConfigurerAdapter(); } // Get the ignored paths in early @Order(Ordered.HIGHEST_PRECEDENCE) - @EnableWebSecurity private static class IgnoredPathsWebSecurityConfigurerAdapter implements SecurityConfigurer { - private static List DEFAULT_IGNORED = Arrays.asList("/css/**", "/js/**", - "/images/**", "/**/favicon.ico"); - - @Autowired(required = false) - private ErrorController errorController; - - @Autowired(required = false) - private EndpointHandlerMapping endpointHandlerMapping; - @Autowired private SecurityProperties security; @@ -159,29 +120,18 @@ public class SecurityAutoConfiguration { @Override public void init(WebSecurity builder) throws Exception { IgnoredRequestConfigurer ignoring = builder.ignoring(); - ignoring.antMatchers(getEndpointPaths(this.endpointHandlerMapping, false)); - List ignored = new ArrayList(this.security.getIgnored()); - if (!this.security.getManagement().isEnabled()) { - ignored.addAll(Arrays.asList(getEndpointPaths( - this.endpointHandlerMapping, true))); - } - if (ignored.isEmpty()) { - ignored.addAll(DEFAULT_IGNORED); - } - else if (ignored.contains("none")) { - ignored.remove("none"); - } - if (this.errorController != null) { - ignored.add(this.errorController.getErrorPath()); - } + List ignored = getIgnored(this.security); ignoring.antMatchers(ignored.toArray(new String[0])); } } - // Give user-supplied filters a chance to be last in line + @ConditionalOnMissingBean({ ApplicationWebSecurityConfigurerAdapter.class }) + @ConditionalOnExpression("${security.basic.enabled:true}") + @Configuration + @EnableWebSecurity @Order(Ordered.LOWEST_PRECEDENCE - 5) - private static class ApplicationWebSecurityConfigurerAdapter extends + protected static class ApplicationWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter { @Autowired @@ -201,8 +151,10 @@ public class SecurityAutoConfiguration { if (this.security.getBasic().isEnabled() && paths.length > 0) { http.exceptionHandling().authenticationEntryPoint(entryPoint()); http.requestMatchers().antMatchers(paths); - http.authorizeRequests().anyRequest() - .hasRole(this.security.getUser().getRole()) // + http.authorizeRequests() + .anyRequest() + .hasAnyRole( + this.security.getUser().getRole().toArray(new String[0])) // .and().httpBasic() // .and().anonymous().disable(); } @@ -247,111 +199,15 @@ public class SecurityAutoConfiguration { return manager; } - } - - // Give user-supplied filters a chance to be last in line - @Order(Ordered.LOWEST_PRECEDENCE - 10) - private static class ManagementWebSecurityConfigurerAdapter extends - WebSecurityConfigurerAdapter { - - @Autowired - private SecurityProperties security; - - @Autowired - private ManagementServerProperties management; - - @Autowired(required = false) - private EndpointHandlerMapping endpointHandlerMapping; - - @Override - protected void configure(HttpSecurity http) throws Exception { - - // secure endpoints - String[] paths = getEndpointPaths(this.endpointHandlerMapping, true); - if (paths.length > 0 && this.security.getManagement().isEnabled()) { - // Always protect them if present - if (this.security.isRequireSsl()) { - http.requiresChannel().anyRequest().requiresSecure(); - } - http.exceptionHandling().authenticationEntryPoint(entryPoint()); - http.requestMatchers().antMatchers(paths); - http.authorizeRequests().anyRequest() - .hasRole(this.security.getManagement().getRole()) // - .and().httpBasic() // - .and().anonymous().disable(); - - // No cookies for management endpoints by default - http.csrf().disable(); - http.sessionManagement().sessionCreationPolicy( - this.security.getManagement().getSessions()); - - SecurityAutoConfiguration.configureHeaders(http.headers(), - this.security.getHeaders()); - - } - - } - - private AuthenticationEntryPoint entryPoint() { - BasicAuthenticationEntryPoint entryPoint = new BasicAuthenticationEntryPoint(); - entryPoint.setRealmName(this.security.getBasic().getRealm()); - return entryPoint; + @Configuration + @ConditionalOnMissingBean(AuthenticationManager.class) + protected static class ApplicationAuthenticationManagerConfiguration extends + AuthenticationManagerConfiguration { } } - @ConditionalOnMissingBean(AuthenticationManager.class) - @Configuration - public static class AuthenticationManagerConfiguration { - - private static Log logger = LogFactory - .getLog(AuthenticationManagerConfiguration.class); - - @Autowired - private SecurityProperties security; - - @Bean - public AuthenticationManager authenticationManager( - ObjectPostProcessor objectPostProcessor) throws Exception { - - InMemoryUserDetailsManagerConfigurer builder = new AuthenticationManagerBuilder( - objectPostProcessor).inMemoryAuthentication(); - User user = this.security.getUser(); - - if (user.isDefaultPassword()) { - logger.info("\n\nUsing default password for application endpoints: " - + user.getPassword() + "\n\n"); - } - - Set roles = new LinkedHashSet(Arrays.asList(this.security - .getManagement().getRole(), user.getRole())); - - builder.withUser(user.getName()).password(user.getPassword()) - .roles(roles.toArray(new String[roles.size()])); - - return builder.and().build(); - - } - - } - - private static String[] getEndpointPaths( - EndpointHandlerMapping endpointHandlerMapping, boolean secure) { - if (endpointHandlerMapping == null) { - return NO_PATHS; - } - - List> endpoints = endpointHandlerMapping.getEndpoints(); - List paths = new ArrayList(endpoints.size()); - for (Endpoint endpoint : endpoints) { - if (endpoint.isSensitive() == secure) { - paths.add(endpoint.getPath()); - } - } - return paths.toArray(new String[paths.size()]); - } - - private static void configureHeaders(HeadersConfigurer configurer, + public static void configureHeaders(HeadersConfigurer configurer, SecurityProperties.Headers headers) throws Exception { if (headers.getHsts() != Headers.HSTS.none) { boolean includeSubdomains = headers.getHsts() == Headers.HSTS.all; @@ -373,4 +229,15 @@ public class SecurityAutoConfiguration { } } + public static List getIgnored(SecurityProperties security) { + List ignored = new ArrayList(security.getIgnored()); + if (ignored.isEmpty()) { + ignored.addAll(DEFAULT_IGNORED); + } + else if (ignored.contains("none")) { + ignored.remove("none"); + } + return ignored; + } + } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/SecurityPrequisite.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/SecurityPrequisite.java new file mode 100644 index 00000000000..d3ff94d698d --- /dev/null +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/SecurityPrequisite.java @@ -0,0 +1,27 @@ +/* + * Copyright 2012-2013 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.autoconfigure.security; + +/** + * Marker interface for beans that need to be initialized before any security + * configuration is evaluated. + * + * @author Dave Syer + */ +public interface SecurityPrequisite { + +} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/properties/SecurityProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/SecurityProperties.java similarity index 82% rename from spring-boot-actuator/src/main/java/org/springframework/boot/actuate/properties/SecurityProperties.java rename to spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/SecurityProperties.java index 72b32f5618c..55bf167fa29 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/properties/SecurityProperties.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/SecurityProperties.java @@ -14,9 +14,10 @@ * limitations under the License. */ -package org.springframework.boot.actuate.properties; +package org.springframework.boot.autoconfigure.security; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.UUID; @@ -30,7 +31,7 @@ import org.springframework.util.StringUtils; * @author Dave Syer */ @ConfigurationProperties(name = "security", ignoreUnknownFields = false) -public class SecurityProperties { +public class SecurityProperties implements SecurityPrequisite { private boolean requireSsl; @@ -45,8 +46,6 @@ public class SecurityProperties { private List ignored = new ArrayList(); - private Management management = new Management(); - private User user = new User(); public Headers getHeaders() { @@ -57,10 +56,6 @@ public class SecurityProperties { return this.user; } - public Management getManagement() { - return this.management; - } - public SessionCreationPolicy getSessions() { return this.sessions; } @@ -193,47 +188,13 @@ public class SecurityProperties { } - public static class Management { - - private boolean enabled = true; - - private String role = "ADMIN"; - - private SessionCreationPolicy sessions = SessionCreationPolicy.STATELESS; - - public SessionCreationPolicy getSessions() { - return this.sessions; - } - - public void setSessions(SessionCreationPolicy sessions) { - this.sessions = sessions; - } - - public void setRole(String role) { - this.role = role; - } - - public String getRole() { - return this.role; - } - - public boolean isEnabled() { - return this.enabled; - } - - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - - } - public static class User { private String name = "user"; private String password = UUID.randomUUID().toString(); - private String role = "USER"; + private List role = new ArrayList(Arrays.asList("USER")); private boolean defaultPassword = true; @@ -258,14 +219,10 @@ public class SecurityProperties { this.password = password; } - public String getRole() { + public List getRole() { return this.role; } - public void setRole(String role) { - this.role = role; - } - public boolean isDefaultPassword() { return this.defaultPassword; } diff --git a/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories index 568a9337de1..ce99bdcbabf 100644 --- a/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories +++ b/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories @@ -14,6 +14,7 @@ org.springframework.boot.autoconfigure.jms.JmsTemplateAutoConfiguration,\ org.springframework.boot.autoconfigure.mobile.DeviceResolverAutoConfiguration,\ org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\ org.springframework.boot.autoconfigure.reactor.ReactorAutoConfiguration,\ +org.springframework.boot.autoconfigure.security.SecurityAutoConfiguration,\ org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\ org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration,\ org.springframework.boot.autoconfigure.web.DispatcherServletAutoConfiguration,\ diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/SecurityAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/SecurityAutoConfigurationTests.java similarity index 77% rename from spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/SecurityAutoConfigurationTests.java rename to spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/SecurityAutoConfigurationTests.java index b43b24d397f..b7c5cd63bc0 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/SecurityAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/SecurityAutoConfigurationTests.java @@ -14,13 +14,16 @@ * limitations under the License. */ -package org.springframework.boot.actuate.autoconfigure; +package org.springframework.boot.autoconfigure.security; import org.junit.Test; import org.springframework.boot.TestUtils; +import org.springframework.boot.autoconfigure.AutoConfigurationReportLoggingInitializer; import org.springframework.boot.autoconfigure.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.context.initializer.LoggingApplicationContextInitializer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.mock.web.MockServletContext; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.TestingAuthenticationToken; @@ -46,13 +49,11 @@ public class SecurityAutoConfigurationTests { this.context = new AnnotationConfigWebApplicationContext(); this.context.setServletContext(new MockServletContext()); this.context.register(SecurityAutoConfiguration.class, - EndpointAutoConfiguration.class, - ManagementServerPropertiesAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class); - this.context.refresh(); + debugRefresh(this.context); assertNotNull(this.context.getBean(AuthenticationManager.class)); - // 4 for static resources, one for management endpoints and one for the rest - assertEquals(6, this.context.getBean(FilterChainProxy.class).getFilterChains() + // 4 for static resources and one for the rest + assertEquals(5, this.context.getBean(FilterChainProxy.class).getFilterChains() .size()); } @@ -61,13 +62,11 @@ public class SecurityAutoConfigurationTests { this.context = new AnnotationConfigWebApplicationContext(); this.context.setServletContext(new MockServletContext()); this.context.register(SecurityAutoConfiguration.class, - EndpointAutoConfiguration.class, - ManagementServerPropertiesAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class); TestUtils.addEnviroment(this.context, "security.ignored:none"); this.context.refresh(); - // Just the application and management endpoints now - assertEquals(2, this.context.getBean(FilterChainProxy.class).getFilterChains() + // Just the application endpoints now + assertEquals(1, this.context.getBean(FilterChainProxy.class).getFilterChains() .size()); } @@ -76,14 +75,11 @@ public class SecurityAutoConfigurationTests { this.context = new AnnotationConfigWebApplicationContext(); this.context.setServletContext(new MockServletContext()); this.context.register(SecurityAutoConfiguration.class, - EndpointAutoConfiguration.class, - ManagementServerPropertiesAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class); TestUtils.addEnviroment(this.context, "security.basic.enabled:false"); this.context.refresh(); - // Just the management endpoints and default ignores now - assertEquals(5, this.context.getBean(FilterChainProxy.class).getFilterChains() - .size()); + // No security at all not even ignores + assertEquals(0, this.context.getBeanNamesForType(FilterChainProxy.class).length); } @Test @@ -91,14 +87,24 @@ public class SecurityAutoConfigurationTests { this.context = new AnnotationConfigWebApplicationContext(); this.context.setServletContext(new MockServletContext()); this.context.register(TestConfiguration.class, SecurityAutoConfiguration.class, - EndpointAutoConfiguration.class, - ManagementServerPropertiesAutoConfiguration.class, PropertyPlaceholderAutoConfiguration.class); this.context.refresh(); assertEquals(this.context.getBean(TestConfiguration.class).authenticationManager, this.context.getBean(AuthenticationManager.class)); } + private static AnnotationConfigWebApplicationContext debugRefresh( + AnnotationConfigWebApplicationContext context) { + TestUtils.addEnviroment(context, "debug:true"); + LoggingApplicationContextInitializer logging = new LoggingApplicationContextInitializer(); + logging.initialize(context); + AutoConfigurationReportLoggingInitializer initializer = new AutoConfigurationReportLoggingInitializer(); + initializer.initialize(context); + context.refresh(); + initializer.onApplicationEvent(new ContextRefreshedEvent(context)); + return context; + } + @Configuration protected static class TestConfiguration { diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/properties/SecurityPropertiesTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/SecurityPropertiesTests.java similarity index 97% rename from spring-boot-actuator/src/test/java/org/springframework/boot/actuate/properties/SecurityPropertiesTests.java rename to spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/SecurityPropertiesTests.java index 7ba417f979d..78b3469b2db 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/properties/SecurityPropertiesTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/SecurityPropertiesTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.boot.actuate.properties; +package org.springframework.boot.autoconfigure.security; import java.util.Collections; import java.util.HashMap; @@ -22,6 +22,7 @@ import java.util.Map; import org.junit.Test; import org.springframework.beans.MutablePropertyValues; +import org.springframework.boot.autoconfigure.security.SecurityProperties; import org.springframework.boot.bind.RelaxedDataBinder; import org.springframework.core.convert.support.DefaultConversionService; diff --git a/spring-boot-cli/src/test/java/org/springframework/boot/cli/ReproIntegrationTests.java b/spring-boot-cli/src/test/java/org/springframework/boot/cli/ReproIntegrationTests.java index 223833d2c79..751fdf88e0a 100644 --- a/spring-boot-cli/src/test/java/org/springframework/boot/cli/ReproIntegrationTests.java +++ b/spring-boot-cli/src/test/java/org/springframework/boot/cli/ReproIntegrationTests.java @@ -44,8 +44,7 @@ public class ReproIntegrationTests { @Test public void securityDependencies() throws Exception { this.cli.run("secure.groovy"); - assertThat(this.cli.getHttpOutput(), - containsString("{\"message\":\"Hello World\"}")); + assertThat(this.cli.getOutput(), containsString("Hello World")); } @Test diff --git a/spring-boot-cli/src/test/resources/repro-samples/secure.groovy b/spring-boot-cli/src/test/resources/repro-samples/secure.groovy index 11be9ed10f7..60a67bfc98f 100644 --- a/spring-boot-cli/src/test/resources/repro-samples/secure.groovy +++ b/spring-boot-cli/src/test/resources/repro-samples/secure.groovy @@ -3,12 +3,12 @@ package org.test // No security features added just a test that the dependencies are resolved @Grab("spring-boot-starter-security") -@RestController -class SampleController { +@Controller +class Sample implements CommandLineRunner { - @RequestMapping("/") - public def hello() { - [message: "Hello World"] + @Override + void run(String... args) { + println "Hello World" } } diff --git a/spring-boot-samples/spring-boot-sample-actuator-ui/src/main/java/org/springframework/boot/sample/ops/ui/SampleActuatorUiApplication.java b/spring-boot-samples/spring-boot-sample-actuator-ui/src/main/java/org/springframework/boot/sample/ops/ui/SampleActuatorUiApplication.java index 48ef553d0db..73d4cfa0946 100644 --- a/spring-boot-samples/spring-boot-sample-actuator-ui/src/main/java/org/springframework/boot/sample/ops/ui/SampleActuatorUiApplication.java +++ b/spring-boot-samples/spring-boot-sample-actuator-ui/src/main/java/org/springframework/boot/sample/ops/ui/SampleActuatorUiApplication.java @@ -20,8 +20,8 @@ import java.util.Date; import java.util.Map; import org.springframework.boot.SpringApplication; -import org.springframework.boot.actuate.properties.SecurityProperties; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.security.SecurityProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.stereotype.Controller; diff --git a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/ManagementAddressSampleActuatorApplicationTests.java b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/ManagementAddressSampleActuatorApplicationTests.java index 335bdc6a1ca..3e75fc36934 100644 --- a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/ManagementAddressSampleActuatorApplicationTests.java +++ b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/ManagementAddressSampleActuatorApplicationTests.java @@ -30,7 +30,7 @@ import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; import org.springframework.boot.SpringApplication; -import org.springframework.boot.actuate.properties.SecurityProperties; +import org.springframework.boot.autoconfigure.security.SecurityProperties; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.http.HttpRequest; import org.springframework.http.HttpStatus; diff --git a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/NoManagementSampleActuatorApplicationTests.java b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/NoManagementSampleActuatorApplicationTests.java index 8faeb7d984c..311b82005c1 100644 --- a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/NoManagementSampleActuatorApplicationTests.java +++ b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/NoManagementSampleActuatorApplicationTests.java @@ -29,7 +29,7 @@ import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.springframework.boot.SpringApplication; -import org.springframework.boot.actuate.properties.SecurityProperties; +import org.springframework.boot.autoconfigure.security.SecurityProperties; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.http.HttpRequest; import org.springframework.http.HttpStatus; diff --git a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/SampleActuatorApplicationTests.java b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/SampleActuatorApplicationTests.java index b06678150ff..b5fffe09a48 100644 --- a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/SampleActuatorApplicationTests.java +++ b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/SampleActuatorApplicationTests.java @@ -35,7 +35,7 @@ import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.springframework.boot.SpringApplication; -import org.springframework.boot.actuate.properties.SecurityProperties; +import org.springframework.boot.autoconfigure.security.SecurityProperties; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; diff --git a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/ShutdownSampleActuatorApplicationTests.java b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/ShutdownSampleActuatorApplicationTests.java index 853d1953231..aa4bc9b0c7d 100644 --- a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/ShutdownSampleActuatorApplicationTests.java +++ b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/ShutdownSampleActuatorApplicationTests.java @@ -29,7 +29,7 @@ import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.springframework.boot.SpringApplication; -import org.springframework.boot.actuate.properties.SecurityProperties; +import org.springframework.boot.autoconfigure.security.SecurityProperties; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.http.HttpRequest; import org.springframework.http.HttpStatus; diff --git a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/UnsecureManagementSampleActuatorApplicationTests.java b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/UnsecureManagementSampleActuatorApplicationTests.java index e26b9742bcd..48a17a4e886 100644 --- a/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/UnsecureManagementSampleActuatorApplicationTests.java +++ b/spring-boot-samples/spring-boot-sample-actuator/src/test/java/org/springframework/boot/sample/ops/UnsecureManagementSampleActuatorApplicationTests.java @@ -57,7 +57,7 @@ public class UnsecureManagementSampleActuatorApplicationTests { public ConfigurableApplicationContext call() throws Exception { return SpringApplication.run( SampleActuatorApplication.class, - "--security.management.enabled=false"); + "--management.security.enabled=false"); } }); context = future.get(60, TimeUnit.SECONDS); diff --git a/spring-boot-samples/spring-boot-sample-secure/pom.xml b/spring-boot-samples/spring-boot-sample-secure/pom.xml index 208697a1177..b77884f5af2 100644 --- a/spring-boot-samples/spring-boot-sample-secure/pom.xml +++ b/spring-boot-samples/spring-boot-sample-secure/pom.xml @@ -16,7 +16,7 @@ ${project.groupId} - spring-boot-starter-actuator + spring-boot-starter-security ${project.version} @@ -24,14 +24,6 @@ spring-boot-starter-web ${project.version} - - org.springframework.security - spring-security-config - - - org.springframework.security - spring-security-web - org.thymeleaf thymeleaf-spring3 diff --git a/spring-boot-samples/spring-boot-sample-secure/src/test/java/org/springframework/boot/sample/ops/ui/SampleSecureApplicationTests.java b/spring-boot-samples/spring-boot-sample-secure/src/test/java/org/springframework/boot/sample/ops/ui/SampleSecureApplicationTests.java index 7c0f2d2e679..fb2e1ca84b1 100644 --- a/spring-boot-samples/spring-boot-sample-secure/src/test/java/org/springframework/boot/sample/ops/ui/SampleSecureApplicationTests.java +++ b/spring-boot-samples/spring-boot-sample-secure/src/test/java/org/springframework/boot/sample/ops/ui/SampleSecureApplicationTests.java @@ -16,9 +16,11 @@ package org.springframework.boot.sample.ops.ui; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + import java.io.IOException; import java.util.Arrays; -import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.Executors; import java.util.concurrent.Future; @@ -39,9 +41,6 @@ import org.springframework.http.client.ClientHttpResponse; import org.springframework.web.client.DefaultResponseErrorHandler; import org.springframework.web.client.RestTemplate; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - /** * Basic integration tests for demo application. * @@ -92,30 +91,6 @@ public class SampleSecureApplicationTests { assertTrue("Wrong body:\n" + entity.getBody(), entity.getBody().contains("body")); } - @Test - public void testMetrics() throws Exception { - @SuppressWarnings("rawtypes") - ResponseEntity entity = getRestTemplate().getForEntity( - "http://localhost:8080/metrics", Map.class); - assertEquals(HttpStatus.UNAUTHORIZED, entity.getStatusCode()); - } - - @Test - public void testError() throws Exception { - HttpHeaders headers = new HttpHeaders(); - headers.setAccept(Arrays.asList(MediaType.TEXT_HTML)); - ResponseEntity entity = getRestTemplate().exchange( - "http://localhost:8080/error", HttpMethod.GET, - new HttpEntity(headers), String.class); - assertEquals(HttpStatus.OK, entity.getStatusCode()); - assertTrue("Wrong body:\n" + entity.getBody(), entity.getBody() - .contains("")); - assertTrue("Wrong body:\n" + entity.getBody(), entity.getBody() - .contains("")); - assertTrue("Wrong body:\n" + entity.getBody(), entity.getBody() - .contains("Please contact the operator with the above information")); - } - private RestTemplate getRestTemplate() { RestTemplate restTemplate = new RestTemplate(); restTemplate.setErrorHandler(new DefaultResponseErrorHandler() { diff --git a/spring-boot/src/main/java/org/springframework/boot/context/initializer/LoggingApplicationContextInitializer.java b/spring-boot/src/main/java/org/springframework/boot/context/initializer/LoggingApplicationContextInitializer.java index 478fa462459..add97cb856b 100644 --- a/spring-boot/src/main/java/org/springframework/boot/context/initializer/LoggingApplicationContextInitializer.java +++ b/spring-boot/src/main/java/org/springframework/boot/context/initializer/LoggingApplicationContextInitializer.java @@ -32,10 +32,8 @@ import org.springframework.context.ConfigurableApplicationContext; import org.springframework.core.Ordered; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.Environment; -import org.springframework.core.env.SimpleCommandLinePropertySource; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; -import org.springframework.util.ObjectUtils; import org.springframework.util.ResourceUtils; /** @@ -108,17 +106,6 @@ public class LoggingApplicationContextInitializer implements LoggingSystem loggingSystem = LoggingSystem.get(springApplication.getClass() .getClassLoader()); loggingSystem.beforeInitialize(); - if (this.parseArgs && this.springBootLogging == null - && !ObjectUtils.isEmpty(args)) { - SimpleCommandLinePropertySource parsedArgs = new SimpleCommandLinePropertySource( - args); - if (parsedArgs.containsProperty("debug")) { - this.springBootLogging = LogLevel.DEBUG; - } - if (parsedArgs.containsProperty("trace")) { - this.springBootLogging = LogLevel.TRACE; - } - } } /** @@ -130,6 +117,15 @@ public class LoggingApplicationContextInitializer implements ConfigurableEnvironment environment = applicationContext.getEnvironment(); + if (this.parseArgs && this.springBootLogging == null) { + if (environment.containsProperty("debug")) { + this.springBootLogging = LogLevel.DEBUG; + } + if (environment.containsProperty("trace")) { + this.springBootLogging = LogLevel.TRACE; + } + } + for (Map.Entry mapping : ENVIRONMENT_SYSTEM_PROPERTY_MAPPING .entrySet()) { if (environment.containsProperty(mapping.getKey())) { diff --git a/spring-boot/src/test/java/org/springframework/boot/context/initializer/LoggingApplicationContextInitializerTests.java b/spring-boot/src/test/java/org/springframework/boot/context/initializer/LoggingApplicationContextInitializerTests.java index 9229926bf3b..9b0c0495676 100644 --- a/spring-boot/src/test/java/org/springframework/boot/context/initializer/LoggingApplicationContextInitializerTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/context/initializer/LoggingApplicationContextInitializerTests.java @@ -132,7 +132,7 @@ public class LoggingApplicationContextInitializerTests { @Test public void parseDebugArg() throws Exception { - this.initializer.initialize(this.springApplication, new String[] { "--debug" }); + TestUtils.addEnviroment(this.context, "debug"); this.initializer.initialize(this.context); this.logger.debug("testatdebug"); this.logger.trace("testattrace"); @@ -142,8 +142,7 @@ public class LoggingApplicationContextInitializerTests { @Test public void parseTraceArg() throws Exception { - this.context = new GenericApplicationContext(); - this.initializer.initialize(this.springApplication, new String[] { "--trace" }); + TestUtils.addEnviroment(this.context, "trace"); this.initializer.initialize(this.context); this.logger.debug("testatdebug"); this.logger.trace("testattrace");