From 8c642bec748b96f6dc21ad120acd2de4498d7871 Mon Sep 17 00:00:00 2001 From: Phillip Webb Date: Mon, 9 Nov 2015 16:46:01 -0800 Subject: [PATCH] Support global endpoint.sensitive override Add support for an `endpoint.sensitive` property that can be used to override the endpoint `sensitive` default. Fixes gh-4419 --- .../EndpointAutoConfiguration.java | 3 + .../actuate/endpoint/AbstractEndpoint.java | 26 ++--- .../actuate/endpoint/EndpointProperties.java | 100 ++++++++++++++++++ .../actuate/endpoint/mvc/DocsMvcEndpoint.java | 19 +++- .../endpoint/mvc/HalJsonMvcEndpoint.java | 19 +++- .../endpoint/mvc/JolokiaMvcEndpoint.java | 18 +++- .../endpoint/mvc/LogFileMvcEndpoint.java | 7 +- ...itional-spring-configuration-metadata.json | 6 -- .../endpoint/AbstractEndpointTests.java | 33 ++++++ .../appendix-application-properties.adoc | 3 +- .../asciidoc/production-ready-features.adoc | 14 ++- 11 files changed, 209 insertions(+), 39 deletions(-) create mode 100644 spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/EndpointProperties.java diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointAutoConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointAutoConfiguration.java index 2fa8a3285c..919586a2ab 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointAutoConfiguration.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointAutoConfiguration.java @@ -35,6 +35,7 @@ import org.springframework.boot.actuate.endpoint.BeansEndpoint; import org.springframework.boot.actuate.endpoint.ConfigurationPropertiesReportEndpoint; import org.springframework.boot.actuate.endpoint.DumpEndpoint; import org.springframework.boot.actuate.endpoint.Endpoint; +import org.springframework.boot.actuate.endpoint.EndpointProperties; import org.springframework.boot.actuate.endpoint.EnvironmentEndpoint; import org.springframework.boot.actuate.endpoint.FlywayEndpoint; import org.springframework.boot.actuate.endpoint.HealthEndpoint; @@ -60,6 +61,7 @@ import org.springframework.boot.autoconfigure.condition.SearchStrategy; import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration; import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration; import org.springframework.boot.bind.PropertiesConfigurationFactory; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.AnnotationAwareOrderComparator; @@ -82,6 +84,7 @@ import org.springframework.web.servlet.handler.AbstractHandlerMethodMapping; */ @Configuration @AutoConfigureAfter({ FlywayAutoConfiguration.class, LiquibaseAutoConfiguration.class }) +@EnableConfigurationProperties(EndpointProperties.class) public class EndpointAutoConfiguration { @Autowired diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/AbstractEndpoint.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/AbstractEndpoint.java index eb3d880a33..9e807fd2b2 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/AbstractEndpoint.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/AbstractEndpoint.java @@ -31,8 +31,6 @@ import org.springframework.core.env.Environment; */ public abstract class AbstractEndpoint implements Endpoint, EnvironmentAware { - private static final String ENDPOINTS_ENABLED_PROPERTY = "endpoints.enabled"; - private Environment environment; /** @@ -43,10 +41,12 @@ public abstract class AbstractEndpoint implements Endpoint, EnvironmentAwa @Pattern(regexp = "\\w+", message = "ID must only contains letters, numbers and '_'") private String id; + private final boolean sensitiveDefaut; + /** * Mark if the endpoint exposes sensitive information. */ - private boolean sensitive; + private Boolean sensitive; /** * Enable the endpoint. @@ -66,11 +66,11 @@ public abstract class AbstractEndpoint implements Endpoint, EnvironmentAwa * 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 + * @param sensitive if the endpoint is sensitive by default */ public AbstractEndpoint(String id, boolean sensitive) { this.id = id; - this.sensitive = sensitive; + this.sensitiveDefaut = sensitive; } /** @@ -81,7 +81,7 @@ public abstract class AbstractEndpoint implements Endpoint, EnvironmentAwa */ public AbstractEndpoint(String id, boolean sensitive, boolean enabled) { this.id = id; - this.sensitive = sensitive; + this.sensitiveDefaut = sensitive; this.enabled = enabled; } @@ -105,14 +105,7 @@ public abstract class AbstractEndpoint implements Endpoint, EnvironmentAwa @Override public boolean isEnabled() { - if (this.enabled != null) { - return this.enabled; - } - if (this.environment != null) { - return this.environment.getProperty(ENDPOINTS_ENABLED_PROPERTY, Boolean.class, - true); - } - return true; + return EndpointProperties.isEnabled(this.environment, this.enabled); } public void setEnabled(Boolean enabled) { @@ -121,10 +114,11 @@ public abstract class AbstractEndpoint implements Endpoint, EnvironmentAwa @Override public boolean isSensitive() { - return this.sensitive; + return EndpointProperties.isSensitive(this.environment, this.sensitive, + this.sensitiveDefaut); } - public void setSensitive(boolean sensitive) { + public void setSensitive(Boolean sensitive) { this.sensitive = sensitive; } diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/EndpointProperties.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/EndpointProperties.java new file mode 100644 index 0000000000..8a162de24f --- /dev/null +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/EndpointProperties.java @@ -0,0 +1,100 @@ +/* + * Copyright 2012-2015 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.boot.actuate.endpoint; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.core.env.Environment; + +/** + * Global endpoint properties. + * + * @author Phillip Webb + * @since 1.3.0 + */ +@ConfigurationProperties(prefix = "endpoints") +public class EndpointProperties { + + private static final String ENDPOINTS_ENABLED_PROPERTY = "endpoints.enabled"; + + private static final String ENDPOINTS_SENSITIVE_PROPERTY = "endpoints.sensitive"; + + /** + * Enable endpoints. + */ + private Boolean enabled = true; + + /** + * Default endpoint sensitive setting. + */ + private Boolean sensitive; + + public Boolean getEnabled() { + return this.enabled; + } + + public void setEnabled(Boolean enabled) { + this.enabled = enabled; + } + + public Boolean getSensitive() { + return this.sensitive; + } + + public void setSensitive(Boolean sensitive) { + this.sensitive = sensitive; + } + + /** + * Determine if an endpoint is enabled based on its specific property and taking into + * account the global default. + * @param environment the Spring environment or {@code null}. + * @param enabled the endpoint property or {@code null} + * @return if the endpoint is enabled + */ + public static boolean isEnabled(Environment environment, Boolean enabled) { + if (enabled != null) { + return enabled; + } + if (environment != null + && environment.containsProperty(ENDPOINTS_ENABLED_PROPERTY)) { + return environment.getProperty(ENDPOINTS_ENABLED_PROPERTY, Boolean.class); + } + return true; + } + + /** + * Determine if an endpoint is sensitive based on its specific property and taking + * into account the global default. + * @param environment the Spring environment or {@code null}. + * @param sensitive the endpoint property or {@code null} + * @param sensitiveDefaut the default setting to use if no environment property is + * defined + * @return if the endpoint is sensitive + */ + public static boolean isSensitive(Environment environment, Boolean sensitive, + boolean sensitiveDefaut) { + if (sensitive != null) { + return sensitive; + } + if (environment != null + && environment.containsProperty(ENDPOINTS_SENSITIVE_PROPERTY)) { + return environment.getProperty(ENDPOINTS_SENSITIVE_PROPERTY, Boolean.class); + } + return sensitiveDefaut; + } + +} diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/DocsMvcEndpoint.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/DocsMvcEndpoint.java index 2e81b96b5d..ff4d463940 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/DocsMvcEndpoint.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/DocsMvcEndpoint.java @@ -17,7 +17,10 @@ package org.springframework.boot.actuate.endpoint.mvc; import org.springframework.boot.actuate.endpoint.Endpoint; +import org.springframework.boot.actuate.endpoint.EndpointProperties; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.EnvironmentAware; +import org.springframework.core.env.Environment; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; @@ -30,10 +33,13 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter * @since 1.3.0 */ @ConfigurationProperties("endpoints.docs") -public class DocsMvcEndpoint extends WebMvcConfigurerAdapter implements MvcEndpoint { +public class DocsMvcEndpoint extends WebMvcConfigurerAdapter + implements MvcEndpoint, EnvironmentAware { private static final String DOCS_LOCATION = "classpath:/META-INF/resources/spring-boot-actuator/docs/"; + private Environment environment; + private String path = "/docs"; /** @@ -44,12 +50,17 @@ public class DocsMvcEndpoint extends WebMvcConfigurerAdapter implements MvcEndpo /** * Mark if the endpoint exposes sensitive information. */ - private boolean sensitive; + private Boolean sensitive; private final ManagementServletContext managementServletContext; private Curies curies = new Curies(); + @Override + public void setEnvironment(Environment environment) { + this.environment = environment; + } + public Curies getCuries() { return this.curies; } @@ -96,10 +107,10 @@ public class DocsMvcEndpoint extends WebMvcConfigurerAdapter implements MvcEndpo @Override public boolean isSensitive() { - return this.sensitive; + return EndpointProperties.isSensitive(this.environment, this.sensitive, false); } - public void setSensitive(boolean sensitive) { + public void setSensitive(Boolean sensitive) { this.sensitive = sensitive; } diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/HalJsonMvcEndpoint.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/HalJsonMvcEndpoint.java index ef9efa1741..15964d73b9 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/HalJsonMvcEndpoint.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/HalJsonMvcEndpoint.java @@ -20,7 +20,10 @@ import javax.validation.constraints.NotNull; import javax.validation.constraints.Pattern; import org.springframework.boot.actuate.endpoint.Endpoint; +import org.springframework.boot.actuate.endpoint.EndpointProperties; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.EnvironmentAware; +import org.springframework.core.env.Environment; import org.springframework.hateoas.ResourceSupport; import org.springframework.http.MediaType; import org.springframework.util.StringUtils; @@ -37,7 +40,10 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter * @since 1.3.0 */ @ConfigurationProperties("endpoints.actuator") -public class HalJsonMvcEndpoint extends WebMvcConfigurerAdapter implements MvcEndpoint { +public class HalJsonMvcEndpoint extends WebMvcConfigurerAdapter + implements MvcEndpoint, EnvironmentAware { + + private Environment environment; /** * Endpoint URL path. @@ -54,7 +60,7 @@ public class HalJsonMvcEndpoint extends WebMvcConfigurerAdapter implements MvcEn /** * Mark if the endpoint exposes sensitive information. */ - private boolean sensitive = false; + private Boolean sensitive; private final ManagementServletContext managementServletContext; @@ -63,6 +69,11 @@ public class HalJsonMvcEndpoint extends WebMvcConfigurerAdapter implements MvcEn this.path = getDefaultPath(managementServletContext); } + @Override + public void setEnvironment(Environment environment) { + this.environment = environment; + } + private String getDefaultPath(ManagementServletContext managementServletContext) { if (StringUtils.hasText(managementServletContext.getContextPath())) { return ""; @@ -95,10 +106,10 @@ public class HalJsonMvcEndpoint extends WebMvcConfigurerAdapter implements MvcEn @Override public boolean isSensitive() { - return this.sensitive; + return EndpointProperties.isSensitive(this.environment, this.sensitive, false); } - public void setSensitive(boolean sensitive) { + public void setSensitive(Boolean sensitive) { this.sensitive = sensitive; } diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/JolokiaMvcEndpoint.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/JolokiaMvcEndpoint.java index 653f6ddbed..180387d9fd 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/JolokiaMvcEndpoint.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/JolokiaMvcEndpoint.java @@ -30,9 +30,12 @@ import org.jolokia.http.AgentServlet; import org.springframework.beans.BeansException; import org.springframework.beans.factory.InitializingBean; import org.springframework.boot.actuate.endpoint.Endpoint; +import org.springframework.boot.actuate.endpoint.EndpointProperties; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; +import org.springframework.context.EnvironmentAware; +import org.springframework.core.env.Environment; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.context.ServletContextAware; import org.springframework.web.servlet.ModelAndView; @@ -48,7 +51,9 @@ import org.springframework.web.util.UrlPathHelper; @ConfigurationProperties(prefix = "endpoints.jolokia", ignoreUnknownFields = false) @HypermediaDisabled public class JolokiaMvcEndpoint implements MvcEndpoint, InitializingBean, - ApplicationContextAware, ServletContextAware { + ApplicationContextAware, ServletContextAware, EnvironmentAware { + + private Environment environment; /** * Endpoint URL path. @@ -65,7 +70,7 @@ public class JolokiaMvcEndpoint implements MvcEndpoint, InitializingBean, /** * Mark if the endpoint exposes sensitive information. */ - private boolean sensitive = true; + private Boolean sensitive; private final ServletWrappingController controller = new ServletWrappingController(); @@ -94,6 +99,11 @@ public class JolokiaMvcEndpoint implements MvcEndpoint, InitializingBean, this.controller.setApplicationContext(context); } + @Override + public void setEnvironment(Environment environment) { + this.environment = environment; + } + public boolean isEnabled() { return this.enabled; } @@ -104,10 +114,10 @@ public class JolokiaMvcEndpoint implements MvcEndpoint, InitializingBean, @Override public boolean isSensitive() { - return this.sensitive; + return EndpointProperties.isSensitive(this.environment, this.sensitive, true); } - public void setSensitive(boolean sensitive) { + public void setSensitive(Boolean sensitive) { this.sensitive = sensitive; } diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/LogFileMvcEndpoint.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/LogFileMvcEndpoint.java index 8535cb498a..d5aa8930ea 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/LogFileMvcEndpoint.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/mvc/LogFileMvcEndpoint.java @@ -25,6 +25,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.boot.actuate.endpoint.Endpoint; +import org.springframework.boot.actuate.endpoint.EndpointProperties; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.logging.LogFile; import org.springframework.context.EnvironmentAware; @@ -67,7 +68,7 @@ public class LogFileMvcEndpoint implements MvcEndpoint, EnvironmentAware { /** * Mark if the endpoint exposes sensitive information. */ - private boolean sensitive = true; + private Boolean sensitive; private Environment environment; @@ -95,10 +96,10 @@ public class LogFileMvcEndpoint implements MvcEndpoint, EnvironmentAware { @Override public boolean isSensitive() { - return this.sensitive; + return EndpointProperties.isSensitive(this.environment, this.sensitive, true); } - public void setSensitive(boolean sensitive) { + public void setSensitive(Boolean sensitive) { this.sensitive = sensitive; } diff --git a/spring-boot-actuator/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-boot-actuator/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 26db5bdd58..fe99ec9116 100644 --- a/spring-boot-actuator/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-boot-actuator/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -1,10 +1,4 @@ {"properties": [ - { - "name": "endpoints.enabled", - "type": "java.lang.Boolean", - "description": "Enable endpoints.", - "defaultValue": true - }, { "name": "endpoints.configprops.keys-to-sanitize", "type": "java.lang.String[]", diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/AbstractEndpointTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/AbstractEndpointTests.java index facd376285..b65ac25bfd 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/AbstractEndpointTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/AbstractEndpointTests.java @@ -108,6 +108,19 @@ public abstract class AbstractEndpointTests> { assertThat(getEndpointBean().isSensitive(), equalTo(!this.sensitive)); } + @Test + public void isSensitiveOverrideWithGlobal() throws Exception { + this.context = new AnnotationConfigApplicationContext(); + Map properties = new HashMap(); + properties.put("endpoint.sensitive", this.sensitive); + properties.put(this.property + ".sensitive", String.valueOf(!this.sensitive)); + PropertySource propertySource = new MapPropertySource("test", properties); + this.context.getEnvironment().getPropertySources().addFirst(propertySource); + this.context.register(this.configClass); + this.context.refresh(); + assertThat(getEndpointBean().isSensitive(), equalTo(!this.sensitive)); + } + @Test public void isEnabledByDefault() throws Exception { assertThat(getEndpointBean().isEnabled(), equalTo(true)); @@ -169,6 +182,26 @@ public abstract class AbstractEndpointTests> { } } + @Test + public void isAllEndpointsSensitive() throws Exception { + testGlobalEndpointsSensitive(true); + } + + @Test + public void isAllEndpointsNotSensitive() throws Exception { + testGlobalEndpointsSensitive(false); + } + + private void testGlobalEndpointsSensitive(boolean sensitive) { + this.context = new AnnotationConfigApplicationContext(); + PropertySource propertySource = new MapPropertySource("test", Collections + .singletonMap("endpoints.sensitive", sensitive)); + this.context.getEnvironment().getPropertySources().addFirst(propertySource); + this.context.register(this.configClass); + this.context.refresh(); + assertThat(getEndpointBean().isSensitive(), equalTo(sensitive)); + } + @SuppressWarnings("unchecked") protected T getEndpointBean() { return (T) this.context.getBean(this.type); diff --git a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc index 74e9732f94..97c86fe747 100644 --- a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc +++ b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc @@ -724,6 +724,8 @@ content into your application; rather pick only the properties that you need. # ---------------------------------------- # ENDPOINTS ({sc-spring-boot-actuator}/endpoint/AbstractEndpoint.{sc-ext}[AbstractEndpoint] subclasses) + endpoints.enabled=true # Enable endpoints. + endpoints.sensitive= # Default endpoint sensitive setting. endpoints.actuator.enabled=true # Enable the endpoint. endpoints.actuator.path= # Endpoint URL path. endpoints.actuator.sensitive=false # Enable security on the endpoint. @@ -744,7 +746,6 @@ content into your application; rather pick only the properties that you need. endpoints.dump.enabled= # Enable the endpoint. endpoints.dump.id= # Endpoint identifier. endpoints.dump.sensitive= # Mark if the endpoint exposes sensitive information. - endpoints.enabled=true # Enable endpoints. endpoints.env.enabled= # Enable the endpoint. endpoints.env.id= # Endpoint identifier. endpoints.env.keys-to-sanitize=password,secret,key,.*credentials.*,vcap_services # Keys that should be sanitized. Keys can be simple strings that the property ends with or regex expressions. diff --git a/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc b/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc index 945c2306c2..12d887b3c8 100644 --- a/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc +++ b/spring-boot-docs/src/main/asciidoc/production-ready-features.adoc @@ -65,7 +65,7 @@ The following endpoints are available: [cols="2,5,1"] |=== -| ID | Description | Sensitive +| ID | Description | Sensitive Default |`actuator` |Provides a hypermedia-based "`discovery page`" for the other endpoints. Requires Spring @@ -166,6 +166,18 @@ For example, the following will disable _all_ endpoints except for `info`: endpoints.info.enabled=true ---- +Likewise, you can also choose to globally set the "`sensitive`" flag of all endpoints. By +default, the sensitive flag depends on the type of endpoint (see the table above). +For example, to mark _all_ endpoints as sensitive except `info`: + +[source,properties,indent=0] +---- + endpoints.sensitive=true + endpoints.info.sensitive=false +---- + + + [[production-ready-endpoint-hypermedia]] === Hypermedia for actuator MVC endpoints If http://projects.spring.io/spring-hateoas[Spring HATEOAS] is on the classpath (e.g.