Check authorities of user in HealthMvcEndpoint

We need to be a bit cautious about whether Spring Security is on
the classpath or not, but if it is we can test for the admin role
(as specified in `management.security.role`).

Fixes gh-4060
pull/4077/merge
Dave Syer 9 years ago
parent 972557851a
commit e1070cce07

@ -31,7 +31,10 @@ import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
@ -54,6 +57,8 @@ public class HealthMvcEndpoint implements MvcEndpoint, EnvironmentAware {
private RelaxedPropertyResolver propertyResolver; private RelaxedPropertyResolver propertyResolver;
private RelaxedPropertyResolver roleResolver;
private long lastAccess = 0; private long lastAccess = 0;
private Health cached; private Health cached;
@ -80,6 +85,8 @@ public class HealthMvcEndpoint implements MvcEndpoint, EnvironmentAware {
public void setEnvironment(Environment environment) { public void setEnvironment(Environment environment) {
this.propertyResolver = new RelaxedPropertyResolver(environment, this.propertyResolver = new RelaxedPropertyResolver(environment,
"endpoints.health."); "endpoints.health.");
this.roleResolver = new RelaxedPropertyResolver(environment,
"management.security.");
} }
/** /**
@ -177,8 +184,22 @@ public class HealthMvcEndpoint implements MvcEndpoint, EnvironmentAware {
} }
private boolean isSecure(Principal principal) { private boolean isSecure(Principal principal) {
return (principal != null if (principal == null || principal.getClass().getName().contains("Anonymous")) {
&& !principal.getClass().getName().contains("Anonymous")); return false;
}
if (!ClassUtils.isPresent("org.springframework.security.core.Authentication",
null) || !(principal instanceof Authentication)) {
return false;
}
String role = this.roleResolver.getProperty("role", "ROLE_ADMIN");
Authentication authentication = (Authentication) principal;
for (GrantedAuthority authority : authentication.getAuthorities()) {
String name = authority.getAuthority();
if (role.equals(name) || ("ROLE_" + role).equals(name)) {
return true;
}
}
return false;
} }
private boolean isUnrestricted() { private boolean isUnrestricted() {

@ -20,8 +20,8 @@ import org.junit.Ignore;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.junit.runners.Suite; import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses; import org.junit.runners.Suite.SuiteClasses;
import org.springframework.boot.actuate.autoconfigure.MetricRepositoryAutoConfigurationTests; import org.springframework.boot.actuate.endpoint.FlywayEndpointTests;
import org.springframework.boot.actuate.autoconfigure.PublicMetricsAutoConfigurationTests; import org.springframework.boot.actuate.health.DataSourceHealthIndicatorTests;
/** /**
* A test suite for probing weird ordering problems in the tests. * A test suite for probing weird ordering problems in the tests.
@ -29,8 +29,7 @@ import org.springframework.boot.actuate.autoconfigure.PublicMetricsAutoConfigura
* @author Dave Syer * @author Dave Syer
*/ */
@RunWith(Suite.class) @RunWith(Suite.class)
@SuiteClasses({ PublicMetricsAutoConfigurationTests.class, @SuiteClasses({ DataSourceHealthIndicatorTests.class, FlywayEndpointTests.class })
MetricRepositoryAutoConfigurationTests.class })
@Ignore @Ignore
public class AdhocTestSuite { public class AdhocTestSuite {

@ -63,6 +63,10 @@ public class HealthMvcEndpointTests {
"user", "password", "user", "password",
AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER")); AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER"));
private UsernamePasswordAuthenticationToken admin = new UsernamePasswordAuthenticationToken(
"user", "password",
AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_ADMIN"));
@Before @Before
public void init() { public void init() {
this.endpoint = mock(HealthEndpoint.class); this.endpoint = mock(HealthEndpoint.class);
@ -94,10 +98,10 @@ public class HealthMvcEndpointTests {
@Test @Test
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void customMapping() { public void customMapping() {
given(this.endpoint.invoke()).willReturn( given(this.endpoint.invoke())
new Health.Builder().status("OK").build()); .willReturn(new Health.Builder().status("OK").build());
this.mvc.setStatusMapping(Collections.singletonMap("OK", this.mvc.setStatusMapping(
HttpStatus.INTERNAL_SERVER_ERROR)); Collections.singletonMap("OK", HttpStatus.INTERNAL_SERVER_ERROR));
Object result = this.mvc.invoke(null); Object result = this.mvc.invoke(null);
assertTrue(result instanceof ResponseEntity); assertTrue(result instanceof ResponseEntity);
ResponseEntity<Health> response = (ResponseEntity<Health>) result; ResponseEntity<Health> response = (ResponseEntity<Health>) result;
@ -108,8 +112,8 @@ public class HealthMvcEndpointTests {
@Test @Test
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void customMappingWithRelaxedName() { public void customMappingWithRelaxedName() {
given(this.endpoint.invoke()).willReturn( given(this.endpoint.invoke())
new Health.Builder().outOfService().build()); .willReturn(new Health.Builder().outOfService().build());
this.mvc.setStatusMapping(Collections.singletonMap("out-of-service", this.mvc.setStatusMapping(Collections.singletonMap("out-of-service",
HttpStatus.INTERNAL_SERVER_ERROR)); HttpStatus.INTERNAL_SERVER_ERROR));
Object result = this.mvc.invoke(null); Object result = this.mvc.invoke(null);
@ -120,23 +124,33 @@ public class HealthMvcEndpointTests {
} }
@Test @Test
public void secure() { public void secureEvenWhenNotSensitive() {
given(this.endpoint.invoke()).willReturn( given(this.endpoint.invoke())
new Health.Builder().up().withDetail("foo", "bar").build()); .willReturn(new Health.Builder().up().withDetail("foo", "bar").build());
given(this.endpoint.isSensitive()).willReturn(false); given(this.endpoint.isSensitive()).willReturn(false);
Object result = this.mvc.invoke(this.user); Object result = this.mvc.invoke(this.admin);
assertTrue(result instanceof Health); assertTrue(result instanceof Health);
assertTrue(((Health) result).getStatus() == Status.UP); assertTrue(((Health) result).getStatus() == Status.UP);
assertEquals("bar", ((Health) result).getDetails().get("foo")); assertEquals("bar", ((Health) result).getDetails().get("foo"));
} }
@Test
public void secureNonAdmin() {
given(this.endpoint.invoke())
.willReturn(new Health.Builder().up().withDetail("foo", "bar").build());
Object result = this.mvc.invoke(this.user);
assertTrue(result instanceof Health);
assertTrue(((Health) result).getStatus() == Status.UP);
assertNull(((Health) result).getDetails().get("foo"));
}
@Test @Test
public void healthIsCached() { public void healthIsCached() {
given(this.endpoint.getTimeToLive()).willReturn(10000L); given(this.endpoint.getTimeToLive()).willReturn(10000L);
given(this.endpoint.isSensitive()).willReturn(true); given(this.endpoint.isSensitive()).willReturn(true);
given(this.endpoint.invoke()).willReturn( given(this.endpoint.invoke())
new Health.Builder().up().withDetail("foo", "bar").build()); .willReturn(new Health.Builder().up().withDetail("foo", "bar").build());
Object result = this.mvc.invoke(this.user); Object result = this.mvc.invoke(this.admin);
assertTrue(result instanceof Health); assertTrue(result instanceof Health);
Health health = (Health) result; Health health = (Health) result;
assertTrue(health.getStatus() == Status.UP); assertTrue(health.getStatus() == Status.UP);
@ -156,8 +170,8 @@ public class HealthMvcEndpointTests {
public void unsecureAnonymousAccessUnrestricted() { public void unsecureAnonymousAccessUnrestricted() {
this.mvc = new HealthMvcEndpoint(this.endpoint, false); this.mvc = new HealthMvcEndpoint(this.endpoint, false);
this.mvc.setEnvironment(this.environment); this.mvc.setEnvironment(this.environment);
given(this.endpoint.invoke()).willReturn( given(this.endpoint.invoke())
new Health.Builder().up().withDetail("foo", "bar").build()); .willReturn(new Health.Builder().up().withDetail("foo", "bar").build());
Object result = this.mvc.invoke(null); Object result = this.mvc.invoke(null);
assertTrue(result instanceof Health); assertTrue(result instanceof Health);
assertTrue(((Health) result).getStatus() == Status.UP); assertTrue(((Health) result).getStatus() == Status.UP);
@ -167,8 +181,8 @@ public class HealthMvcEndpointTests {
@Test @Test
public void unsensitiveAnonymousAccessRestricted() { public void unsensitiveAnonymousAccessRestricted() {
this.environment.getPropertySources().addLast(NON_SENSITIVE); this.environment.getPropertySources().addLast(NON_SENSITIVE);
given(this.endpoint.invoke()).willReturn( given(this.endpoint.invoke())
new Health.Builder().up().withDetail("foo", "bar").build()); .willReturn(new Health.Builder().up().withDetail("foo", "bar").build());
Object result = this.mvc.invoke(null); Object result = this.mvc.invoke(null);
assertTrue(result instanceof Health); assertTrue(result instanceof Health);
assertTrue(((Health) result).getStatus() == Status.UP); assertTrue(((Health) result).getStatus() == Status.UP);
@ -180,8 +194,8 @@ public class HealthMvcEndpointTests {
this.mvc = new HealthMvcEndpoint(this.endpoint, false); this.mvc = new HealthMvcEndpoint(this.endpoint, false);
this.mvc.setEnvironment(this.environment); this.mvc.setEnvironment(this.environment);
this.environment.getPropertySources().addLast(NON_SENSITIVE); this.environment.getPropertySources().addLast(NON_SENSITIVE);
given(this.endpoint.invoke()).willReturn( given(this.endpoint.invoke())
new Health.Builder().up().withDetail("foo", "bar").build()); .willReturn(new Health.Builder().up().withDetail("foo", "bar").build());
Object result = this.mvc.invoke(null); Object result = this.mvc.invoke(null);
assertTrue(result instanceof Health); assertTrue(result instanceof Health);
assertTrue(((Health) result).getStatus() == Status.UP); assertTrue(((Health) result).getStatus() == Status.UP);
@ -191,8 +205,8 @@ public class HealthMvcEndpointTests {
@Test @Test
public void noCachingWhenTimeToLiveIsZero() { public void noCachingWhenTimeToLiveIsZero() {
given(this.endpoint.getTimeToLive()).willReturn(0L); given(this.endpoint.getTimeToLive()).willReturn(0L);
given(this.endpoint.invoke()).willReturn( given(this.endpoint.invoke())
new Health.Builder().up().withDetail("foo", "bar").build()); .willReturn(new Health.Builder().up().withDetail("foo", "bar").build());
Object result = this.mvc.invoke(null); Object result = this.mvc.invoke(null);
assertTrue(result instanceof Health); assertTrue(result instanceof Health);
assertTrue(((Health) result).getStatus() == Status.UP); assertTrue(((Health) result).getStatus() == Status.UP);
@ -207,8 +221,8 @@ public class HealthMvcEndpointTests {
public void newValueIsReturnedOnceTtlExpires() throws InterruptedException { public void newValueIsReturnedOnceTtlExpires() throws InterruptedException {
given(this.endpoint.getTimeToLive()).willReturn(50L); given(this.endpoint.getTimeToLive()).willReturn(50L);
given(this.endpoint.isSensitive()).willReturn(false); given(this.endpoint.isSensitive()).willReturn(false);
given(this.endpoint.invoke()).willReturn( given(this.endpoint.invoke())
new Health.Builder().up().withDetail("foo", "bar").build()); .willReturn(new Health.Builder().up().withDetail("foo", "bar").build());
Object result = this.mvc.invoke(null); Object result = this.mvc.invoke(null);
assertTrue(result instanceof Health); assertTrue(result instanceof Health);
assertTrue(((Health) result).getStatus() == Status.UP); assertTrue(((Health) result).getStatus() == Status.UP);

@ -182,8 +182,8 @@ public class MvcEndpointIntegrationTests {
EnvironmentTestUtils.addEnvironment(this.context, EnvironmentTestUtils.addEnvironment(this.context,
"spring.jackson.serialization.indent-output:true"); "spring.jackson.serialization.indent-output:true");
MockMvc mockMvc = createMockMvc(); MockMvc mockMvc = createMockMvc();
mockMvc.perform(get("/beans")).andExpect( mockMvc.perform(get("/beans"))
content().string(startsWith("{" + LINE_SEPARATOR))); .andExpect(content().string(startsWith("{" + LINE_SEPARATOR)));
} }
private MockMvc createMockMvc() { private MockMvc createMockMvc() {
@ -205,8 +205,8 @@ public class MvcEndpointIntegrationTests {
} }
@ImportAutoConfiguration({ JacksonAutoConfiguration.class, @ImportAutoConfiguration({ JacksonAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class, EndpointAutoConfiguration.class,
EndpointAutoConfiguration.class, EndpointWebMvcAutoConfiguration.class, EndpointWebMvcAutoConfiguration.class,
ManagementServerPropertiesAutoConfiguration.class, ManagementServerPropertiesAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class, WebMvcAutoConfiguration.class }) PropertyPlaceholderAutoConfiguration.class, WebMvcAutoConfiguration.class })
static class DefaultConfiguration { static class DefaultConfiguration {
@ -224,8 +224,8 @@ public class MvcEndpointIntegrationTests {
@ImportAutoConfiguration({ HypermediaAutoConfiguration.class, @ImportAutoConfiguration({ HypermediaAutoConfiguration.class,
RepositoryRestMvcAutoConfiguration.class, JacksonAutoConfiguration.class, RepositoryRestMvcAutoConfiguration.class, JacksonAutoConfiguration.class,
HttpMessageConvertersAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class, EndpointAutoConfiguration.class,
EndpointAutoConfiguration.class, EndpointWebMvcAutoConfiguration.class, EndpointWebMvcAutoConfiguration.class,
ManagementServerPropertiesAutoConfiguration.class, ManagementServerPropertiesAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class, WebMvcAutoConfiguration.class }) PropertyPlaceholderAutoConfiguration.class, WebMvcAutoConfiguration.class })
static class SpringDataRestConfiguration { static class SpringDataRestConfiguration {

Loading…
Cancel
Save