Commit cb7c0b50 authored by Phillip Webb's avatar Phillip Webb

Merge branch '1.5.x'

parents 4edab86b dcbfb51a
......@@ -17,7 +17,7 @@
package org.springframework.boot.actuate.endpoint.mvc;
/**
* A {@link MvcEndpoint} that also includes a logical name. Unlike {@link #getPath()
* An {@link MvcEndpoint} that also includes a logical name. Unlike {@link #getPath()
* endpoints paths}, it should not be possible for a user to change the endpoint name.
* Names provide a consistent way to reference an endpoint, for example they may be used
* as the {@literal 'rel'} attribute in a HAL response.
......
......@@ -30,7 +30,7 @@ import org.springframework.jdbc.support.SQLExceptionTranslator;
import org.springframework.jdbc.support.SQLStateSQLExceptionTranslator;
/**
* Transforms {@link java.sql.SQLException} into a Spring-specific @{link
* Transforms {@link java.sql.SQLException} into a Spring-specific {@link
* DataAccessException}.
*
* @author Lukas Eder
......
......@@ -174,6 +174,20 @@ public class SecurityProperties implements SecurityPrerequisite {
NONE, DOMAIN, ALL
}
public enum ContentSecurityPolicyMode {
/**
* Use the 'Content-Security-Policy' header.
*/
DEFAULT,
/**
* Use the 'Content-Security-Policy-Report-Only' header.
*/
REPORT_ONLY
}
/**
* Enable cross site scripting (XSS) protection.
*/
......@@ -194,6 +208,16 @@ public class SecurityProperties implements SecurityPrerequisite {
*/
private boolean contentType = true;
/**
* Value for content security policy header.
*/
private String contentSecurityPolicy;
/**
* Security policy mode.
*/
private ContentSecurityPolicyMode contentSecurityPolicyMode = ContentSecurityPolicyMode.DEFAULT;
/**
* HTTP Strict Transport Security (HSTS) mode (none, domain, all).
*/
......@@ -231,6 +255,23 @@ public class SecurityProperties implements SecurityPrerequisite {
this.contentType = contentType;
}
public String getContentSecurityPolicy() {
return this.contentSecurityPolicy;
}
public void setContentSecurityPolicy(String contentSecurityPolicy) {
this.contentSecurityPolicy = contentSecurityPolicy;
}
public ContentSecurityPolicyMode getContentSecurityPolicyMode() {
return this.contentSecurityPolicyMode;
}
public void setContentSecurityPolicyMode(
ContentSecurityPolicyMode contentSecurityPolicyMode) {
this.contentSecurityPolicyMode = contentSecurityPolicyMode;
}
public HSTS getHsts() {
return this.hsts;
}
......
......@@ -29,6 +29,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.security.SecurityProperties.Headers;
import org.springframework.boot.autoconfigure.security.SecurityProperties.Headers.ContentSecurityPolicyMode;
import org.springframework.boot.autoconfigure.web.ErrorController;
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
......@@ -108,6 +109,16 @@ public class SpringBootWebSecurityConfiguration {
if (!headers.isContentType()) {
configurer.contentTypeOptions().disable();
}
if (StringUtils.hasText(headers.getContentSecurityPolicy())) {
String policyDirectives = headers.getContentSecurityPolicy();
ContentSecurityPolicyMode mode = headers.getContentSecurityPolicyMode();
if (mode == ContentSecurityPolicyMode.DEFAULT) {
configurer.contentSecurityPolicy(policyDirectives);
}
else {
configurer.contentSecurityPolicy(policyDirectives).reportOnly();
}
}
if (!headers.isXss()) {
configurer.xssProtection().disable();
}
......
......@@ -16,6 +16,7 @@
package org.springframework.boot.autoconfigure.condition;
import org.junit.After;
import org.junit.Test;
import org.springframework.boot.cloud.CloudPlatform;
......@@ -33,6 +34,13 @@ public class ConditionalOnCloudPlatformTests {
private final AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
@After
public void cleanUp() {
if (this.context != null) {
this.context.close();
}
}
@Test
public void outcomeWhenCloudfoundryPlatformNotPresentShouldNotMatch()
throws Exception {
......
......@@ -213,7 +213,9 @@ public class SpringBootWebSecurityConfigurationTests {
.andExpect(MockMvcResultMatchers.header().string("Cache-Control",
is(notNullValue())))
.andExpect(MockMvcResultMatchers.header().string("X-Frame-Options",
is(notNullValue())));
is(notNullValue())))
.andExpect(MockMvcResultMatchers.header()
.doesNotExist("Content-Security-Policy"));
}
@Test
......@@ -239,6 +241,39 @@ public class SpringBootWebSecurityConfigurationTests {
MockMvcResultMatchers.header().doesNotExist("X-Frame-Options"));
}
@Test
public void contentSecurityPolicyConfiguration() throws Exception {
this.context = SpringApplication.run(VanillaWebConfiguration.class,
"--security.headers.content-security-policy=default-src 'self';");
MockMvc mockMvc = MockMvcBuilders
.webAppContextSetup((WebApplicationContext) this.context)
.addFilters((FilterChainProxy) this.context
.getBean("springSecurityFilterChain", Filter.class))
.build();
mockMvc.perform(MockMvcRequestBuilders.get("/"))
.andExpect(MockMvcResultMatchers.header()
.string("Content-Security-Policy", is("default-src 'self';")))
.andExpect(MockMvcResultMatchers.header()
.doesNotExist("Content-Security-Policy-Report-Only"));
}
@Test
public void contentSecurityPolicyReportOnlyConfiguration() throws Exception {
this.context = SpringApplication.run(VanillaWebConfiguration.class,
"--security.headers.content-security-policy=default-src 'self';",
"--security.headers.content-security-policy-mode=report-only");
MockMvc mockMvc = MockMvcBuilders
.webAppContextSetup((WebApplicationContext) this.context)
.addFilters((FilterChainProxy) this.context
.getBean("springSecurityFilterChain", Filter.class))
.build();
mockMvc.perform(MockMvcRequestBuilders.get("/"))
.andExpect(MockMvcResultMatchers.header().string(
"Content-Security-Policy-Report-Only", is("default-src 'self';")))
.andExpect(MockMvcResultMatchers.header()
.doesNotExist("Content-Security-Policy"));
}
@Configuration
@Import(TestWebConfiguration.class)
@Order(Ordered.LOWEST_PRECEDENCE)
......
......@@ -577,7 +577,7 @@ public class WebMvcAutoConfigurationTests {
}
@Test
public void welcomePageMappingDoesNotHandleRequestThatDoNotAcceptTextHtml()
public void welcomePageMappingDoesNotHandleRequestsThatDoNotAcceptTextHtml()
throws Exception {
load("spring.resources.static-locations:classpath:/welcome-page/");
assertThat(this.context.getBeansOfType(WelcomePageHandlerMapping.class))
......
......@@ -87,7 +87,7 @@
<hikaricp.version>2.4.7</hikaricp.version>
<hikaricp-java6.version>2.3.13</hikaricp-java6.version>
<hsqldb.version>2.3.3</hsqldb.version>
<htmlunit.version>2.21</htmlunit.version>
<htmlunit.version>2.23</htmlunit.version>
<httpasyncclient.version>4.1.2</httpasyncclient.version>
<httpclient.version>4.5.2</httpclient.version>
<httpcore.version>4.4.5</httpcore.version>
......@@ -136,6 +136,7 @@
<querydsl.version>4.1.4</querydsl.version>
<reactor.version>3.0.3.RELEASE</reactor.version>
<selenium.version>2.53.1</selenium.version>
<selenium-htmlunit.version>2.23.2</selenium-htmlunit.version>
<sendgrid.version>2.2.2</sendgrid.version>
<servlet-api.version>3.1.0</servlet-api.version>
<simple-json.version>1.1.1</simple-json.version>
......@@ -1818,7 +1819,7 @@
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>htmlunit-driver</artifactId>
<version>${htmlunit.version}</version>
<version>${selenium-htmlunit.version}</version>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
......
......@@ -45,15 +45,14 @@ public class RestartApplicationListener
onApplicationStartingEvent((ApplicationStartingEvent) event);
}
if (event instanceof ApplicationPreparedEvent) {
Restarter.getInstance()
.prepare(((ApplicationPreparedEvent) event).getApplicationContext());
onApplicationPreparedEvent((ApplicationPreparedEvent) event);
}
if (event instanceof ApplicationReadyEvent
|| event instanceof ApplicationFailedEvent) {
Restarter.getInstance().finish();
if (event instanceof ApplicationFailedEvent) {
Restarter.getInstance().prepare(null);
}
}
if (event instanceof ApplicationFailedEvent) {
onApplicationFailedEvent((ApplicationFailedEvent) event);
}
}
......@@ -72,6 +71,14 @@ public class RestartApplicationListener
}
}
private void onApplicationPreparedEvent(ApplicationPreparedEvent event) {
Restarter.getInstance().prepare(event.getApplicationContext());
}
private void onApplicationFailedEvent(ApplicationFailedEvent event) {
Restarter.getInstance().remove(event.getApplicationContext());
}
@Override
public int getOrder() {
return this.order;
......
......@@ -31,6 +31,7 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.Callable;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.locks.Lock;
......@@ -116,7 +117,7 @@ public class Restarter {
private boolean finished = false;
private volatile ConfigurableApplicationContext rootContext;
private final List<ConfigurableApplicationContext> rootContexts = new CopyOnWriteArrayList<ConfigurableApplicationContext>();
/**
* Internal constructor to create a new {@link Restarter} instance.
......@@ -314,9 +315,9 @@ public class Restarter {
this.logger.debug("Stopping application");
this.stopLock.lock();
try {
if (this.rootContext != null) {
this.rootContext.close();
this.rootContext = null;
for (ConfigurableApplicationContext context : this.rootContexts) {
context.close();
this.rootContexts.remove(context);
}
cleanupCaches();
if (this.forceReferenceCleanup) {
......@@ -418,7 +419,13 @@ public class Restarter {
if (applicationContext != null && applicationContext.getParent() != null) {
return;
}
this.rootContext = applicationContext;
this.rootContexts.add(applicationContext);
}
void remove(ConfigurableApplicationContext applicationContext) {
if (applicationContext != null) {
this.rootContexts.remove(applicationContext);
}
}
private LeakSafeThread getLeakSafeThread() {
......
......@@ -16,6 +16,8 @@
package org.springframework.boot.devtools.restart;
import java.util.List;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
......@@ -64,8 +66,8 @@ public class RestartApplicationListenerTests {
assertThat(ReflectionTestUtils.getField(Restarter.getInstance(), "args"))
.isEqualTo(ARGS);
assertThat(Restarter.getInstance().isFinished()).isTrue();
assertThat(ReflectionTestUtils.getField(Restarter.getInstance(), "rootContext"))
.isNotNull();
assertThat((List<?>) ReflectionTestUtils.getField(Restarter.getInstance(),
"rootContexts")).isNotEmpty();
}
@Test
......@@ -74,8 +76,8 @@ public class RestartApplicationListenerTests {
assertThat(ReflectionTestUtils.getField(Restarter.getInstance(), "args"))
.isEqualTo(ARGS);
assertThat(Restarter.getInstance().isFinished()).isTrue();
assertThat(ReflectionTestUtils.getField(Restarter.getInstance(), "rootContext"))
.isNull();
assertThat((List<?>) ReflectionTestUtils.getField(Restarter.getInstance(),
"rootContexts")).isEmpty();
}
@Test
......
......@@ -430,6 +430,8 @@ content into your application; rather pick only the properties that you need.
security.filter-order=0 # Security filter chain order.
security.filter-dispatcher-types=ASYNC, FORWARD, INCLUDE, REQUEST # Security filter chain dispatcher types.
security.headers.cache=true # Enable cache control HTTP headers.
security.headers.content-security-policy= # Value for content security policy header.
security.headers.content-security-policy-mode=default # Content security policy mode (default, report-only).
security.headers.content-type=true # Enable "X-Content-Type-Options" header.
security.headers.frame=true # Enable "X-Frame-Options" header.
security.headers.hsts= # HTTP Strict Transport Security (HSTS) mode (none, domain, all).
......
......@@ -811,7 +811,7 @@ If you are using Jolokia but you don't want Spring Boot to configure it, simply
== Loggers
Spring Boot Actuator includes the ability to view and configure the log levels of your
application at runtime. You can view either the entire list or an individual logger's
configuration which is made up of both the explictily configured logging level as well as
configuration which is made up of both the explicitly configured logging level as well as
the effective logging level given to it by the logging framework. These levels can be:
* `TRACE`
......@@ -823,7 +823,7 @@ the effective logging level given to it by the logging framework. These levels
* `OFF`
* `null`
with `null` indicating that there is no explict configuration.
with `null` indicating that there is no explicit configuration.
......
......@@ -3883,7 +3883,7 @@ abstraction expects. No further customization is applied on it.
EhCache 2.x is used if a file named `ehcache.xml` can be found at the root of the
classpath. If EhCache 2.x, the `EhCacheCacheManager` provided by the
`spring-boot-starter-cache` '`Starter`' and such file is present it is used to bootstrap
the cache manager. An alternate configuration file can be provide a well using:
the cache manager. An alternate configuration file can be provided as well using:
[source,properties,indent=0]
----
......
......@@ -769,7 +769,7 @@ dependencies out-of-the-box so you may want to have a look to the
{propdeps-plugin}[`propdeps-plugin`] in the meantime.
TIP: repackaged archives do not contain devtools by default. If you want to use
<<using-boot-devtools-remote,certain remote devtools feature>>, you'll need to enable the
<<using-boot-devtools-remote,certain remote devtools feature>>, you'll need to disable the
`excludeDevtools` build property to include it. The property is supported with both the
Maven and Gradle plugins.
......
......@@ -42,6 +42,7 @@
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
......
......@@ -10,7 +10,7 @@ import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests that {code ResetMocksTestExecutionListener} and
* Tests that {@code ResetMocksTestExecutionListener} and
* {@code MockitoTestExecutionListener} gracefully degrade when Mockito is not on the
* classpath.
*
......
......@@ -81,7 +81,9 @@ public class LocalHostWebConnectionHtmlUnitDriverTests {
throws Exception {
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("Environment must not be null");
new LocalHostWebConnectionHtmlUnitDriver(null, mock(Capabilities.class));
Capabilities capabilities = mock(Capabilities.class);
given(capabilities.getBrowserName()).willReturn("chrome");
new LocalHostWebConnectionHtmlUnitDriver(null, capabilities);
}
@Test
......
......@@ -52,7 +52,7 @@ public class RunProcess {
/**
* Creates new {@link RunProcess} instance for the specified command.
* @param command the program to execute and it's arguments
* @param command the program to execute and its arguments
*/
public RunProcess(String... command) {
this(null, command);
......@@ -63,7 +63,7 @@ public class RunProcess {
* command.
* @param workingDirectory the working directory of the child process or {@code null}
* to run in the working directory of the current Java process
* @param command the program to execute and it's arguments
* @param command the program to execute and its arguments
*/
public RunProcess(File workingDirectory, String... command) {
this.workingDirectory = workingDirectory;
......
......@@ -65,7 +65,7 @@ public class ExplodedArchive implements Archive {
* Create a new {@link ExplodedArchive} instance.
* @param root the root folder
* @param recursive if recursive searching should be used to locate the manifest.
* Defaults to {@code true}, folders with a large tree might want to set this to {code
* Defaults to {@code true}, folders with a large tree might want to set this to {@code
* false}.
*/
public ExplodedArchive(File root, boolean recursive) {
......
......@@ -89,7 +89,7 @@ public abstract class AbstractRunMojo extends AbstractDependencyFilterMojo {
/**
* Current working directory to use for the application. If not specified, basedir
* will be used NOTE: the use of working directory means that processes will be
* will be used. NOTE: the use of working directory means that processes will be
* started by forking a new JVM.
* @since 1.5
*/
......
......@@ -185,7 +185,14 @@ public class ErrorPageFilter implements Filter, ErrorPageRegistry {
request.getRequestDispatcher(path).forward(request, response);
}
private String getDescription(HttpServletRequest request) {
/**
* Return the description for the given request. By default this method will return a
* description based on the request {@code servletPath} and {@code pathInfo}.
* @param request the source request
* @return the description
* @since 1.5.0
*/
protected String getDescription(HttpServletRequest request) {
return "[" + request.getServletPath()
+ (request.getPathInfo() == null ? "" : request.getPathInfo()) + "]";
}
......
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