Move remaining integration tests out of actuator-autoconfigure-all

See gh-46071
This commit is contained in:
Andy Wilkinson 2025-06-09 13:37:35 +01:00
parent 9860168d3e
commit 76719a4e28
32 changed files with 251 additions and 496 deletions

View File

@ -59,7 +59,6 @@ include "spring-boot-project:spring-boot"
include "spring-boot-project:spring-boot-activemq"
include "spring-boot-project:spring-boot-actuator"
include "spring-boot-project:spring-boot-actuator-autoconfigure"
include "spring-boot-project:spring-boot-actuator-autoconfigure-all"
include "spring-boot-project:spring-boot-actuator-docs"
include "spring-boot-project:spring-boot-actuator-integration-tests"
include "spring-boot-project:spring-boot-amqp"

View File

@ -1,66 +0,0 @@
/*
* Copyright 2012-present 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
*
* https://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.
*/
plugins {
id "java-library"
id "org.springframework.boot.deployed"
id "org.springframework.boot.optional-dependencies"
}
description = "Spring Boot Actuator AutoConfigure All"
configurations.all {
resolutionStrategy.eachDependency {
if (it.requested.group == 'org.opensaml') {
it.useVersion '4.0.1'
}
}
}
dependencies {
api(project(":spring-boot-project:spring-boot-actuator-autoconfigure"))
api(project(":spring-boot-project:spring-boot-autoconfigure"))
optional(project(":spring-boot-project:spring-boot-jersey"))
optional(project(":spring-boot-project:spring-boot-security"))
optional(project(":spring-boot-project:spring-boot-security-oauth2-client"))
optional(project(":spring-boot-project:spring-boot-security-oauth2-resource-server"))
optional(project(":spring-boot-project:spring-boot-security-saml2"))
optional(project(":spring-boot-project:spring-boot-web-server"))
optional(project(":spring-boot-project:spring-boot-webmvc"))
optional("io.projectreactor:reactor-core")
optional("jakarta.servlet:jakarta.servlet-api")
testImplementation(project(":spring-boot-project:spring-boot-data-cassandra"))
testImplementation(project(":spring-boot-project:spring-boot-data-rest"))
testImplementation(project(":spring-boot-project:spring-boot-hateoas"))
testImplementation(project(":spring-boot-project:spring-boot-http-codec"))
testImplementation(project(":spring-boot-project:spring-boot-http-converter"))
testImplementation(project(":spring-boot-project:spring-boot-jackson"))
testImplementation(project(":spring-boot-project:spring-boot-reactor-netty"))
testImplementation(project(":spring-boot-project:spring-boot-test"))
testImplementation(project(":spring-boot-project:spring-boot-tomcat"))
testImplementation(project(":spring-boot-project:spring-boot-tools:spring-boot-test-support"))
testImplementation(project(":spring-boot-project:spring-boot-webflux"))
testImplementation("org.springframework.security:spring-security-test")
testRuntimeOnly("ch.qos.logback:logback-classic")
}
tasks.named("test") {
jvmArgs += "--add-opens=java.base/java.net=ALL-UNNAMED"
}

View File

@ -1,61 +0,0 @@
/*
* Copyright 2012-present 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
*
* https://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.autoconfigure.integrationtest;
import java.util.ArrayList;
import java.util.List;
import org.springframework.boot.actuate.autoconfigure.audit.AuditEventsEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.beans.BeansEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.condition.ConditionsReportEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.context.ShutdownEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.context.properties.ConfigurationPropertiesReportEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.env.EnvironmentEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.info.InfoEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.management.ThreadDumpEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.web.exchanges.HttpExchangesEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.web.mappings.MappingsEndpointAutoConfiguration;
import org.springframework.util.ClassUtils;
/**
* A list of all endpoint auto-configuration classes for use in tests.
*/
final class EndpointAutoConfigurationClasses {
static final Class<?>[] ALL;
static {
List<Class<?>> all = new ArrayList<>();
all.add(AuditEventsEndpointAutoConfiguration.class);
all.add(BeansEndpointAutoConfiguration.class);
all.add(ConditionsReportEndpointAutoConfiguration.class);
all.add(ConfigurationPropertiesReportEndpointAutoConfiguration.class);
all.add(ShutdownEndpointAutoConfiguration.class);
all.add(EnvironmentEndpointAutoConfiguration.class);
all.add(HealthEndpointAutoConfiguration.class);
all.add(InfoEndpointAutoConfiguration.class);
all.add(ThreadDumpEndpointAutoConfiguration.class);
all.add(HttpExchangesEndpointAutoConfiguration.class);
all.add(MappingsEndpointAutoConfiguration.class);
ALL = ClassUtils.toClassArray(all);
}
private EndpointAutoConfigurationClasses() {
}
}

View File

@ -1,80 +0,0 @@
/*
* Copyright 2012-present 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
*
* https://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.autoconfigure.integrationtest;
import java.io.IOException;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer;
import org.springframework.boot.actuate.endpoint.jackson.EndpointObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
/**
* {@link Configuration @Configuration} that creates an {@link EndpointObjectMapper} that
* reverses all strings.
*
* @author Phillip Webb
*/
@Configuration
@SuppressWarnings("removal")
class EndpointObjectMapperConfiguration {
@Bean
EndpointObjectMapper endpointObjectMapper() {
SimpleModule module = new SimpleModule();
module.addSerializer(String.class, new ReverseStringSerializer());
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().modules(module).build();
return () -> objectMapper;
}
static class ReverseStringSerializer extends StdScalarSerializer<Object> {
ReverseStringSerializer() {
super(String.class, false);
}
@Override
public boolean isEmpty(SerializerProvider prov, Object value) {
return ((String) value).isEmpty();
}
@Override
public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException {
serialize(value, gen);
}
@Override
public final void serializeWithType(Object value, JsonGenerator gen, SerializerProvider provider,
TypeSerializer typeSer) throws IOException {
serialize(value, gen);
}
private void serialize(Object value, JsonGenerator gen) throws IOException {
StringBuilder builder = new StringBuilder((String) value);
gen.writeString(builder.reverse().toString());
}
}
}

View File

