Merge branch '1.5.x'

pull/8257/merge
Phillip Webb 8 years ago
commit 58ac67eb27

@ -159,9 +159,11 @@ public class EndpointWebMvcManagementContextConfiguration {
@Bean @Bean
@ConditionalOnBean(HealthEndpoint.class) @ConditionalOnBean(HealthEndpoint.class)
@ConditionalOnEnabledEndpoint("health") @ConditionalOnEnabledEndpoint("health")
public HealthMvcEndpoint healthMvcEndpoint(HealthEndpoint delegate) { public HealthMvcEndpoint healthMvcEndpoint(HealthEndpoint delegate,
ManagementServerProperties managementServerProperties) {
HealthMvcEndpoint healthMvcEndpoint = new HealthMvcEndpoint(delegate, HealthMvcEndpoint healthMvcEndpoint = new HealthMvcEndpoint(delegate,
this.managementServerProperties.getSecurity().isEnabled()); this.managementServerProperties.getSecurity().isEnabled(),
managementServerProperties.getSecurity().getRoles());
if (this.healthMvcEndpointProperties.getMapping() != null) { if (this.healthMvcEndpointProperties.getMapping() != null) {
healthMvcEndpoint healthMvcEndpoint
.addStatusMapping(this.healthMvcEndpointProperties.getMapping()); .addStatusMapping(this.healthMvcEndpointProperties.getMapping());

@ -16,7 +16,9 @@
package org.springframework.boot.actuate.endpoint.mvc; package org.springframework.boot.actuate.endpoint.mvc;
import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -52,6 +54,8 @@ public class HealthMvcEndpoint extends AbstractEndpointMvcAdapter<HealthEndpoint
private final boolean secure; private final boolean secure;
private final List<String> roles;
private Map<String, HttpStatus> statusMapping = new HashMap<String, HttpStatus>(); private Map<String, HttpStatus> statusMapping = new HashMap<String, HttpStatus>();
private RelaxedPropertyResolver securityPropertyResolver; private RelaxedPropertyResolver securityPropertyResolver;
@ -65,9 +69,15 @@ public class HealthMvcEndpoint extends AbstractEndpointMvcAdapter<HealthEndpoint
} }
public HealthMvcEndpoint(HealthEndpoint delegate, boolean secure) { public HealthMvcEndpoint(HealthEndpoint delegate, boolean secure) {
this(delegate, secure, null);
}
public HealthMvcEndpoint(HealthEndpoint delegate, boolean secure,
List<String> roles) {
super(delegate); super(delegate);
this.secure = secure; this.secure = secure;
setupDefaultStatusMapping(); setupDefaultStatusMapping();
this.roles = roles;
} }
private void setupDefaultStatusMapping() { private void setupDefaultStatusMapping() {
@ -173,10 +183,7 @@ public class HealthMvcEndpoint extends AbstractEndpointMvcAdapter<HealthEndpoint
if (!this.secure) { if (!this.secure) {
return true; return true;
} }
String[] roles = StringUtils.commaDelimitedListToStringArray( for (String role : getRoles()) {
this.securityPropertyResolver.getProperty("roles", "ROLE_ACTUATOR"));
roles = StringUtils.trimArrayElements(roles);
for (String role : roles) {
if (request.isUserInRole(role) || request.isUserInRole("ROLE_" + role)) { if (request.isUserInRole(role) || request.isUserInRole("ROLE_" + role)) {
return true; return true;
} }
@ -184,4 +191,14 @@ public class HealthMvcEndpoint extends AbstractEndpointMvcAdapter<HealthEndpoint
return false; return false;
} }
private List<String> getRoles() {
if (this.roles != null) {
return this.roles;
}
String[] roles = StringUtils.commaDelimitedListToStringArray(
this.securityPropertyResolver.getProperty("roles", "ROLE_ACTUATOR"));
roles = StringUtils.trimArrayElements(roles);
return Arrays.asList(roles);
}
} }

@ -125,10 +125,14 @@ public class MvcEndpointSecurityInterceptor extends HandlerInterceptorAdapter {
} }
} }
private class AuthoritiesValidator { /**
* Inner class to check authorities using Spring Security (when available).
*/
private static class AuthoritiesValidator {
private boolean hasAuthority(String role) { private boolean hasAuthority(String role) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); Authentication authentication = SecurityContextHolder.getContext()
.getAuthentication();
if (authentication != null) { if (authentication != null) {
for (GrantedAuthority authority : authentication.getAuthorities()) { for (GrantedAuthority authority : authentication.getAuthorities()) {
if (authority.getAuthority().equals(role)) { if (authority.getAuthority().equals(role)) {

@ -16,6 +16,8 @@
package org.springframework.boot.actuate.autoconfigure; package org.springframework.boot.actuate.autoconfigure;
import java.util.Arrays;
import org.junit.After; import org.junit.After;
import org.junit.Test; import org.junit.Test;
@ -34,6 +36,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockServletContext; import org.springframework.mock.web.MockServletContext;
import org.springframework.test.util.ReflectionTestUtils;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -83,6 +86,20 @@ public class HealthMvcEndpointAutoConfigurationTests {
assertThat(map.getDetails().get("foo")).isEqualTo("bar"); assertThat(map.getDetails().get("foo")).isEqualTo("bar");
} }
@Test
public void testSetRoles() throws Exception {
// gh-8314
this.context = new AnnotationConfigWebApplicationContext();
this.context.setServletContext(new MockServletContext());
this.context.register(TestConfiguration.class);
EnvironmentTestUtils.addEnvironment(this.context,
"management.security.roles[0]=super");
this.context.refresh();
HealthMvcEndpoint health = this.context.getBean(HealthMvcEndpoint.class);
assertThat(ReflectionTestUtils.getField(health, "roles"))
.isEqualTo(Arrays.asList("super"));
}
@Configuration @Configuration
@ImportAutoConfiguration({ SecurityAutoConfiguration.class, @ImportAutoConfiguration({ SecurityAutoConfiguration.class,
JacksonAutoConfiguration.class, WebMvcAutoConfiguration.class, JacksonAutoConfiguration.class, WebMvcAutoConfiguration.class,

@ -16,6 +16,7 @@
package org.springframework.boot.actuate.endpoint.mvc; package org.springframework.boot.actuate.endpoint.mvc;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -182,6 +183,19 @@ public class HealthMvcEndpointTests {
assertThat(((Health) result).getDetails().get("foo")).isNull(); assertThat(((Health) result).getDetails().get("foo")).isNull();
} }
@Test
public void customRoleFromListShouldNotExposeDetailsForDefaultRole() {
// gh-8314
this.mvc = new HealthMvcEndpoint(this.endpoint, true,
Arrays.asList("HERO", "USER"));
given(this.endpoint.invoke())
.willReturn(new Health.Builder().up().withDetail("foo", "bar").build());
Object result = this.mvc.invoke(this.hero);
assertThat(result instanceof Health).isTrue();
assertThat(((Health) result).getStatus() == Status.UP).isTrue();
assertThat(((Health) result).getDetails().get("foo")).isEqualTo("bar");
}
@Test @Test
public void healthIsCached() { public void healthIsCached() {
given(this.endpoint.getTimeToLive()).willReturn(10000L); given(this.endpoint.getTimeToLive()).willReturn(10000L);

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2016 the original author or authors. * Copyright 2012-2017 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -130,11 +130,13 @@ public class MvcEndpointSecurityInterceptorTests {
} }
@Test @Test
public void sensitiveEndpointIfRoleNotCorrectShouldCheckAuthorities() throws Exception { public void sensitiveEndpointIfRoleNotCorrectShouldCheckAuthorities()
throws Exception {
Principal principal = mock(Principal.class); Principal principal = mock(Principal.class);
this.request.setUserPrincipal(principal); this.request.setUserPrincipal(principal);
Authentication authentication = mock(Authentication.class); Authentication authentication = mock(Authentication.class);
Set<SimpleGrantedAuthority> authorities = Collections.singleton(new SimpleGrantedAuthority("SUPER_HERO")); Set<SimpleGrantedAuthority> authorities = Collections
.singleton(new SimpleGrantedAuthority("SUPER_HERO"));
doReturn(authorities).when(authentication).getAuthorities(); doReturn(authorities).when(authentication).getAuthorities();
SecurityContextHolder.getContext().setAuthentication(authentication); SecurityContextHolder.getContext().setAuthentication(authentication);
assertThat(this.securityInterceptor.preHandle(this.request, this.response, assertThat(this.securityInterceptor.preHandle(this.request, this.response,
@ -142,11 +144,13 @@ public class MvcEndpointSecurityInterceptorTests {
} }
@Test @Test
public void sensitiveEndpointIfRoleAndAuthoritiesNotCorrectShouldNotAllowAccess() throws Exception { public void sensitiveEndpointIfRoleAndAuthoritiesNotCorrectShouldNotAllowAccess()
throws Exception {
Principal principal = mock(Principal.class); Principal principal = mock(Principal.class);
this.request.setUserPrincipal(principal); this.request.setUserPrincipal(principal);
Authentication authentication = mock(Authentication.class); Authentication authentication = mock(Authentication.class);
Set<SimpleGrantedAuthority> authorities = Collections.singleton(new SimpleGrantedAuthority("HERO")); Set<SimpleGrantedAuthority> authorities = Collections
.singleton(new SimpleGrantedAuthority("HERO"));
doReturn(authorities).when(authentication).getAuthorities(); doReturn(authorities).when(authentication).getAuthorities();
SecurityContextHolder.getContext().setAuthentication(authentication); SecurityContextHolder.getContext().setAuthentication(authentication);
assertThat(this.securityInterceptor.preHandle(this.request, this.response, assertThat(this.securityInterceptor.preHandle(this.request, this.response,

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2016 the original author or authors. * Copyright 2012-2017 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -39,6 +39,8 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
/** /**
* Tests for {@link MvcEndpointSecurityInterceptor} when Spring Security is not available.
*
* @author Madhura Bhave * @author Madhura Bhave
*/ */
@RunWith(ModifiedClassPathRunner.class) @RunWith(ModifiedClassPathRunner.class)
@ -77,7 +79,8 @@ public class NoSpringSecurityMvcEndpointSecurityInterceptorTests {
} }
@Test @Test
public void sensitiveEndpointIfRoleNotPresentShouldNotValidateAuthorities() throws Exception { public void sensitiveEndpointIfRoleNotPresentShouldNotValidateAuthorities()
throws Exception {
Principal principal = mock(Principal.class); Principal principal = mock(Principal.class);
this.request.setUserPrincipal(principal); this.request.setUserPrincipal(principal);
this.servletContext.declareRoles("HERO"); this.servletContext.declareRoles("HERO");
@ -105,5 +108,5 @@ public class NoSpringSecurityMvcEndpointSecurityInterceptorTests {
} }
} }
}
}

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2016 the original author or authors. * Copyright 2012-2017 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -64,20 +64,33 @@ class DataSourceInitializedPublisher implements BeanPostProcessor {
if (bean instanceof JpaProperties) { if (bean instanceof JpaProperties) {
this.properties = (JpaProperties) bean; this.properties = (JpaProperties) bean;
} }
if (bean instanceof EntityManagerFactory && this.dataSource != null if (bean instanceof EntityManagerFactory) {
&& isInitializingDatabase()) { publishEventIfRequired((EntityManagerFactory) bean);
this.applicationContext
.publishEvent(new DataSourceInitializedEvent(this.dataSource));
} }
return bean; return bean;
} }
private boolean isInitializingDatabase() { private void publishEventIfRequired(EntityManagerFactory entityManagerFactory) {
DataSource dataSource = findDataSource(entityManagerFactory);
if (dataSource != null && isInitializingDatabase(dataSource)) {
this.applicationContext
.publishEvent(new DataSourceInitializedEvent(dataSource));
}
}
private DataSource findDataSource(EntityManagerFactory entityManagerFactory) {
Object dataSource = entityManagerFactory.getProperties()
.get("javax.persistence.nonJtaDataSource");
return (dataSource != null && dataSource instanceof DataSource
? (DataSource) dataSource : this.dataSource);
}
private boolean isInitializingDatabase(DataSource dataSource) {
if (this.properties == null) { if (this.properties == null) {
return true; // better safe than sorry return true; // better safe than sorry
} }
Map<String, String> hibernate = this.properties Map<String, String> hibernate = this.properties
.getHibernateProperties(this.dataSource); .getHibernateProperties(dataSource);
if (hibernate.containsKey("hibernate.hbm2ddl.auto")) { if (hibernate.containsKey("hibernate.hbm2ddl.auto")) {
return true; return true;
} }

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2016 the original author or authors. * Copyright 2012-2017 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2016 the original author or authors. * Copyright 2012-2017 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.

@ -16,7 +16,7 @@
package org.springframework.boot.maven; package org.springframework.boot.maven;
import java.util.Iterator; import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.StringTokenizer; import java.util.StringTokenizer;
@ -87,29 +87,15 @@ public abstract class AbstractDependencyFilterMojo extends AbstractMojo {
this.excludeArtifactIds = excludeArtifactIds; this.excludeArtifactIds = excludeArtifactIds;
} }
@SuppressWarnings("unchecked")
protected Set<Artifact> filterDependencies(Set<Artifact> dependencies, protected Set<Artifact> filterDependencies(Set<Artifact> dependencies,
FilterArtifacts filters) throws MojoExecutionException { FilterArtifacts filters) throws MojoExecutionException {
List<ArtifactsFilter> artifactsFilters = filters.getFilters();
try { try {
for (ArtifactsFilter filter : artifactsFilters) { Set<Artifact> filtered = new LinkedHashSet<Artifact>(dependencies);
Set<Artifact> result = filter.filter(dependencies); filtered.retainAll(filters.filter(dependencies));
applyFiltering(dependencies, result); return filtered;
}
return dependencies;
}
catch (ArtifactFilterException e) {
throw new MojoExecutionException(e.getMessage(), e);
} }
} catch (ArtifactFilterException ex) {
throw new MojoExecutionException(ex.getMessage(), ex);
private void applyFiltering(Set<Artifact> original, Set<Artifact> filtered) {
Iterator<Artifact> iterator = original.iterator();
while (iterator.hasNext()) {
Artifact element = iterator.next();
if (!filtered.contains(element)) {
iterator.remove();
}
} }
} }

@ -103,7 +103,7 @@ public class DependencyFilterMojoTests {
} }
@Test @Test
public void filterExcludeKeepOrder() throws MojoExecutionException { public void filterExcludeKeepOrder() throws MojoExecutionException {
Exclude exclude = new Exclude(); Exclude exclude = new Exclude();
exclude.setGroupId("com.bar"); exclude.setGroupId("com.bar");
exclude.setArtifactId("two"); exclude.setArtifactId("two");
@ -121,7 +121,8 @@ public class DependencyFilterMojoTests {
return createArtifact(groupId, artifactId, null); return createArtifact(groupId, artifactId, null);
} }
private static Artifact createArtifact(String groupId, String artifactId, String scope) { private static Artifact createArtifact(String groupId, String artifactId,
String scope) {
Artifact a = mock(Artifact.class); Artifact a = mock(Artifact.class);
given(a.getGroupId()).willReturn(groupId); given(a.getGroupId()).willReturn(groupId);
given(a.getArtifactId()).willReturn(artifactId); given(a.getArtifactId()).willReturn(artifactId);

@ -205,9 +205,6 @@ public class JettyEmbeddedServletContainer implements EmbeddedWebServer {
@Override @Override
public void stop() { public void stop() {
synchronized (this.monitor) { synchronized (this.monitor) {
if (!this.started) {
return;
}
this.started = false; this.started = false;
try { try {
this.server.stop(); this.server.stop();

@ -90,32 +90,38 @@ public class TomcatEmbeddedServletContainer implements EmbeddedWebServer {
synchronized (this.monitor) { synchronized (this.monitor) {
try { try {
addInstanceIdToEngineName(); addInstanceIdToEngineName();
try {
// Remove service connectors to that protocol binding doesn't happen
// yet
removeServiceConnectors();
// Remove service connectors to that protocol binding doesn't happen yet // Start the server to trigger initialization listeners
removeServiceConnectors(); this.tomcat.start();
// Start the server to trigger initialization listeners // We can re-throw failure exception directly in the main thread
this.tomcat.start(); rethrowDeferredStartupExceptions();
// We can re-throw failure exception directly in the main thread Context context = findContext();
rethrowDeferredStartupExceptions(); try {
ContextBindings.bindClassLoader(context, context.getNamingToken(),
getClass().getClassLoader());
}
catch (NamingException ex) {
// Naming is not enabled. Continue
}
Context context = findContext(); // Unlike Jetty, all Tomcat threads are daemon threads. We create a
try { // blocking non-daemon to stop immediate shutdown
ContextBindings.bindClassLoader(context, context.getNamingToken(), startDaemonAwaitThread();
getClass().getClassLoader());
} }
catch (NamingException ex) { catch (Exception ex) {
// Naming is not enabled. Continue containerCounter.decrementAndGet();
throw ex;
} }
// Unlike Jetty, all Tomcat threads are daemon threads. We create a
// blocking non-daemon to stop immediate shutdown
startDaemonAwaitThread();
} }
catch (Exception ex) { catch (Exception ex) {
throw new EmbeddedWebServerException( throw new EmbeddedWebServerException("Unable to start embedded Tomcat",
"Unable to start embedded Tomcat", ex); ex);
} }
} }
} }
@ -279,9 +285,7 @@ public class TomcatEmbeddedServletContainer implements EmbeddedWebServer {
@Override @Override
public void stop() throws EmbeddedWebServerException { public void stop() throws EmbeddedWebServerException {
synchronized (this.monitor) { synchronized (this.monitor) {
if (!this.started) { boolean wasStarted = this.started;
return;
}
try { try {
this.started = false; this.started = false;
try { try {
@ -293,11 +297,13 @@ public class TomcatEmbeddedServletContainer implements EmbeddedWebServer {
} }
} }
catch (Exception ex) { catch (Exception ex) {
throw new EmbeddedWebServerException( throw new EmbeddedWebServerException("Unable to stop embedded Tomcat",
"Unable to stop embedded Tomcat", ex); ex);
} }
finally { finally {
containerCounter.decrementAndGet(); if (wasStarted) {
containerCounter.decrementAndGet();
}
} }
} }
} }

@ -174,6 +174,16 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests {
assertThat(this.output.toString()).containsOnlyOnce("started on port"); assertThat(this.output.toString()).containsOnlyOnce("started on port");
} }
@Test
public void stopCalledTwice() throws Exception {
AbstractEmbeddedServletContainerFactory factory = getFactory();
this.container = factory
.getEmbeddedServletContainer(exampleServletRegistration());
this.container.start();
this.container.stop();
this.container.stop();
}
@Test @Test
public void emptyServerWhenPortIsMinusOne() throws Exception { public void emptyServerWhenPortIsMinusOne() throws Exception {
AbstractEmbeddedServletContainerFactory factory = getFactory(); AbstractEmbeddedServletContainerFactory factory = getFactory();
@ -315,16 +325,6 @@ public abstract class AbstractEmbeddedServletContainerFactoryTests {
getFactory().setContextPath("/"); getFactory().setContextPath("/");
} }
@Test
public void doubleStop() throws Exception {
AbstractEmbeddedServletContainerFactory factory = getFactory();
this.container = factory
.getEmbeddedServletContainer(exampleServletRegistration());
this.container.start();
this.container.stop();
this.container.stop();
}
@Test @Test
public void multipleConfigurations() throws Exception { public void multipleConfigurations() throws Exception {
AbstractEmbeddedServletContainerFactory factory = getFactory(); AbstractEmbeddedServletContainerFactory factory = getFactory();

@ -143,6 +143,16 @@ public class JettyEmbeddedServletContainerFactoryTests
.isEmpty(); .isEmpty();
} }
@Test
public void stopCalledWithoutStart() throws Exception {
JettyEmbeddedServletContainerFactory factory = getFactory();
this.container = factory
.getEmbeddedServletContainer(exampleServletRegistration());
this.container.stop();
Server server = ((JettyEmbeddedServletContainer) this.container).getServer();
assertThat(server.isStopped()).isTrue();
}
@Override @Override
protected void addConnector(final int port, protected void addConnector(final int port,
AbstractEmbeddedServletContainerFactory factory) { AbstractEmbeddedServletContainerFactory factory) {

@ -352,6 +352,16 @@ public class TomcatEmbeddedServletContainerFactoryTests
.doesNotContain("appears to have started a thread named [main]"); .doesNotContain("appears to have started a thread named [main]");
} }
@Test
public void stopCalledWithoutStart() throws Exception {
TomcatEmbeddedServletContainerFactory factory = getFactory();
this.container = factory
.getEmbeddedServletContainer(exampleServletRegistration());
this.container.stop();
Tomcat tomcat = ((TomcatEmbeddedServletContainer) this.container).getTomcat();
assertThat(tomcat.getServer().getState()).isSameAs(LifecycleState.DESTROYED);
}
@Override @Override
protected void addConnector(int port, protected void addConnector(int port,
AbstractEmbeddedServletContainerFactory factory) { AbstractEmbeddedServletContainerFactory factory) {

Loading…
Cancel
Save