Provide a way to opt-in to endpoint enablement

Update AbstractEndpoint so that the `enable` property is optional and
when it not specified the `endpoints.enabled` property will be used.

This allows users to switch the way that endpoints are enabled. Rather
than opting-out specific endpoint enablement the `endpoints.enabled`
property can be set to `false` and specific endpoints can be opted-in.

Fixes gh-2102
pull/2111/head
Phillip Webb 10 years ago
parent 3e2433c842
commit a27217ae43

@ -19,13 +19,20 @@ package org.springframework.boot.actuate.endpoint;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
/**
* Abstract base for {@link Endpoint} implementations.
*
* @author Phillip Webb
* @author Christian Dupuis
*/
public abstract class AbstractEndpoint<T> implements Endpoint<T> {
public abstract class AbstractEndpoint<T> implements Endpoint<T>, EnvironmentAware {
private static final String ENDPOINTS_ENABLED_PROPERTY = "endpoints.enabled";
private Environment environment;
/**
* Endpoint identifier. With HTTP monitoring the identifier of the endpoint is mapped
@ -36,25 +43,52 @@ public abstract class AbstractEndpoint<T> implements Endpoint<T> {
private String id;
/**
* Enable security on the endpoint.
* Mark if the endpoint exposes sensitive information.
*/
private boolean sensitive;
/**
* Enable the endpoint.
*/
private boolean enabled = true;
private Boolean enabled;
/**
* Create a new sensitive endpoint instance. The enpoint will enabled flag will be
* based on the spring {@link Environment} unless explicitly set.
* @param id the endpoint ID
*/
public AbstractEndpoint(String id) {
this(id, true, true);
this(id, true);
}
/**
* Create a new endpoint instance. The enpoint will enabled flag will be based on the
* spring {@link Environment} unless explicitly set.
* @param id the endpoint ID
* @param sensitive if the endpoint is sensitive
*/
public AbstractEndpoint(String id, boolean sensitive) {
this.id = id;
this.sensitive = sensitive;
}
/**
* Create a new endpoint instance.
* @param id the endpoint ID
* @param sensitive if the endpoint is sensitive
* @param enabled if the endpoint is enabled or not.
*/
public AbstractEndpoint(String id, boolean sensitive, boolean enabled) {
this.id = id;
this.sensitive = sensitive;
this.enabled = enabled;
}
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
@Override
public String getId() {
return this.id;
@ -66,10 +100,16 @@ public abstract class AbstractEndpoint<T> implements Endpoint<T> {
@Override
public boolean isEnabled() {
return this.enabled;
if (this.enabled != null) {
return this.enabled;
}
if (this.environment != null) {
this.environment.getProperty(ENDPOINTS_ENABLED_PROPERTY, Boolean.class, true);
}
return true;
}
public void setEnabled(boolean enabled) {
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}

@ -18,11 +18,13 @@ package org.springframework.boot.actuate.endpoint;
/**
* An endpoint that can be used to expose useful information to operations. Usually
* exposed via Spring MVC but could also be exposed using some other technique.
* exposed via Spring MVC but could also be exposed using some other technique. Consider
* extending {@link AbstractEndpoint} if you are developing your own endpoint.
*
* @author Phillip Webb
* @author Dave Syer
* @author Christian Dupuis
* @see AbstractEndpoint
*/
public interface Endpoint<T> {

@ -47,7 +47,7 @@ public class HealthEndpoint extends AbstractEndpoint<Health> {
*/
public HealthEndpoint(HealthAggregator healthAggregator,
Map<String, HealthIndicator> healthIndicators) {
super("health", false, true);
super("health", false);
Assert.notNull(healthAggregator, "HealthAggregator must not be null");
Assert.notNull(healthIndicators, "HealthIndicators must not be null");
CompositeHealthIndicator healthIndicator = new CompositeHealthIndicator(

@ -39,7 +39,7 @@ public class InfoEndpoint extends AbstractEndpoint<Map<String, Object>> {
* @param info the info to expose
*/
public InfoEndpoint(Map<String, ? extends Object> info) {
super("info", false, true);
super("info", false);
Assert.notNull(info, "Info must not be null");
this.info = info;
}

@ -1,4 +1,10 @@
{"properties": [
{
"name": "endpoints.enabled",
"type": "java.lang.Boolean",
"description": "Enable endpoints.",
"defaultValue": true
},
{
"name": "endpoints.configprops.keys-to-sanitize",
"type": "java.lang.String",

@ -202,7 +202,7 @@ public class EndpointMBeanExportAutoConfigurationTests {
protected static class ManagedEndpoint extends AbstractEndpoint<Boolean> {
public ManagedEndpoint() {
super("managed", true, true);
super("managed", true);
}
@Override
@ -224,7 +224,7 @@ public class EndpointMBeanExportAutoConfigurationTests {
class Nested extends AbstractEndpoint<Boolean> {
public Nested() {
super("managed", true, true);
super("managed", true);
}
@Override

@ -102,6 +102,37 @@ public abstract class AbstractEndpointTests<T extends Endpoint<?>> {
assertThat(getEndpointBean().isSensitive(), equalTo(!this.sensitive));
}
@Test
public void isEnabledByDefault() throws Exception {
assertThat(getEndpointBean().isEnabled(), equalTo(true));
}
@Test
public void isEnabledFallbackToEnvironment() throws Exception {
this.context = new AnnotationConfigApplicationContext();
PropertySource<?> propertySource = new MapPropertySource("test",
Collections.<String, Object> singletonMap(this.property + ".enabled",
false));
this.context.getEnvironment().getPropertySources().addFirst(propertySource);
this.context.register(this.configClass);
this.context.refresh();
assertThat(getEndpointBean().isEnabled(), equalTo(false));
}
@Test
@SuppressWarnings("rawtypes")
public void isExplicitlyEnabled() throws Exception {
this.context = new AnnotationConfigApplicationContext();
PropertySource<?> propertySource = new MapPropertySource("test",
Collections.<String, Object> singletonMap(this.property + ".enabled",
false));
this.context.getEnvironment().getPropertySources().addFirst(propertySource);
this.context.register(this.configClass);
this.context.refresh();
((AbstractEndpoint) getEndpointBean()).setEnabled(true);
assertThat(getEndpointBean().isEnabled(), equalTo(true));
}
@SuppressWarnings("unchecked")
protected T getEndpointBean() {
return (T) this.context.getBean(this.type);

@ -26,6 +26,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ContextClosedEvent;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.startsWith;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
@ -43,6 +44,12 @@ public class ShutdownEndpointTests extends AbstractEndpointTests<ShutdownEndpoin
"endpoints.shutdown");
}
@Override
public void isEnabledByDefault() throws Exception {
// Shutdown is dangerous so is disabled by default
assertThat(getEndpointBean().isEnabled(), equalTo(false));
}
@Test
public void invoke() throws Exception {
CountDownLatch latch = this.context.getBean(Config.class).latch;
@ -61,7 +68,6 @@ public class ShutdownEndpointTests extends AbstractEndpointTests<ShutdownEndpoin
@Bean
public ShutdownEndpoint endpoint() {
ShutdownEndpoint endpoint = new ShutdownEndpoint();
endpoint.setEnabled(true);
return endpoint;
}

@ -135,6 +135,16 @@ of the `beans` endpoint and also enables `shutdown`.
NOTE: The prefix ‟`endpoints` + `.` + `name`” is used to uniquely identify the endpoint
that is being configured.
By default, all endpoints except for `shutdown` are enabled. If you prefer to
specifically "`opt-in`" endpoint enablement you can use the `endpoints.enabled` property.
For example, the following will disable _all_ endpoints except for `info`:
[source,properties,indent=0]
----
endpoints.enabled=false
endpoints.info.enabled=true
----
[[production-ready-health]]
@ -394,6 +404,10 @@ in your `application.properties`:
management.security.role=SUPERUSER
----
TIP: If you don't use Spring Security and your HTTP endpoints are exposed publicly,
you should carefully consider which endpoints you enable. See
<<production-ready-customizing-endpoints>> for details of how you can set
`endpoints.enabled` to `false` then "`opt-in`" only specific endpoints.
[[production-ready-customizing-management-server-context-path]]

Loading…
Cancel
Save