@ -40,6 +40,7 @@ dependencies {
optional("jakarta.servlet:jakarta.servlet-api")
optional("org.springframework.security:spring-security-config")
testFixturesImplementation(project(":spring-boot-project:spring-boot-test"))
testFixturesImplementation(project(":spring-boot-project:spring-boot-tools:spring-boot-test-support"))
testImplementation(project(":spring-boot-project:spring-boot-test"))

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.integrationtest;
package org.springframework.boot.actuate.autoconfigure.endpoint.jmx;
import javax.management.MBeanOperationInfo;
import javax.management.MBeanServer;
@ -23,8 +23,8 @@ import javax.management.ObjectName;
import org.junit.jupiter.api.Test;
import org.springframework.boot.actuate.autoconfigure.beans.BeansEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.jmx.JmxEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
import org.springframework.boot.actuate.endpoint.annotation.DeleteOperation;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
@ -49,7 +49,7 @@ class JmxEndpointAccessIntegrationTests {
JmxEndpointAutoConfiguration.class, HealthContributorAutoConfiguration.class))
.withUserConfiguration(CustomJmxEndpoint.class)
.withPropertyValues("spring.jmx.enabled=true")
.withConfiguration(AutoConfigurations.of(EndpointAutoConfigurationClasses.ALL));
.withConfiguration(AutoConfigurations.of(BeansEndpointAutoConfiguration.class));
@Test
void accessIsUnrestrictedByDefault() {

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.integrationtest;
package org.springframework.boot.actuate.autoconfigure.endpoint.jmx;
import javax.management.InstanceNotFoundException;
import javax.management.IntrospectionException;
@ -28,9 +28,11 @@ import org.junit.jupiter.api.Test;
import org.springframework.boot.LazyInitializationBeanFactoryPostProcessor;
import org.springframework.boot.actuate.audit.InMemoryAuditEventRepository;
import org.springframework.boot.actuate.autoconfigure.beans.BeansEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.context.ShutdownEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.jmx.JmxEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
import org.springframework.boot.actuate.web.exchanges.InMemoryHttpExchangeRepository;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration;
@ -54,14 +56,14 @@ class JmxEndpointIntegrationTests {
JmxEndpointAutoConfiguration.class, HealthContributorAutoConfiguration.class))
.withUserConfiguration(HttpExchangeRepositoryConfiguration.class, AuditEventRepositoryConfiguration.class)
.withPropertyValues("spring.jmx.enabled=true")
.withConfiguration(AutoConfigurations.of(EndpointAutoConfigurationClasses.ALL));
.withConfiguration(AutoConfigurations.of(HealthEndpointAutoConfiguration.class,
BeansEndpointAutoConfiguration.class, ShutdownEndpointAutoConfiguration.class));
@Test
void jmxEndpointsExposeHealthByDefault() {
this.contextRunner.run((context) -> {
MBeanServer mBeanServer = context.getBean(MBeanServer.class);
checkEndpointMBeans(mBeanServer, new String[] { "health" }, new String[] { "beans", "conditions",
"configprops", "env", "info", "mappings", "threaddump", "httpexchanges", "shutdown" });
checkEndpointMBeans(mBeanServer, new String[] { "health" }, new String[] { "beans", "shutdown" });
});
}
@ -71,8 +73,7 @@ class JmxEndpointIntegrationTests {
.withBean(LazyInitializationBeanFactoryPostProcessor.class, LazyInitializationBeanFactoryPostProcessor::new)
.run((context) -> {
MBeanServer mBeanServer = context.getBean(MBeanServer.class);
checkEndpointMBeans(mBeanServer, new String[] { "beans", "conditions", "configprops", "env", "health",
"info", "mappings", "threaddump", "httpexchanges" }, new String[] { "shutdown" });
checkEndpointMBeans(mBeanServer, new String[] { "beans", "health" }, new String[] { "shutdown" });
});
}
@ -80,8 +81,7 @@ class JmxEndpointIntegrationTests {
void jmxEndpointsCanBeExcluded() {
this.contextRunner.withPropertyValues("management.endpoints.jmx.exposure.exclude:*").run((context) -> {
MBeanServer mBeanServer = context.getBean(MBeanServer.class);
checkEndpointMBeans(mBeanServer, new String[0], new String[] { "beans", "conditions", "configprops", "env",
"health", "mappings", "shutdown", "threaddump", "httpexchanges" });
checkEndpointMBeans(mBeanServer, new String[0], new String[] { "beans", "health", "shutdown" });
});
}
@ -90,8 +90,7 @@ class JmxEndpointIntegrationTests {
void singleJmxEndpointCanBeExposed() {
this.contextRunner.withPropertyValues("management.endpoints.jmx.exposure.include=beans").run((context) -> {
MBeanServer mBeanServer = context.getBean(MBeanServer.class);
checkEndpointMBeans(mBeanServer, new String[] { "beans" }, new String[] { "conditions", "configprops",
"env", "health", "mappings", "shutdown", "threaddump", "httpexchanges" });
checkEndpointMBeans(mBeanServer, new String[] { "beans" }, new String[] { "health", "shutdown" });
});
}

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.integrationtest;
package org.springframework.boot.actuate.autoconfigure.endpoint.web;
import org.junit.jupiter.api.Test;
@ -22,13 +22,9 @@ import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.actuate.health.HealthEndpointWebExtension;
import org.springframework.boot.actuate.health.ReactiveHealthEndpointWebExtension;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.cassandra.autoconfigure.CassandraAutoConfiguration;
import org.springframework.boot.context.annotation.UserConfigurations;
import org.springframework.boot.data.cassandra.autoconfigure.CassandraDataAutoConfiguration;
import org.springframework.boot.data.rest.autoconfigure.RepositoryRestMvcAutoConfiguration;
import org.springframework.boot.test.context.runner.ReactiveWebApplicationContextRunner;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.boot.testsupport.classpath.ClassPathExclusions;
import static org.assertj.core.api.Assertions.assertThat;
@ -46,7 +42,6 @@ class WebEndpointsAutoConfigurationIntegrationTests {
}
@Test
@ClassPathExclusions({ "spring-security-oauth2-client-*.jar", "spring-security-oauth2-resource-server-*.jar" })
void healthEndpointReactiveWebExtensionIsAutoConfigured() {
reactiveWebRunner()
.run((context) -> assertThat(context).hasSingleBean(ReactiveHealthEndpointWebExtension.class));
@ -64,8 +59,7 @@ class WebEndpointsAutoConfigurationIntegrationTests {
.withPropertyValues("management.defaults.metrics.export.enabled=false");
}
@EnableAutoConfiguration(exclude = { CassandraAutoConfiguration.class, CassandraDataAutoConfiguration.class,
RepositoryRestMvcAutoConfiguration.class })
@EnableAutoConfiguration
@SpringBootConfiguration
static class WebEndpointTestApplication {

View File

@ -37,11 +37,11 @@ import static org.assertj.core.api.Assertions.assertThatNoException;
* @param <A> the assertions
* @author Madhura Bhave
*/
abstract class AbstractHealthEndpointAdditionalPathIntegrationTests<T extends AbstractApplicationContextRunner<T, C, A>, C extends ConfigurableApplicationContext, A extends ApplicationContextAssertProvider<C>> {
public abstract class AbstractHealthEndpointAdditionalPathIntegrationTests<T extends AbstractApplicationContextRunner<T, C, A>, C extends ConfigurableApplicationContext, A extends ApplicationContextAssertProvider<C>> {
private final T runner;
AbstractHealthEndpointAdditionalPathIntegrationTests(T runner) {
protected AbstractHealthEndpointAdditionalPathIntegrationTests(T runner) {
this.runner = runner;
}

View File

@ -23,7 +23,7 @@ plugins {
description = "Spring Boot Actuator Docs"
dependencies {
testImplementation(project(":spring-boot-project:spring-boot-actuator-autoconfigure-all"))
testImplementation(project(":spring-boot-project:spring-boot-actuator-autoconfigure"))
testImplementation(project(":spring-boot-project:spring-boot-cache"))
testImplementation(project(":spring-boot-project:spring-boot-flyway"))
testImplementation(project(":spring-boot-project:spring-boot-http-converter"))

View File

@ -1986,7 +1986,6 @@ bom {
"spring-boot-activemq",
"spring-boot-actuator",
"spring-boot-actuator-autoconfigure",
"spring-boot-actuator-autoconfigure-all",
"spring-boot-amqp",
"spring-boot-artemis",
"spring-boot-autoconfigure",

View File

@ -374,7 +374,7 @@ dependencies {
springApplicationExample(platform(project(":spring-boot-project:spring-boot-dependencies")))
springApplicationExample(project(path: ":spring-boot-project:spring-boot-starters:spring-boot-starter-web"))
testImplementation(project(":spring-boot-project:spring-boot-actuator-autoconfigure-all"))
testImplementation(project(":spring-boot-project:spring-boot-actuator-autoconfigure"))
testImplementation(project(":spring-boot-project:spring-boot-tools:spring-boot-test-support"))
testRuntimeOnly(project(":spring-boot-project:spring-boot-starters:spring-boot-starter-web"))

View File

@ -55,6 +55,7 @@ dependencies {
testImplementation(project(":spring-boot-project:spring-boot-web-server-test"))
testImplementation(testFixtures(project(":spring-boot-project:spring-boot-actuator-autoconfigure")))
testImplementation("jakarta.servlet:jakarta.servlet-api")
testImplementation("org.springframework:spring-webflux")
testRuntimeOnly("ch.qos.logback:logback-classic")
}

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.integrationtest;
package org.springframework.boot.jersey.actuate.autoconfigure;
import java.io.IOException;
import java.time.Duration;
@ -33,7 +33,6 @@ import org.springframework.boot.actuate.autoconfigure.web.server.ManagementConte
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.jackson.autoconfigure.JacksonAutoConfiguration;
import org.springframework.boot.jersey.autoconfigure.JerseyAutoConfiguration;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.assertj.AssertableWebApplicationContext;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.boot.tomcat.autoconfigure.servlet.TomcatServletWebServerAutoConfiguration;
@ -44,7 +43,6 @@ import org.springframework.http.HttpStatus;
import org.springframework.test.web.reactive.server.EntityExchangeResult;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.web.reactive.function.client.ExchangeStrategies;
import org.springframework.web.servlet.DispatcherServlet;
import static org.assertj.core.api.Assertions.assertThat;
@ -61,7 +59,6 @@ class JerseyEndpointAccessIntegrationTests {
EndpointAutoConfiguration.class, TomcatServletWebServerAutoConfiguration.class,
WebEndpointAutoConfiguration.class, ManagementContextAutoConfiguration.class,
BeansEndpointAutoConfiguration.class))
.withClassLoader(new FilteredClassLoader(DispatcherServlet.class))
.withUserConfiguration(CustomServletEndpoint.class)
.withPropertyValues("server.port:0");

View File

@ -14,14 +14,21 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.integrationtest;
package org.springframework.boot.jersey.actuate.autoconfigure;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer;
import org.glassfish.jersey.server.ResourceConfig;
import org.junit.jupiter.api.Test;
@ -29,19 +36,16 @@ import org.springframework.boot.actuate.autoconfigure.beans.BeansEndpointAutoCon
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration;
import org.springframework.boot.actuate.endpoint.jackson.EndpointObjectMapper;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.jackson.autoconfigure.JacksonAutoConfiguration;
import org.springframework.boot.jersey.autoconfigure.JerseyAutoConfiguration;
import org.springframework.boot.security.actuate.autoconfigure.servlet.ManagementWebSecurityAutoConfiguration;
import org.springframework.boot.security.autoconfigure.servlet.SecurityAutoConfiguration;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.boot.tomcat.autoconfigure.servlet.TomcatServletWebServerAutoConfiguration;
import org.springframework.boot.web.server.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.web.servlet.DispatcherServlet;
import static org.assertj.core.api.Assertions.assertThat;
@ -79,23 +83,6 @@ class JerseyEndpointIntegrationTests {
testJerseyEndpoints(new Class<?>[] { EndpointsConfiguration.class });
}
@Test
void actuatorEndpointsWhenSecurityAvailable() {
WebApplicationContextRunner contextRunner = getContextRunner(
new Class<?>[] { EndpointsConfiguration.class, ResourceConfigConfiguration.class },
getAutoconfigurations(SecurityAutoConfiguration.class, ManagementWebSecurityAutoConfiguration.class));
contextRunner.run((context) -> {
int port = context.getSourceApplicationContext(AnnotationConfigServletWebServerApplicationContext.class)
.getWebServer()
.getPort();
WebTestClient client = WebTestClient.bindToServer()
.baseUrl("http://localhost:" + port)
.responseTimeout(Duration.ofMinutes(5))
.build();
client.get().uri("/actuator").exchange().expectStatus().isUnauthorized();
});
}
@Test
void endpointObjectMapperCanBeApplied() {
WebApplicationContextRunner contextRunner = getContextRunner(new Class<?>[] { EndpointsConfiguration.class,
@ -141,9 +128,7 @@ class JerseyEndpointIntegrationTests {
WebApplicationContextRunner getContextRunner(Class<?>[] userConfigurations,
Class<?>... additionalAutoConfigurations) {
FilteredClassLoader classLoader = new FilteredClassLoader(DispatcherServlet.class);
return new WebApplicationContextRunner(AnnotationConfigServletWebServerApplicationContext::new)
.withClassLoader(classLoader)
.withConfiguration(AutoConfigurations.of(getAutoconfigurations(additionalAutoConfigurations)))
.withUserConfiguration(userConfigurations)
.withPropertyValues("management.endpoints.web.exposure.include:*", "server.port:0");
@ -195,4 +180,49 @@ class JerseyEndpointIntegrationTests {
}
@Configuration
@SuppressWarnings({ "deprecation", "removal" })
static class EndpointObjectMapperConfiguration {
@Bean
EndpointObjectMapper endpointObjectMapper() {
SimpleModule module = new SimpleModule();
module.addSerializer(String.class, new ReverseStringSerializer());
ObjectMapper objectMapper = org.springframework.http.converter.json.Jackson2ObjectMapperBuilder.json()
.modules(module)
.build();
return () -> objectMapper;
}
static class ReverseStringSerializer extends StdScalarSerializer<Object> {
ReverseStringSerializer() {
super(String.class, false);
}
@Override
public boolean isEmpty(SerializerProvider prov, Object value) {
return ((String) value).isEmpty();
}
@Override
public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException {
serialize(value, gen);
}
@Override
public final void serializeWithType(Object value, JsonGenerator gen, SerializerProvider provider,
TypeSerializer typeSer) throws IOException {
serialize(value, gen);
}
private void serialize(Object value, JsonGenerator gen) throws IOException {
StringBuilder builder = new StringBuilder((String) value);
gen.writeString(builder.reverse().toString());
}
}
}
}

View File

@ -14,11 +14,12 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.integrationtest;
package org.springframework.boot.jersey.actuate.autoconfigure;
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.integrationtest.AbstractHealthEndpointAdditionalPathIntegrationTests;
import org.springframework.boot.actuate.autoconfigure.system.DiskSpaceHealthContributorAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;
@ -26,7 +27,6 @@ import org.springframework.boot.jackson.autoconfigure.JacksonAutoConfiguration;
import org.springframework.boot.jersey.actuate.autoconfigure.health.HealthEndpointJerseyExtensionAutoConfiguration;
import org.springframework.boot.jersey.autoconfigure.JerseyAutoConfiguration;
import org.springframework.boot.servlet.actuate.autoconfigure.ServletManagementContextAutoConfiguration;
import org.springframework.boot.test.context.FilteredClassLoader;
import org.springframework.boot.test.context.assertj.AssertableWebApplicationContext;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.boot.tomcat.actuate.autoconfigure.web.TomcatServletManagementContextAutoConfiguration;
@ -34,7 +34,6 @@ import org.springframework.boot.tomcat.autoconfigure.servlet.TomcatServletWebSer
import org.springframework.boot.web.server.context.ServerPortInfoApplicationContextInitializer;
import org.springframework.boot.web.server.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.web.context.ConfigurableWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
/**
* Integration tests for health groups on an additional path on Jersey.
@ -54,7 +53,6 @@ class JerseyHealthEndpointAdditionalPathIntegrationTests extends
HealthEndpointJerseyExtensionAutoConfiguration.class,
DiskSpaceHealthContributorAutoConfiguration.class))
.withInitializer(new ServerPortInfoApplicationContextInitializer())
.withClassLoader(new FilteredClassLoader(DispatcherServlet.class))
.withPropertyValues("server.port=0"));
}

View File

@ -21,7 +21,7 @@ plugins {
description = "Starter for using Spring Boot's Actuator which provides production ready features to help you monitor and manage your application"
dependencies {
api(project(":spring-boot-project:spring-boot-actuator-autoconfigure-all"))
api(project(":spring-boot-project:spring-boot-actuator-autoconfigure"))
api(project(":spring-boot-project:spring-boot-metrics"))
api(project(":spring-boot-project:spring-boot-tracing"))
api(project(":spring-boot-project:spring-boot-starters:spring-boot-starter"))

View File

@ -145,7 +145,7 @@ dependencies {
optional("io.micrometer:micrometer-tracing")
testImplementation(project(":spring-boot-project:spring-boot-actuator"))
testImplementation(project(":spring-boot-project:spring-boot-actuator-autoconfigure-all"))
testImplementation(project(":spring-boot-project:spring-boot-actuator-autoconfigure"))
testImplementation(project(":spring-boot-project:spring-boot-freemarker"))
testImplementation(project(":spring-boot-project:spring-boot-gson"))
testImplementation(project(":spring-boot-project:spring-boot-mustache"))

View File

@ -39,7 +39,6 @@ if (project.hasProperty("oldVersion") && project.hasProperty("newVersion")) {
["spring-boot",
"spring-boot-actuator",
"spring-boot-actuator-autoconfigure",
"spring-boot-actuator-autoconfigure-all",
"spring-boot-autoconfigure",
"spring-boot-devtools",
"spring-boot-test-autoconfigure"].each {

View File

@ -45,6 +45,7 @@ dependencies {
testFixturesImplementation(project(":spring-boot-project:spring-boot-jackson"))
testFixturesImplementation(project(":spring-boot-project:spring-boot-reactor-netty"))
testImplementation(project(":spring-boot-project:spring-boot-jackson"))
testImplementation(project(":spring-boot-project:spring-boot-mustache"))
testImplementation(project(":spring-boot-project:spring-boot-reactor-netty"))
testImplementation(project(":spring-boot-project:spring-boot-test"))

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.integrationtest;
package org.springframework.boot.webflux.actuate.autoconfigure.web;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
@ -28,13 +28,10 @@ import org.springframework.boot.actuate.endpoint.web.annotation.ControllerEndpoi
import org.springframework.boot.actuate.endpoint.web.annotation.RestControllerEndpoint;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.http.converter.autoconfigure.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.jackson.autoconfigure.JacksonAutoConfiguration;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.boot.web.context.reactive.AnnotationConfigReactiveWebApplicationContext;
import org.springframework.boot.webflux.autoconfigure.WebFluxAutoConfiguration;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.test.context.TestSecurityContextHolder;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.web.bind.annotation.GetMapping;
@ -51,14 +48,11 @@ class ControllerEndpointWebFluxIntegrationTests {
@AfterEach
void close() {
TestSecurityContextHolder.clearContext();
this.context.close();
}
@Test
void endpointsCanBeAccessed() {
TestSecurityContextHolder.getContext()
.setAuthentication(new TestingAuthenticationToken("user", "N/A", "ROLE_ACTUATOR"));
this.context = new AnnotationConfigReactiveWebApplicationContext();
this.context.register(DefaultConfiguration.class, ExampleController.class);
TestPropertyValues.of("management.endpoints.web.exposure.include=*").applyTo(this.context);
@ -67,8 +61,8 @@ class ControllerEndpointWebFluxIntegrationTests {
webClient.get().uri("/actuator/example").exchange().expectStatus().isOk();
}
@ImportAutoConfiguration({ JacksonAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class,
EndpointAutoConfiguration.class, WebEndpointAutoConfiguration.class, AuditAutoConfiguration.class,
@ImportAutoConfiguration({ JacksonAutoConfiguration.class, EndpointAutoConfiguration.class,
WebEndpointAutoConfiguration.class, AuditAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class, WebFluxAutoConfiguration.class,
ManagementContextAutoConfiguration.class, BeansEndpointAutoConfiguration.class })
static class DefaultConfiguration {

View File

@ -14,12 +14,13 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.integrationtest;
package org.springframework.boot.webflux.actuate.autoconfigure.web;
import java.time.Duration;
import org.junit.jupiter.api.Test;
import org.springframework.boot.actuate.autoconfigure.beans.BeansEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration;
@ -56,7 +57,7 @@ class WebFluxEndpointAccessIntegrationTests {
HttpHandlerAutoConfiguration.class, JacksonAutoConfiguration.class, CodecsAutoConfiguration.class,
WebFluxAutoConfiguration.class, EndpointAutoConfiguration.class, WebEndpointAutoConfiguration.class,
ManagementContextAutoConfiguration.class))
.withConfiguration(AutoConfigurations.of(EndpointAutoConfigurationClasses.ALL))
.withConfiguration(AutoConfigurations.of(BeansEndpointAutoConfiguration.class))
.withUserConfiguration(CustomWebFluxEndpoint.class)
.withPropertyValues("server.port:0");

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.integrationtest;
package org.springframework.boot.webflux.actuate.autoconfigure.web;
import java.util.function.Consumer;

View File

@ -14,16 +14,24 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.integrationtest;
package org.springframework.boot.webflux.actuate.autoconfigure.web;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer;
import org.junit.jupiter.api.Test;
import org.springframework.boot.actuate.autoconfigure.beans.BeansEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration;
import org.springframework.boot.actuate.endpoint.jackson.EndpointObjectMapper;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.http.codec.autoconfigure.CodecsAutoConfiguration;
import org.springframework.boot.jackson.autoconfigure.JacksonAutoConfiguration;
@ -131,4 +139,49 @@ class WebFluxEndpointIntegrationTests {
}
@Configuration
@SuppressWarnings({ "deprecation", "removal" })
static class EndpointObjectMapperConfiguration {
@Bean
EndpointObjectMapper endpointObjectMapper() {
SimpleModule module = new SimpleModule();
module.addSerializer(String.class, new ReverseStringSerializer());
ObjectMapper objectMapper = org.springframework.http.converter.json.Jackson2ObjectMapperBuilder.json()
.modules(module)
.build();
return () -> objectMapper;
}
static class ReverseStringSerializer extends StdScalarSerializer<Object> {
ReverseStringSerializer() {
super(String.class, false);
}
@Override
public boolean isEmpty(SerializerProvider prov, Object value) {
return ((String) value).isEmpty();
}
@Override
public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException {
serialize(value, gen);
}
@Override
public final void serializeWithType(Object value, JsonGenerator gen, SerializerProvider provider,
TypeSerializer typeSer) throws IOException {
serialize(value, gen);
}
private void serialize(Object value, JsonGenerator gen) throws IOException {
StringBuilder builder = new StringBuilder((String) value);
gen.writeString(builder.reverse().toString());
}
}
}
}

View File

@ -14,12 +14,13 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.integrationtest;
package org.springframework.boot.webflux.actuate.autoconfigure.web;
import org.springframework.boot.actuate.autoconfigure.beans.BeansEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.integrationtest.AbstractHealthEndpointAdditionalPathIntegrationTests;
import org.springframework.boot.actuate.autoconfigure.system.DiskSpaceHealthContributorAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;

View File

@ -51,6 +51,7 @@ dependencies {
testFixturesImplementation(project(":spring-boot-project:spring-boot-jackson"))
testImplementation(project(":spring-boot-project:spring-boot-freemarker"))
testImplementation(project(":spring-boot-project:spring-boot-jackson"))
testImplementation(project(":spring-boot-project:spring-boot-restclient"))
testImplementation(project(":spring-boot-project:spring-boot-test"))
testImplementation(project(":spring-boot-project:spring-boot-tomcat"))

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.integrationtest;
package org.springframework.boot.webmvc.actuate.autoconfigure.endpoint.web;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
@ -28,24 +28,16 @@ import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.http.converter.autoconfigure.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.jackson.autoconfigure.JacksonAutoConfiguration;
import org.springframework.boot.security.autoconfigure.servlet.SecurityAutoConfiguration;
import org.springframework.boot.servlet.actuate.autoconfigure.ServletManagementContextAutoConfiguration;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.boot.web.context.servlet.AnnotationConfigServletWebApplicationContext;
import org.springframework.boot.webmvc.autoconfigure.DispatcherServletAutoConfiguration;
import org.springframework.boot.webmvc.autoconfigure.WebMvcAutoConfiguration;
import org.springframework.context.annotation.Import;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockServletContext;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.test.context.TestSecurityContextHolder;
import org.springframework.test.web.servlet.assertj.MockMvcTester;
import org.springframework.test.web.servlet.setup.MockMvcConfigurer;
import org.springframework.web.bind.annotation.GetMapping;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
/**
* Integration tests for the Actuator's MVC
@ -61,45 +53,24 @@ class ControllerEndpointWebMvcIntegrationTests {
@AfterEach
void close() {
TestSecurityContextHolder.clearContext();
this.context.close();
}
@Test
void endpointsAreSecureByDefault() {
this.context = new AnnotationConfigServletWebApplicationContext();
this.context.register(SecureConfiguration.class, ExampleController.class);
MockMvcTester mvc = createSecureMockMvcTester();
assertThat(mvc.get().uri("/actuator/example").accept(MediaType.APPLICATION_JSON))
.hasStatus(HttpStatus.UNAUTHORIZED);
}
@Test
void endpointsCanBeAccessed() {
TestSecurityContextHolder.getContext()
.setAuthentication(new TestingAuthenticationToken("user", "N/A", "ROLE_ACTUATOR"));
this.context = new AnnotationConfigServletWebApplicationContext();
this.context.register(SecureConfiguration.class, ExampleController.class);
this.context.register(DefaultConfiguration.class, ExampleController.class);
TestPropertyValues
.of("management.endpoints.web.base-path:/management", "management.endpoints.web.exposure.include=*")
.applyTo(this.context);
MockMvcTester mvc = createSecureMockMvcTester();
MockMvcTester mvc = createMockMvcTester();
assertThat(mvc.get().uri("/management/example")).hasStatusOk();
}
private MockMvcTester createSecureMockMvcTester() {
return doCreateMockMvcTester(springSecurity());
}
private MockMvcTester doCreateMockMvcTester(MockMvcConfigurer... configurers) {
private MockMvcTester createMockMvcTester() {
this.context.setServletContext(new MockServletContext());
this.context.refresh();
return MockMvcTester.from(this.context, (builder) -> {
for (MockMvcConfigurer configurer : configurers) {
builder.apply(configurer);
}
return builder.build();
});
return MockMvcTester.from(this.context);
}
@ImportAutoConfiguration({ JacksonAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class,
@ -112,12 +83,6 @@ class ControllerEndpointWebMvcIntegrationTests {
}
@Import(DefaultConfiguration.class)
@ImportAutoConfiguration({ SecurityAutoConfiguration.class })
static class SecureConfiguration {
}
@org.springframework.boot.actuate.endpoint.web.annotation.RestControllerEndpoint(id = "example")
@SuppressWarnings("removal")
static class ExampleController {

View File

@ -14,10 +14,9 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.integrationtest;
package org.springframework.boot.webmvc.actuate.autoconfigure.endpoint.web;
import java.io.IOException;
import java.time.Duration;
import java.util.function.Supplier;
import jakarta.servlet.ServletException;
@ -26,6 +25,7 @@ import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.junit.jupiter.api.Test;
import org.springframework.boot.actuate.autoconfigure.beans.BeansEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
@ -43,11 +43,10 @@ import org.springframework.boot.webmvc.autoconfigure.DispatcherServletAutoConfig
import org.springframework.boot.webmvc.autoconfigure.WebMvcAutoConfiguration;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.test.web.reactive.server.EntityExchangeResult;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.reactive.function.client.ExchangeStrategies;
import org.springframework.web.client.RestClient;
import static org.assertj.core.api.Assertions.assertThat;
@ -65,15 +64,14 @@ class WebMvcEndpointAccessIntegrationTests {
JacksonAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class,
WebMvcAutoConfiguration.class, EndpointAutoConfiguration.class, WebEndpointAutoConfiguration.class,
ManagementContextAutoConfiguration.class, ServletManagementContextAutoConfiguration.class,
HealthContributorAutoConfiguration.class))
.withConfiguration(AutoConfigurations.of(EndpointAutoConfigurationClasses.ALL))
HealthContributorAutoConfiguration.class, BeansEndpointAutoConfiguration.class))
.withUserConfiguration(CustomMvcEndpoint.class, CustomServletEndpoint.class)
.withPropertyValues("server.port:0");
@Test
void accessIsUnrestrictedByDefault() {
this.contextRunner.withPropertyValues("management.endpoints.web.exposure.include=*").run((context) -> {
WebTestClient client = createClient(context);
RestClient client = createClient(context);
assertThat(isAccessible(client, HttpMethod.GET, "beans")).isTrue();
assertThat(isAccessible(client, HttpMethod.GET, "custommvc")).isTrue();
assertThat(isAccessible(client, HttpMethod.POST, "custommvc")).isTrue();
@ -88,7 +86,7 @@ class WebMvcEndpointAccessIntegrationTests {
.withPropertyValues("management.endpoints.web.exposure.include=*",
"management.endpoints.access.default=READ_ONLY")
.run((context) -> {
WebTestClient client = createClient(context);
RestClient client = createClient(context);
assertThat(isAccessible(client, HttpMethod.GET, "beans")).isTrue();
assertThat(isAccessible(client, HttpMethod.GET, "custommvc")).isTrue();
assertThat(isAccessible(client, HttpMethod.POST, "custommvc")).isFalse();
@ -103,7 +101,7 @@ class WebMvcEndpointAccessIntegrationTests {
.withPropertyValues("management.endpoints.web.exposure.include=*",
"management.endpoints.access.default=NONE")
.run((context) -> {
WebTestClient client = createClient(context);
RestClient client = createClient(context);
assertThat(isAccessible(client, HttpMethod.GET, "beans")).isFalse();
assertThat(isAccessible(client, HttpMethod.GET, "custommvc")).isFalse();
assertThat(isAccessible(client, HttpMethod.POST, "custommvc")).isFalse();
@ -119,7 +117,7 @@ class WebMvcEndpointAccessIntegrationTests {
"management.endpoints.access.default=READ_ONLY",
"management.endpoint.customservlet.access=UNRESTRICTED")
.run((context) -> {
WebTestClient client = createClient(context);
RestClient client = createClient(context);
assertThat(isAccessible(client, HttpMethod.GET, "beans")).isTrue();
assertThat(isAccessible(client, HttpMethod.GET, "custommvc")).isTrue();
assertThat(isAccessible(client, HttpMethod.POST, "custommvc")).isFalse();
@ -135,7 +133,7 @@ class WebMvcEndpointAccessIntegrationTests {
"management.endpoints.access.default=UNRESTRICTED",
"management.endpoints.access.max-permitted=READ_ONLY")
.run((context) -> {
WebTestClient client = createClient(context);
RestClient client = createClient(context);
assertThat(isAccessible(client, HttpMethod.GET, "beans")).isTrue();
assertThat(isAccessible(client, HttpMethod.GET, "custommvc")).isTrue();
assertThat(isAccessible(client, HttpMethod.POST, "custommvc")).isFalse();
@ -149,7 +147,7 @@ class WebMvcEndpointAccessIntegrationTests {
this.contextRunner.withPropertyValues("management.endpoints.web.exposure.include=*",
"management.endpoints.access.default=UNRESTRICTED", "management.endpoints.access.max-permitted=NONE")
.run((context) -> {
WebTestClient client = createClient(context);
RestClient client = createClient(context);
assertThat(isAccessible(client, HttpMethod.GET, "beans")).isFalse();
assertThat(isAccessible(client, HttpMethod.GET, "custommvc")).isFalse();
assertThat(isAccessible(client, HttpMethod.POST, "custommvc")).isFalse();
@ -158,31 +156,25 @@ class WebMvcEndpointAccessIntegrationTests {
});
}
private WebTestClient createClient(AssertableWebApplicationContext context) {
private RestClient createClient(AssertableWebApplicationContext context) {
int port = context.getSourceApplicationContext(ServletWebServerApplicationContext.class)
.getWebServer()
.getPort();
ExchangeStrategies exchangeStrategies = ExchangeStrategies.builder()
.codecs((configurer) -> configurer.defaultCodecs().maxInMemorySize(-1))
.build();
return WebTestClient.bindToServer()
.baseUrl("http://localhost:" + port)
.exchangeStrategies(exchangeStrategies)
.responseTimeout(Duration.ofMinutes(5))
.build();
return RestClient.builder().defaultStatusHandler((status) -> true, (request, response) -> {
}).baseUrl("http://localhost:" + port).build();
}
private boolean isAccessible(WebTestClient client, HttpMethod method, String path) {
private boolean isAccessible(RestClient client, HttpMethod method, String path) {
path = "/actuator/" + path;
EntityExchangeResult<byte[]> result = client.method(method).uri(path).exchange().expectBody().returnResult();
if (result.getStatus() == HttpStatus.OK) {
ResponseEntity<byte[]> result = client.method(method).uri(path).retrieve().toEntity(byte[].class);
if (result.getStatusCode() == HttpStatus.OK) {
return true;
}
if (result.getStatus() == HttpStatus.NOT_FOUND || result.getStatus() == HttpStatus.METHOD_NOT_ALLOWED) {
if (result.getStatusCode() == HttpStatus.NOT_FOUND || result.getStatusCode() == HttpStatus.METHOD_NOT_ALLOWED) {
return false;
}
throw new IllegalStateException(
String.format("Unexpected %s HTTP status for endpoint %s", result.getStatus(), path));
String.format("Unexpected %s HTTP status for endpoint %s", result.getStatusCode(), path));
}
@org.springframework.boot.actuate.endpoint.web.annotation.RestControllerEndpoint(id = "custommvc")

View File

@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.integrationtest;
package org.springframework.boot.webmvc.actuate.autoconfigure.endpoint.web;
import org.assertj.core.api.ThrowingConsumer;
import org.junit.jupiter.api.Test;
@ -29,7 +29,6 @@ import org.springframework.boot.jackson.autoconfigure.JacksonAutoConfiguration;
import org.springframework.boot.servlet.actuate.autoconfigure.ServletManagementContextAutoConfiguration;
import org.springframework.boot.test.context.runner.ContextConsumer;
import org.springframework.boot.test.context.runner.WebApplicationContextRunner;
import org.springframework.boot.webmvc.actuate.autoconfigure.endpoint.web.WebMvcEndpointManagementContextConfiguration;
import org.springframework.boot.webmvc.autoconfigure.DispatcherServletAutoConfiguration;
import org.springframework.boot.webmvc.autoconfigure.WebMvcAutoConfiguration;
import org.springframework.http.HttpHeaders;

View File

@ -14,10 +14,9 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.integrationtest;
package org.springframework.boot.webmvc.actuate.autoconfigure.endpoint.web;
import java.io.IOException;
import java.time.Duration;
import java.util.function.Supplier;
import jakarta.servlet.ServletException;
@ -27,9 +26,12 @@ import jakarta.servlet.http.HttpServletResponse;
import org.junit.jupiter.api.Test;
import org.springframework.boot.actuate.audit.InMemoryAuditEventRepository;
import org.springframework.boot.actuate.autoconfigure.beans.BeansEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.context.ShutdownEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.health.HealthContributorAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration;
import org.springframework.boot.actuate.web.exchanges.InMemoryHttpExchangeRepository;
import org.springframework.boot.autoconfigure.AutoConfigurations;
@ -48,10 +50,9 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.test.web.reactive.server.EntityExchangeResult;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.reactive.function.client.ExchangeStrategies;
import org.springframework.web.client.RestClient;
import static org.assertj.core.api.Assertions.assertThat;
@ -71,28 +72,19 @@ class WebMvcEndpointExposureIntegrationTests {
WebMvcAutoConfiguration.class, EndpointAutoConfiguration.class, WebEndpointAutoConfiguration.class,
ManagementContextAutoConfiguration.class, ManagementContextAutoConfiguration.class,
ServletManagementContextAutoConfiguration.class, ServletHttpExchangesAutoConfiguration.class,
HealthContributorAutoConfiguration.class))
.withConfiguration(AutoConfigurations.of(EndpointAutoConfigurationClasses.ALL))
HealthContributorAutoConfiguration.class, BeansEndpointAutoConfiguration.class,
HealthEndpointAutoConfiguration.class, ShutdownEndpointAutoConfiguration.class))
.withUserConfiguration(CustomMvcEndpoint.class, CustomServletEndpoint.class,
HttpExchangeRepositoryConfiguration.class, AuditEventRepositoryConfiguration.class)
.withPropertyValues("server.port:0");
@Test
void webEndpointsAreDisabledByDefault() {
void webEndpointsExceptHealthAreDisabledByDefault() {
this.contextRunner.run((context) -> {
WebTestClient client = createClient(context);
RestClient client = createClient(context);
assertThat(isExposed(client, HttpMethod.GET, "beans")).isFalse();
assertThat(isExposed(client, HttpMethod.GET, "conditions")).isFalse();
assertThat(isExposed(client, HttpMethod.GET, "configprops")).isFalse();
assertThat(isExposed(client, HttpMethod.GET, "custommvc")).isFalse();
assertThat(isExposed(client, HttpMethod.GET, "customservlet")).isFalse();
assertThat(isExposed(client, HttpMethod.GET, "env")).isFalse();
assertThat(isExposed(client, HttpMethod.GET, "health")).isTrue();
assertThat(isExposed(client, HttpMethod.GET, "info")).isFalse();
assertThat(isExposed(client, HttpMethod.GET, "mappings")).isFalse();
assertThat(isExposed(client, HttpMethod.POST, "shutdown")).isFalse();
assertThat(isExposed(client, HttpMethod.GET, "threaddump")).isFalse();
assertThat(isExposed(client, HttpMethod.GET, "httpexchanges")).isFalse();
});
}
@ -101,19 +93,10 @@ class WebMvcEndpointExposureIntegrationTests {
WebApplicationContextRunner contextRunner = this.contextRunner
.withPropertyValues("management.endpoints.web.exposure.include=*");
contextRunner.run((context) -> {
WebTestClient client = createClient(context);
RestClient client = createClient(context);
assertThat(isExposed(client, HttpMethod.GET, "beans")).isTrue();
assertThat(isExposed(client, HttpMethod.GET, "conditions")).isTrue();
assertThat(isExposed(client, HttpMethod.GET, "configprops")).isTrue();
assertThat(isExposed(client, HttpMethod.GET, "custommvc")).isTrue();
assertThat(isExposed(client, HttpMethod.GET, "customservlet")).isTrue();
assertThat(isExposed(client, HttpMethod.GET, "env")).isTrue();
assertThat(isExposed(client, HttpMethod.GET, "health")).isTrue();
assertThat(isExposed(client, HttpMethod.GET, "info")).isTrue();
assertThat(isExposed(client, HttpMethod.GET, "mappings")).isTrue();
assertThat(isExposed(client, HttpMethod.POST, "shutdown")).isFalse();
assertThat(isExposed(client, HttpMethod.GET, "threaddump")).isTrue();
assertThat(isExposed(client, HttpMethod.GET, "httpexchanges")).isTrue();
});
}
@ -122,68 +105,44 @@ class WebMvcEndpointExposureIntegrationTests {
WebApplicationContextRunner contextRunner = this.contextRunner
.withPropertyValues("management.endpoints.web.exposure.include=beans");
contextRunner.run((context) -> {
WebTestClient client = createClient(context);
RestClient client = createClient(context);
assertThat(isExposed(client, HttpMethod.GET, "beans")).isTrue();
assertThat(isExposed(client, HttpMethod.GET, "conditions")).isFalse();
assertThat(isExposed(client, HttpMethod.GET, "configprops")).isFalse();
assertThat(isExposed(client, HttpMethod.GET, "custommvc")).isFalse();
assertThat(isExposed(client, HttpMethod.GET, "customservlet")).isFalse();
assertThat(isExposed(client, HttpMethod.GET, "env")).isFalse();
assertThat(isExposed(client, HttpMethod.GET, "health")).isFalse();
assertThat(isExposed(client, HttpMethod.GET, "info")).isFalse();
assertThat(isExposed(client, HttpMethod.GET, "mappings")).isFalse();
assertThat(isExposed(client, HttpMethod.POST, "shutdown")).isFalse();
assertThat(isExposed(client, HttpMethod.GET, "threaddump")).isFalse();
assertThat(isExposed(client, HttpMethod.GET, "httpexchanges")).isFalse();
});
}
@Test
void singleWebEndpointCanBeExcluded() {
WebApplicationContextRunner contextRunner = this.contextRunner.withPropertyValues(
"management.endpoints.web.exposure.include=*", "management.endpoints.web.exposure.exclude=shutdown");
"management.endpoints.web.exposure.include=*", "management.endpoints.web.exposure.exclude=beans");
contextRunner.run((context) -> {
WebTestClient client = createClient(context);
assertThat(isExposed(client, HttpMethod.GET, "beans")).isTrue();
assertThat(isExposed(client, HttpMethod.GET, "conditions")).isTrue();
assertThat(isExposed(client, HttpMethod.GET, "configprops")).isTrue();
assertThat(isExposed(client, HttpMethod.GET, "custommvc")).isTrue();
assertThat(isExposed(client, HttpMethod.GET, "customservlet")).isTrue();
assertThat(isExposed(client, HttpMethod.GET, "env")).isTrue();
RestClient client = createClient(context);
assertThat(isExposed(client, HttpMethod.GET, "beans")).isFalse();
assertThat(isExposed(client, HttpMethod.GET, "health")).isTrue();
assertThat(isExposed(client, HttpMethod.GET, "info")).isTrue();
assertThat(isExposed(client, HttpMethod.GET, "mappings")).isTrue();
assertThat(isExposed(client, HttpMethod.POST, "shutdown")).isFalse();
assertThat(isExposed(client, HttpMethod.GET, "threaddump")).isTrue();
assertThat(isExposed(client, HttpMethod.GET, "httpexchanges")).isTrue();
});
}
private WebTestClient createClient(AssertableWebApplicationContext context) {
private RestClient createClient(AssertableWebApplicationContext context) {
int port = context.getSourceApplicationContext(ServletWebServerApplicationContext.class)
.getWebServer()
.getPort();
ExchangeStrategies exchangeStrategies = ExchangeStrategies.builder()
.codecs((configurer) -> configurer.defaultCodecs().maxInMemorySize(-1))
.build();
return WebTestClient.bindToServer()
.baseUrl("http://localhost:" + port)
.exchangeStrategies(exchangeStrategies)
.responseTimeout(Duration.ofMinutes(5))
.build();
return RestClient.builder().defaultStatusHandler((status) -> true, (request, response) -> {
}).baseUrl("http://localhost:" + port).build();
}
private boolean isExposed(WebTestClient client, HttpMethod method, String path) {
private boolean isExposed(RestClient client, HttpMethod method, String path) {
path = "/actuator/" + path;
EntityExchangeResult<byte[]> result = client.method(method).uri(path).exchange().expectBody().returnResult();
if (result.getStatus() == HttpStatus.OK) {
ResponseEntity<byte[]> result = client.method(method).uri(path).retrieve().toEntity(byte[].class);
if (result.getStatusCode() == HttpStatus.OK) {
return true;
}
if (result.getStatus() == HttpStatus.NOT_FOUND) {
if (result.getStatusCode() == HttpStatus.NOT_FOUND) {
return false;
}
throw new IllegalStateException(
String.format("Unexpected %s HTTP status for endpoint %s", result.getStatus(), path));
String.format("Unexpected %s HTTP status for endpoint %s", result.getStatusCode(), path));
}
@org.springframework.boot.actuate.endpoint.web.annotation.RestControllerEndpoint(id = "custommvc")

View File

@ -14,12 +14,18 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.integrationtest;
package org.springframework.boot.webmvc.actuate.autoconfigure.endpoint.web;
import java.io.IOException;
import java.util.function.Supplier;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.jsontype.TypeSerializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.StdScalarSerializer;
import jakarta.servlet.http.HttpServlet;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.actuate.autoconfigure.audit.AuditAutoConfiguration;
@ -27,13 +33,11 @@ import org.springframework.boot.actuate.autoconfigure.beans.BeansEndpointAutoCon
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration;
import org.springframework.boot.actuate.endpoint.jackson.EndpointObjectMapper;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.data.rest.autoconfigure.RepositoryRestMvcAutoConfiguration;
import org.springframework.boot.hateoas.autoconfigure.HypermediaAutoConfiguration;
import org.springframework.boot.http.converter.autoconfigure.HttpMessageConvertersAutoConfiguration;
import org.springframework.boot.jackson.autoconfigure.JacksonAutoConfiguration;
import org.springframework.boot.security.autoconfigure.servlet.SecurityAutoConfiguration;
import org.springframework.boot.servlet.actuate.autoconfigure.ServletManagementContextAutoConfiguration;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.boot.web.context.servlet.AnnotationConfigServletWebApplicationContext;
@ -42,18 +46,12 @@ import org.springframework.boot.webmvc.autoconfigure.DispatcherServletAutoConfig
import org.springframework.boot.webmvc.autoconfigure.WebMvcAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockServletContext;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.test.context.TestSecurityContextHolder;
import org.springframework.test.web.servlet.assertj.MockMvcTester;
import org.springframework.test.web.servlet.setup.MockMvcConfigurer;
import org.springframework.web.util.pattern.PathPatternParser;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
/**
* Integration tests for the Actuator's MVC endpoints.
@ -64,12 +62,6 @@ class WebMvcEndpointIntegrationTests {
private AnnotationConfigServletWebApplicationContext context;
@AfterEach
void close() {
TestSecurityContextHolder.clearContext();
this.context.close();
}
@Test
void webMvcEndpointHandlerMappingIsConfiguredWithPathPatternParser() {
this.context = new AnnotationConfigServletWebApplicationContext();
@ -80,38 +72,6 @@ class WebMvcEndpointIntegrationTests {
assertThat(handlerMapping.getPatternParser()).isInstanceOf(PathPatternParser.class);
}
@Test
void endpointsAreSecureByDefault() {
this.context = new AnnotationConfigServletWebApplicationContext();
this.context.register(SecureConfiguration.class);
MockMvcTester mvc = createSecureMockMvcTester();
assertThat(mvc.get().uri("/actuator/beans").accept(MediaType.APPLICATION_JSON))
.hasStatus(HttpStatus.UNAUTHORIZED);
}
@Test
void endpointsAreSecureByDefaultWithCustomBasePath() {
this.context = new AnnotationConfigServletWebApplicationContext();
this.context.register(SecureConfiguration.class);
TestPropertyValues.of("management.endpoints.web.base-path:/management").applyTo(this.context);
MockMvcTester mvc = createSecureMockMvcTester();
assertThat(mvc.get().uri("/management/beans").accept(MediaType.APPLICATION_JSON))
.hasStatus(HttpStatus.UNAUTHORIZED);
}
@Test
void endpointsAreSecureWithActuatorRoleWithCustomBasePath() {
TestSecurityContextHolder.getContext()
.setAuthentication(new TestingAuthenticationToken("user", "N/A", "ROLE_ACTUATOR"));
this.context = new AnnotationConfigServletWebApplicationContext();
this.context.register(SecureConfiguration.class);
TestPropertyValues
.of("management.endpoints.web.base-path:/management", "management.endpoints.web.exposure.include=*")
.applyTo(this.context);
MockMvcTester mvc = createSecureMockMvcTester();
assertThat(mvc.get().uri("/management/beans")).hasStatusOk();
}
@Test
void linksAreProvidedToAllEndpointTypes() {
this.context = new AnnotationConfigServletWebApplicationContext();
@ -143,19 +103,10 @@ class WebMvcEndpointIntegrationTests {
assertThat(mvc.get().uri("/actuator/beans")).hasStatusOk().bodyText().contains("\"scope\":\"notelgnis\"");
}
private MockMvcTester createSecureMockMvcTester() {
return doCreateMockMvcTester(springSecurity());
}
private MockMvcTester doCreateMockMvcTester(MockMvcConfigurer... configurers) {
private MockMvcTester doCreateMockMvcTester() {
this.context.setServletContext(new MockServletContext());
this.context.refresh();
return MockMvcTester.from(this.context, (builder) -> {
for (MockMvcConfigurer configurer : configurers) {
builder.apply(configurer);
}
return builder.build();
});
return MockMvcTester.from(this.context);
}
@ImportAutoConfiguration({ JacksonAutoConfiguration.class, HttpMessageConvertersAutoConfiguration.class,
@ -168,24 +119,6 @@ class WebMvcEndpointIntegrationTests {
}
@Import(SecureConfiguration.class)
@ImportAutoConfiguration({ HypermediaAutoConfiguration.class })
static class SpringHateoasConfiguration {
}
@Import(SecureConfiguration.class)
@ImportAutoConfiguration({ HypermediaAutoConfiguration.class, RepositoryRestMvcAutoConfiguration.class })
static class SpringDataRestConfiguration {
}
@Import(DefaultConfiguration.class)
@ImportAutoConfiguration({ SecurityAutoConfiguration.class })
static class SecureConfiguration {
}
@org.springframework.boot.actuate.endpoint.web.annotation.ServletEndpoint(id = "servlet")
@SuppressWarnings({ "deprecation", "removal" })
static class TestServletEndpoint
@ -231,4 +164,49 @@ class WebMvcEndpointIntegrationTests {
}
@Configuration
@SuppressWarnings({ "deprecation", "removal" })
static class EndpointObjectMapperConfiguration {
@Bean
EndpointObjectMapper endpointObjectMapper() {
SimpleModule module = new SimpleModule();
module.addSerializer(String.class, new ReverseStringSerializer());
ObjectMapper objectMapper = org.springframework.http.converter.json.Jackson2ObjectMapperBuilder.json()
.modules(module)
.build();
return () -> objectMapper;
}
static class ReverseStringSerializer extends StdScalarSerializer<Object> {
ReverseStringSerializer() {
super(String.class, false);
}
@Override
public boolean isEmpty(SerializerProvider prov, Object value) {
return ((String) value).isEmpty();
}
@Override
public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException {
serialize(value, gen);
}
@Override
public final void serializeWithType(Object value, JsonGenerator gen, SerializerProvider provider,
TypeSerializer typeSer) throws IOException {
serialize(value, gen);
}
private void serialize(Object value, JsonGenerator gen) throws IOException {
StringBuilder builder = new StringBuilder((String) value);
gen.writeString(builder.reverse().toString());
}
}
}
}

View File

@ -14,11 +14,12 @@
* limitations under the License.
*/
package org.springframework.boot.actuate.autoconfigure.integrationtest;
package org.springframework.boot.webmvc.actuate.autoconfigure.endpoint.web;
import org.springframework.boot.actuate.autoconfigure.endpoint.EndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.endpoint.web.WebEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.health.HealthEndpointAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.integrationtest.AbstractHealthEndpointAdditionalPathIntegrationTests;
import org.springframework.boot.actuate.autoconfigure.system.DiskSpaceHealthContributorAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.web.server.ManagementContextAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurations;