Commit 24e71e86 authored by Dave Syer's avatar Dave Syer

Allow /health and /info to authenticate anonymously

Then we can optionally find a non-anonymous principal if there
is one. If the user is anonymous then the health result is cached
up to endpoints.health.ttl (default 1000ms) to prevent a DOS attack.

Fixes gh-1353
parent 43eda4ce
...@@ -141,11 +141,9 @@ public class ManagementSecurityAutoConfiguration { ...@@ -141,11 +141,9 @@ public class ManagementSecurityAutoConfiguration {
// add them back. // add them back.
List<String> ignored = SpringBootWebSecurityConfiguration List<String> ignored = SpringBootWebSecurityConfiguration
.getIgnored(this.security); .getIgnored(this.security);
ignored.addAll(Arrays.asList(getEndpointPaths(this.endpointHandlerMapping,
false)));
if (!this.management.getSecurity().isEnabled()) { if (!this.management.getSecurity().isEnabled()) {
ignored.addAll(Arrays.asList(getEndpointPaths( ignored.addAll(Arrays.asList(getEndpointPaths(
this.endpointHandlerMapping, true))); this.endpointHandlerMapping)));
} }
if (ignored.contains("none")) { if (ignored.contains("none")) {
ignored.remove("none"); ignored.remove("none");
...@@ -220,7 +218,7 @@ public class ManagementSecurityAutoConfiguration { ...@@ -220,7 +218,7 @@ public class ManagementSecurityAutoConfiguration {
protected void configure(HttpSecurity http) throws Exception { protected void configure(HttpSecurity http) throws Exception {
// secure endpoints // secure endpoints
String[] paths = getEndpointPaths(this.endpointHandlerMapping, true); String[] paths = getEndpointPaths(this.endpointHandlerMapping);
if (paths.length > 0 && this.management.getSecurity().isEnabled()) { if (paths.length > 0 && this.management.getSecurity().isEnabled()) {
// Always protect them if present // Always protect them if present
if (this.security.isRequireSsl()) { if (this.security.isRequireSsl()) {
...@@ -229,10 +227,12 @@ public class ManagementSecurityAutoConfiguration { ...@@ -229,10 +227,12 @@ public class ManagementSecurityAutoConfiguration {
http.exceptionHandling().authenticationEntryPoint(entryPoint()); http.exceptionHandling().authenticationEntryPoint(entryPoint());
paths = this.server.getPathsArray(paths); paths = this.server.getPathsArray(paths);
http.requestMatchers().antMatchers(paths); http.requestMatchers().antMatchers(paths);
http.authorizeRequests().anyRequest() // @formatter:off
.hasRole(this.management.getSecurity().getRole()) // http.authorizeRequests()
.and().httpBasic() // .antMatchers(this.server.getPathsArray(getEndpointPaths(this.endpointHandlerMapping, false))).access("permitAll()")
.and().anonymous().disable(); .anyRequest().hasRole(this.management.getSecurity().getRole());
// @formatter:on
http.httpBasic();
// No cookies for management endpoints by default // No cookies for management endpoints by default
http.csrf().disable(); http.csrf().disable();
...@@ -254,6 +254,12 @@ public class ManagementSecurityAutoConfiguration { ...@@ -254,6 +254,12 @@ public class ManagementSecurityAutoConfiguration {
} }
private static String[] getEndpointPaths(EndpointHandlerMapping endpointHandlerMapping) {
return StringUtils.mergeStringArrays(
getEndpointPaths(endpointHandlerMapping, false),
getEndpointPaths(endpointHandlerMapping, true));
}
private static String[] getEndpointPaths( private static String[] getEndpointPaths(
EndpointHandlerMapping endpointHandlerMapping, boolean secure) { EndpointHandlerMapping endpointHandlerMapping, boolean secure) {
if (endpointHandlerMapping == null) { if (endpointHandlerMapping == null) {
...@@ -264,13 +270,11 @@ public class ManagementSecurityAutoConfiguration { ...@@ -264,13 +270,11 @@ public class ManagementSecurityAutoConfiguration {
List<String> paths = new ArrayList<String>(endpoints.size()); List<String> paths = new ArrayList<String>(endpoints.size());
for (MvcEndpoint endpoint : endpoints) { for (MvcEndpoint endpoint : endpoints) {
if (endpoint.isSensitive() == secure) { if (endpoint.isSensitive() == secure) {
String path = endpointHandlerMapping.getPrefix() + endpoint.getPath(); String path = endpointHandlerMapping.getPath(endpoint.getPath());
paths.add(path); paths.add(path);
if (secure) { // Add Spring MVC-generated additional paths
// Add Spring MVC-generated additional paths paths.add(path + "/");
paths.add(path + "/"); paths.add(path + ".*");
paths.add(path + ".*");
}
} }
} }
return paths.toArray(new String[paths.size()]); return paths.toArray(new String[paths.size()]);
......
...@@ -202,6 +202,7 @@ public class ConfigurationPropertiesReportEndpoint extends ...@@ -202,6 +202,7 @@ public class ConfigurationPropertiesReportEndpoint extends
* Extension to {@link JacksonAnnotationIntrospector} to suppress CGLIB generated bean * Extension to {@link JacksonAnnotationIntrospector} to suppress CGLIB generated bean
* properties. * properties.
*/ */
@SuppressWarnings("serial")
private static class CglibAnnotationIntrospector extends private static class CglibAnnotationIntrospector extends
JacksonAnnotationIntrospector { JacksonAnnotationIntrospector {
......
...@@ -36,6 +36,22 @@ public class HealthEndpoint extends AbstractEndpoint<Health> { ...@@ -36,6 +36,22 @@ public class HealthEndpoint extends AbstractEndpoint<Health> {
private final HealthIndicator healthIndicator; private final HealthIndicator healthIndicator;
private long ttl = 1000;
/**
* Time to live for cached result. If accessed anonymously, we might need to cache the
* result of this endpoint to prevent a DOS attack.
*
* @return time to live in milliseconds (default 1000)
*/
public long getTtl() {
return ttl;
}
public void setTtl(long ttl) {
this.ttl = ttl;
}
/** /**
* Create a new {@link HealthIndicator} instance. * Create a new {@link HealthIndicator} instance.
*/ */
......
...@@ -18,7 +18,9 @@ package org.springframework.boot.actuate.endpoint.mvc; ...@@ -18,7 +18,9 @@ package org.springframework.boot.actuate.endpoint.mvc;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map;
import java.util.Set; import java.util.Set;
import org.springframework.boot.actuate.endpoint.Endpoint; import org.springframework.boot.actuate.endpoint.Endpoint;
...@@ -50,7 +52,7 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandl ...@@ -50,7 +52,7 @@ import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandl
public class EndpointHandlerMapping extends RequestMappingHandlerMapping implements public class EndpointHandlerMapping extends RequestMappingHandlerMapping implements
ApplicationContextAware { ApplicationContextAware {
private final Set<? extends MvcEndpoint> endpoints; private final Map<String, MvcEndpoint> endpoints = new HashMap<String, MvcEndpoint>();
private String prefix = ""; private String prefix = "";
...@@ -62,7 +64,10 @@ public class EndpointHandlerMapping extends RequestMappingHandlerMapping impleme ...@@ -62,7 +64,10 @@ public class EndpointHandlerMapping extends RequestMappingHandlerMapping impleme
* @param endpoints * @param endpoints
*/ */
public EndpointHandlerMapping(Collection<? extends MvcEndpoint> endpoints) { public EndpointHandlerMapping(Collection<? extends MvcEndpoint> endpoints) {
this.endpoints = new HashSet<MvcEndpoint>(endpoints); HashMap<String, MvcEndpoint> map = (HashMap<String, MvcEndpoint>) this.endpoints;
for (MvcEndpoint endpoint : endpoints) {
map.put(endpoint.getPath(), endpoint);
}
// By default the static resource handler mapping is LOWEST_PRECEDENCE - 1 // By default the static resource handler mapping is LOWEST_PRECEDENCE - 1
// and the RequestMappingHandlerMapping is 0 (we ideally want to be before both) // and the RequestMappingHandlerMapping is 0 (we ideally want to be before both)
setOrder(-100); setOrder(-100);
...@@ -72,7 +77,7 @@ public class EndpointHandlerMapping extends RequestMappingHandlerMapping impleme ...@@ -72,7 +77,7 @@ public class EndpointHandlerMapping extends RequestMappingHandlerMapping impleme
public void afterPropertiesSet() { public void afterPropertiesSet() {
super.afterPropertiesSet(); super.afterPropertiesSet();
if (!this.disabled) { if (!this.disabled) {
for (MvcEndpoint endpoint : this.endpoints) { for (MvcEndpoint endpoint : this.endpoints.values()) {
detectHandlerMethods(endpoint); detectHandlerMethods(endpoint);
} }
} }
...@@ -146,6 +151,13 @@ public class EndpointHandlerMapping extends RequestMappingHandlerMapping impleme ...@@ -146,6 +151,13 @@ public class EndpointHandlerMapping extends RequestMappingHandlerMapping impleme
return this.prefix; return this.prefix;
} }
/**
* @return the path used in mappings
*/
public String getPath(String endpoint) {
return this.prefix + endpoint;
}
/** /**
* Sets if this mapping is disabled. * Sets if this mapping is disabled.
*/ */
...@@ -164,7 +176,7 @@ public class EndpointHandlerMapping extends RequestMappingHandlerMapping impleme ...@@ -164,7 +176,7 @@ public class EndpointHandlerMapping extends RequestMappingHandlerMapping impleme
* Return the endpoints * Return the endpoints
*/ */
public Set<? extends MvcEndpoint> getEndpoints() { public Set<? extends MvcEndpoint> getEndpoints() {
return this.endpoints; return new HashSet<MvcEndpoint>(this.endpoints.values());
} }
} }
...@@ -56,6 +56,7 @@ public class EnvironmentMvcEndpoint extends EndpointMvcAdapter implements ...@@ -56,6 +56,7 @@ public class EnvironmentMvcEndpoint extends EndpointMvcAdapter implements
this.environment = environment; this.environment = environment;
} }
@SuppressWarnings("serial")
@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "No such property") @ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "No such property")
public static class NoSuchPropertyException extends RuntimeException { public static class NoSuchPropertyException extends RuntimeException {
......
...@@ -16,10 +16,12 @@ ...@@ -16,10 +16,12 @@
package org.springframework.boot.actuate.endpoint.mvc; package org.springframework.boot.actuate.endpoint.mvc;
import java.security.Principal;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.springframework.boot.actuate.endpoint.Endpoint;
import org.springframework.boot.actuate.endpoint.HealthEndpoint; import org.springframework.boot.actuate.endpoint.HealthEndpoint;
import org.springframework.boot.actuate.health.Health; import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.Status; import org.springframework.boot.actuate.health.Status;
...@@ -33,14 +35,21 @@ import org.springframework.web.bind.annotation.ResponseBody; ...@@ -33,14 +35,21 @@ import org.springframework.web.bind.annotation.ResponseBody;
* Adapter to expose {@link HealthEndpoint} as an {@link MvcEndpoint}. * Adapter to expose {@link HealthEndpoint} as an {@link MvcEndpoint}.
* *
* @author Christian Dupuis * @author Christian Dupuis
* @author Dave Syer
* @since 1.1.0 * @since 1.1.0
*/ */
public class HealthMvcEndpoint extends EndpointMvcAdapter { public class HealthMvcEndpoint implements MvcEndpoint {
private Map<String, HttpStatus> statusMapping = new HashMap<String, HttpStatus>(); private Map<String, HttpStatus> statusMapping = new HashMap<String, HttpStatus>();
private HealthEndpoint delegate;
private long lastAccess = 0;
private Health cached;
public HealthMvcEndpoint(HealthEndpoint delegate) { public HealthMvcEndpoint(HealthEndpoint delegate) {
super(delegate); this.delegate = delegate;
setupDefaultStatusMapping(); setupDefaultStatusMapping();
} }
...@@ -91,21 +100,65 @@ public class HealthMvcEndpoint extends EndpointMvcAdapter { ...@@ -91,21 +100,65 @@ public class HealthMvcEndpoint extends EndpointMvcAdapter {
@RequestMapping @RequestMapping
@ResponseBody @ResponseBody
@Override public Object invoke(Principal principal) {
public Object invoke() {
if (!this.getDelegate().isEnabled()) { if (!delegate.isEnabled()) {
// Shouldn't happen // Shouldn't happen because the request mapping should not be registered
return new ResponseEntity<Map<String, String>>(Collections.singletonMap( return new ResponseEntity<Map<String, String>>(Collections.singletonMap(
"message", "This endpoint is disabled"), HttpStatus.NOT_FOUND); "message", "This endpoint is disabled"), HttpStatus.NOT_FOUND);
} }
Health health = (Health) getDelegate().invoke(); Health health = getHealth(principal);
Status status = health.getStatus(); Status status = health.getStatus();
if (this.statusMapping.containsKey(status.getCode())) { if (this.statusMapping.containsKey(status.getCode())) {
return new ResponseEntity<Health>(health, this.statusMapping.get(status return new ResponseEntity<Health>(health, this.statusMapping.get(status
.getCode())); .getCode()));
} }
return health; return health;
}
private Health getHealth(Principal principal) {
Health health = useCachedValue(principal) ? cached : (Health) delegate.invoke();
// Not too worried about concurrent access here, the worst that can happen is the
// odd extra call to delegate.invoke()
cached = health;
if (!secure(principal)) {
// If not secure we only expose the status
health = Health.status(health.getStatus()).build();
}
return health;
}
private boolean secure(Principal principal) {
return principal != null && !principal.getClass().getName().contains("Anonymous");
}
private boolean useCachedValue(Principal principal) {
long currentAccess = System.currentTimeMillis();
if (cached == null || secure(principal)
|| currentAccess - lastAccess > delegate.getTtl()) {
lastAccess = currentAccess;
return false;
}
return cached != null;
}
@Override
public String getPath() {
return "/" + this.delegate.getId();
}
@Override
public boolean isSensitive() {
return this.delegate.isSensitive();
}
@Override
@SuppressWarnings("rawtypes")
public Class<? extends Endpoint> getEndpointType() {
return this.delegate.getClass();
} }
} }
...@@ -48,6 +48,7 @@ public class MetricsMvcEndpoint extends EndpointMvcAdapter { ...@@ -48,6 +48,7 @@ public class MetricsMvcEndpoint extends EndpointMvcAdapter {
return value; return value;
} }
@SuppressWarnings("serial")
@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "No such metric") @ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "No such metric")
public static class NoSuchMetricException extends RuntimeException { public static class NoSuchMetricException extends RuntimeException {
......
...@@ -73,8 +73,8 @@ public class ManagementSecurityAutoConfigurationTests { ...@@ -73,8 +73,8 @@ public class ManagementSecurityAutoConfigurationTests {
PropertyPlaceholderAutoConfiguration.class); PropertyPlaceholderAutoConfiguration.class);
this.context.refresh(); this.context.refresh();
assertNotNull(this.context.getBean(AuthenticationManagerBuilder.class)); assertNotNull(this.context.getBean(AuthenticationManagerBuilder.class));
// 6 for static resources, one for management endpoints and one for the rest // 4 for static resources, one for management endpoints and one for the rest
assertEquals(8, this.context.getBean(FilterChainProxy.class).getFilterChains() assertEquals(6, this.context.getBean(FilterChainProxy.class).getFilterChains()
.size()); .size());
} }
...@@ -144,7 +144,7 @@ public class ManagementSecurityAutoConfigurationTests { ...@@ -144,7 +144,7 @@ public class ManagementSecurityAutoConfigurationTests {
this.context.refresh(); this.context.refresh();
// Just the management endpoints (one filter) and ignores now plus the backup // Just the management endpoints (one filter) and ignores now plus the backup
// filter on app endpoints // filter on app endpoints
assertEquals(8, this.context.getBean(FilterChainProxy.class).getFilterChains() assertEquals(6, this.context.getBean(FilterChainProxy.class).getFilterChains()
.size()); .size());
} }
......
...@@ -16,6 +16,11 @@ ...@@ -16,6 +16,11 @@
package org.springframework.boot.actuate.endpoint.mvc; package org.springframework.boot.actuate.endpoint.mvc;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import java.util.Collections; import java.util.Collections;
import org.junit.Before; import org.junit.Before;
...@@ -25,11 +30,8 @@ import org.springframework.boot.actuate.health.Health; ...@@ -25,11 +30,8 @@ import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.Status; import org.springframework.boot.actuate.health.Status;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity; import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import static org.junit.Assert.assertEquals; import org.springframework.security.core.authority.AuthorityUtils;
import static org.junit.Assert.assertTrue;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
/** /**
* Tests for {@link HealthMvcEndpoint}. * Tests for {@link HealthMvcEndpoint}.
...@@ -42,6 +44,10 @@ public class HealthMvcEndpointTests { ...@@ -42,6 +44,10 @@ public class HealthMvcEndpointTests {
private HealthMvcEndpoint mvc = null; private HealthMvcEndpoint mvc = null;
private UsernamePasswordAuthenticationToken user = new UsernamePasswordAuthenticationToken(
"user", "password",
AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER"));
@Before @Before
public void init() { public void init() {
this.endpoint = mock(HealthEndpoint.class); this.endpoint = mock(HealthEndpoint.class);
...@@ -52,7 +58,7 @@ public class HealthMvcEndpointTests { ...@@ -52,7 +58,7 @@ public class HealthMvcEndpointTests {
@Test @Test
public void up() { public void up() {
given(this.endpoint.invoke()).willReturn(new Health.Builder().up().build()); given(this.endpoint.invoke()).willReturn(new Health.Builder().up().build());
Object result = this.mvc.invoke(); 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);
} }
...@@ -61,7 +67,7 @@ public class HealthMvcEndpointTests { ...@@ -61,7 +67,7 @@ public class HealthMvcEndpointTests {
@Test @Test
public void down() { public void down() {
given(this.endpoint.invoke()).willReturn(new Health.Builder().down().build()); given(this.endpoint.invoke()).willReturn(new Health.Builder().down().build());
Object result = this.mvc.invoke(); 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;
assertTrue(response.getBody().getStatus() == Status.DOWN); assertTrue(response.getBody().getStatus() == Status.DOWN);
...@@ -75,10 +81,51 @@ public class HealthMvcEndpointTests { ...@@ -75,10 +81,51 @@ public class HealthMvcEndpointTests {
new Health.Builder().status("OK").build()); new Health.Builder().status("OK").build());
this.mvc.setStatusMapping(Collections.singletonMap("OK", this.mvc.setStatusMapping(Collections.singletonMap("OK",
HttpStatus.INTERNAL_SERVER_ERROR)); HttpStatus.INTERNAL_SERVER_ERROR));
Object result = this.mvc.invoke(); 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;
assertTrue(response.getBody().getStatus().equals(new Status("OK"))); assertTrue(response.getBody().getStatus().equals(new Status("OK")));
assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode()); assertEquals(HttpStatus.INTERNAL_SERVER_ERROR, response.getStatusCode());
} }
@Test
public void secure() {
given(this.endpoint.invoke()).willReturn(
new Health.Builder().up().withDetail("foo", "bar").build());
Object result = this.mvc.invoke(user);
assertTrue(result instanceof Health);
assertTrue(((Health) result).getStatus() == Status.UP);
assertEquals("bar", ((Health) result).getDetails().get("foo"));
}
@Test
public void secureNotCached() {
given(this.endpoint.getTtl()).willReturn(10000L);
given(this.endpoint.invoke()).willReturn(
new Health.Builder().up().withDetail("foo", "bar").build());
Object result = this.mvc.invoke(user);
assertTrue(result instanceof Health);
assertTrue(((Health) result).getStatus() == Status.UP);
given(this.endpoint.invoke()).willReturn(new Health.Builder().down().build());
result = this.mvc.invoke(user);
@SuppressWarnings("unchecked")
Health health = (Health) ((ResponseEntity<Health>) result).getBody();
assertTrue(health.getStatus() == Status.DOWN);
}
@Test
public void unsecureCached() {
given(this.endpoint.getTtl()).willReturn(10000L);
given(this.endpoint.invoke()).willReturn(
new Health.Builder().up().withDetail("foo", "bar").build());
Object result = this.mvc.invoke(user);
assertTrue(result instanceof Health);
assertTrue(((Health) result).getStatus() == Status.UP);
given(this.endpoint.invoke()).willReturn(new Health.Builder().down().build());
result = this.mvc.invoke(null); // insecure now
Health health = (Health) result;
// so the result is cached
assertTrue(health.getStatus() == Status.UP);
}
} }
...@@ -17,6 +17,8 @@ ...@@ -17,6 +17,8 @@
package sample.actuator; package sample.actuator;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan;
...@@ -26,10 +28,15 @@ import org.springframework.context.annotation.Configuration; ...@@ -26,10 +28,15 @@ import org.springframework.context.annotation.Configuration;
@EnableAutoConfiguration @EnableAutoConfiguration
@EnableConfigurationProperties @EnableConfigurationProperties
@ComponentScan @ComponentScan
public class SampleActuatorApplication { public class SampleActuatorApplication implements HealthIndicator {
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
SpringApplication.run(SampleActuatorApplication.class, args); SpringApplication.run(SampleActuatorApplication.class, args);
} }
@Override
public Health health() {
return Health.up().withDetail("hello", "world").build();
}
} }
logging.file: /tmp/logs/app.log logging.file: /tmp/logs/app.log
logging.level.org.springframework.security: INFO logging.level.org.springframework.security: DEBUG
management.address: 127.0.0.1 management.address: 127.0.0.1
#management.port: 8181 #management.port: 8181
endpoints.shutdown.enabled: true endpoints.shutdown.enabled: true
......
...@@ -132,6 +132,17 @@ public class SampleActuatorApplicationTests { ...@@ -132,6 +132,17 @@ public class SampleActuatorApplicationTests {
assertEquals(HttpStatus.OK, entity.getStatusCode()); assertEquals(HttpStatus.OK, entity.getStatusCode());
assertTrue("Wrong body: " + entity.getBody(), assertTrue("Wrong body: " + entity.getBody(),
entity.getBody().contains("\"status\":\"UP\"")); entity.getBody().contains("\"status\":\"UP\""));
assertFalse("Wrong body: " + entity.getBody(),
entity.getBody().contains("\"hello\":\"1\""));
}
@Test
public void testSecureHealth() throws Exception {
ResponseEntity<String> entity = new TestRestTemplate("user", getPassword()).getForEntity(
"http://localhost:" + this.port + "/health", String.class);
assertEquals(HttpStatus.OK, entity.getStatusCode());
assertTrue("Wrong body: " + entity.getBody(),
entity.getBody().contains("\"hello\":1"));
} }
@Test @Test
......
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