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
This commit is contained in:
@@ -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]]
|
||||
|
||||
Reference in New Issue
Block a user