Skip to content

Commit a27217a

Browse files
committed
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
1 parent 3e2433c commit a27217a

File tree

9 files changed

+111
-12
lines changed

9 files changed

+111
-12
lines changed

spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/AbstractEndpoint.java

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,20 @@
1919
import javax.validation.constraints.NotNull;
2020
import javax.validation.constraints.Pattern;
2121

22+
import org.springframework.context.EnvironmentAware;
23+
import org.springframework.core.env.Environment;
24+
2225
/**
2326
* Abstract base for {@link Endpoint} implementations.
2427
*
2528
* @author Phillip Webb
2629
* @author Christian Dupuis
2730
*/
28-
public abstract class AbstractEndpoint<T> implements Endpoint<T> {
31+
public abstract class AbstractEndpoint<T> implements Endpoint<T>, EnvironmentAware {
32+
33+
private static final String ENDPOINTS_ENABLED_PROPERTY = "endpoints.enabled";
34+
35+
private Environment environment;
2936

3037
/**
3138
* Endpoint identifier. With HTTP monitoring the identifier of the endpoint is mapped
@@ -36,25 +43,52 @@ public abstract class AbstractEndpoint<T> implements Endpoint<T> {
3643
private String id;
3744

3845
/**
39-
* Enable security on the endpoint.
46+
* Mark if the endpoint exposes sensitive information.
4047
*/
4148
private boolean sensitive;
4249

4350
/**
4451
* Enable the endpoint.
4552
*/
46-
private boolean enabled = true;
53+
private Boolean enabled;
4754

55+
/**
56+
* Create a new sensitive endpoint instance. The enpoint will enabled flag will be
57+
* based on the spring {@link Environment} unless explicitly set.
58+
* @param id the endpoint ID
59+
*/
4860
public AbstractEndpoint(String id) {
49-
this(id, true, true);
61+
this(id, true);
62+
}
63+
64+
/**
65+
* Create a new endpoint instance. The enpoint will enabled flag will be based on the
66+
* spring {@link Environment} unless explicitly set.
67+
* @param id the endpoint ID
68+
* @param sensitive if the endpoint is sensitive
69+
*/
70+
public AbstractEndpoint(String id, boolean sensitive) {
71+
this.id = id;
72+
this.sensitive = sensitive;
5073
}
5174

75+
/**
76+
* Create a new endpoint instance.
77+
* @param id the endpoint ID
78+
* @param sensitive if the endpoint is sensitive
79+
* @param enabled if the endpoint is enabled or not.
80+
*/
5281
public AbstractEndpoint(String id, boolean sensitive, boolean enabled) {
5382
this.id = id;
5483
this.sensitive = sensitive;
5584
this.enabled = enabled;
5685
}
5786

87+
@Override
88+
public void setEnvironment(Environment environment) {
89+
this.environment = environment;
90+
}
91+
5892
@Override
5993
public String getId() {
6094
return this.id;
@@ -66,10 +100,16 @@ public void setId(String id) {
66100

67101
@Override
68102
public boolean isEnabled() {
69-
return this.enabled;
103+
if (this.enabled != null) {
104+
return this.enabled;
105+
}
106+
if (this.environment != null) {
107+
this.environment.getProperty(ENDPOINTS_ENABLED_PROPERTY, Boolean.class, true);
108+
}
109+
return true;
70110
}
71111

72-
public void setEnabled(boolean enabled) {
112+
public void setEnabled(Boolean enabled) {
73113
this.enabled = enabled;
74114
}
75115

spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/Endpoint.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,13 @@
1818

1919
/**
2020
* An endpoint that can be used to expose useful information to operations. Usually
21-
* exposed via Spring MVC but could also be exposed using some other technique.
21+
* exposed via Spring MVC but could also be exposed using some other technique. Consider
22+
* extending {@link AbstractEndpoint} if you are developing your own endpoint.
2223
*
2324
* @author Phillip Webb
2425
* @author Dave Syer
2526
* @author Christian Dupuis
27+
* @see AbstractEndpoint
2628
*/
2729
public interface Endpoint<T> {
2830

spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/HealthEndpoint.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ public class HealthEndpoint extends AbstractEndpoint<Health> {
4747
*/
4848
public HealthEndpoint(HealthAggregator healthAggregator,
4949
Map<String, HealthIndicator> healthIndicators) {
50-
super("health", false, true);
50+
super("health", false);
5151
Assert.notNull(healthAggregator, "HealthAggregator must not be null");
5252
Assert.notNull(healthIndicators, "HealthIndicators must not be null");
5353
CompositeHealthIndicator healthIndicator = new CompositeHealthIndicator(

spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/InfoEndpoint.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public class InfoEndpoint extends AbstractEndpoint<Map<String, Object>> {
3939
* @param info the info to expose
4040
*/
4141
public InfoEndpoint(Map<String, ? extends Object> info) {
42-
super("info", false, true);
42+
super("info", false);
4343
Assert.notNull(info, "Info must not be null");
4444
this.info = info;
4545
}

spring-boot-actuator/src/main/resources/META-INF/additional-spring-configuration-metadata.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
11
{"properties": [
2+
{
3+
"name": "endpoints.enabled",
4+
"type": "java.lang.Boolean",
5+
"description": "Enable endpoints.",
6+
"defaultValue": true
7+
},
28
{
39
"name": "endpoints.configprops.keys-to-sanitize",
410
"type": "java.lang.String",

spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointMBeanExportAutoConfigurationTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ public static class TestConfiguration {
202202
protected static class ManagedEndpoint extends AbstractEndpoint<Boolean> {
203203

204204
public ManagedEndpoint() {
205-
super("managed", true, true);
205+
super("managed", true);
206206
}
207207

208208
@Override
@@ -224,7 +224,7 @@ public Endpoint<Boolean> nested() {
224224
class Nested extends AbstractEndpoint<Boolean> {
225225

226226
public Nested() {
227-
super("managed", true, true);
227+
super("managed", true);
228228
}
229229

230230
@Override

spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/AbstractEndpointTests.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,37 @@ Collections.<String, Object> singletonMap(this.property + ".sensitive",
102102
assertThat(getEndpointBean().isSensitive(), equalTo(!this.sensitive));
103103
}
104104

105+
@Test
106+
public void isEnabledByDefault() throws Exception {
107+
assertThat(getEndpointBean().isEnabled(), equalTo(true));
108+
}
109+
110+
@Test
111+
public void isEnabledFallbackToEnvironment() throws Exception {
112+
this.context = new AnnotationConfigApplicationContext();
113+
PropertySource<?> propertySource = new MapPropertySource("test",
114+
Collections.<String, Object> singletonMap(this.property + ".enabled",
115+
false));
116+
this.context.getEnvironment().getPropertySources().addFirst(propertySource);
117+
this.context.register(this.configClass);
118+
this.context.refresh();
119+
assertThat(getEndpointBean().isEnabled(), equalTo(false));
120+
}
121+
122+
@Test
123+
@SuppressWarnings("rawtypes")
124+
public void isExplicitlyEnabled() throws Exception {
125+
this.context = new AnnotationConfigApplicationContext();
126+
PropertySource<?> propertySource = new MapPropertySource("test",
127+
Collections.<String, Object> singletonMap(this.property + ".enabled",
128+
false));
129+
this.context.getEnvironment().getPropertySources().addFirst(propertySource);
130+
this.context.register(this.configClass);
131+
this.context.refresh();
132+
((AbstractEndpoint) getEndpointBean()).setEnabled(true);
133+
assertThat(getEndpointBean().isEnabled(), equalTo(true));
134+
}
135+
105136
@SuppressWarnings("unchecked")
106137
protected T getEndpointBean() {
107138
return (T) this.context.getBean(this.type);

spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/ShutdownEndpointTests.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.springframework.context.annotation.Configuration;
2727
import org.springframework.context.event.ContextClosedEvent;
2828

29+
import static org.hamcrest.Matchers.equalTo;
2930
import static org.hamcrest.Matchers.startsWith;
3031
import static org.junit.Assert.assertThat;
3132
import static org.junit.Assert.assertTrue;
@@ -43,6 +44,12 @@ public ShutdownEndpointTests() {
4344
"endpoints.shutdown");
4445
}
4546

47+
@Override
48+
public void isEnabledByDefault() throws Exception {
49+
// Shutdown is dangerous so is disabled by default
50+
assertThat(getEndpointBean().isEnabled(), equalTo(false));
51+
}
52+
4653
@Test
4754
public void invoke() throws Exception {
4855
CountDownLatch latch = this.context.getBean(Config.class).latch;
@@ -61,7 +68,6 @@ public static class Config {
6168
@Bean
6269
public ShutdownEndpoint endpoint() {
6370
ShutdownEndpoint endpoint = new ShutdownEndpoint();
64-
endpoint.setEnabled(true);
6571
return endpoint;
6672
}
6773

spring-boot-docs/src/main/asciidoc/production-ready-features.adoc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,16 @@ of the `beans` endpoint and also enables `shutdown`.
135135
NOTE: The prefix ‟`endpoints` + `.` + `name`” is used to uniquely identify the endpoint
136136
that is being configured.
137137

138+
By default, all endpoints except for `shutdown` are enabled. If you prefer to
139+
specifically "`opt-in`" endpoint enablement you can use the `endpoints.enabled` property.
140+
For example, the following will disable _all_ endpoints except for `info`:
141+
142+
[source,properties,indent=0]
143+
----
144+
endpoints.enabled=false
145+
endpoints.info.enabled=true
146+
----
147+
138148

139149

140150
[[production-ready-health]]
@@ -394,6 +404,10 @@ in your `application.properties`:
394404
management.security.role=SUPERUSER
395405
----
396406

407+
TIP: If you don't use Spring Security and your HTTP endpoints are exposed publicly,
408+
you should carefully consider which endpoints you enable. See
409+
<<production-ready-customizing-endpoints>> for details of how you can set
410+
`endpoints.enabled` to `false` then "`opt-in`" only specific endpoints.
397411

398412

399413
[[production-ready-customizing-management-server-context-path]]

0 commit comments

Comments
 (0)