Merge branch '1.5.x'
This commit is contained in:
@@ -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()) + "]";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user