Add RSocket and WebFlux Observation Tests

Issue gh-11989
Issue gh-11990
This commit is contained in:
Josh Cummings
2024-11-25 12:32:44 -07:00
parent 46fe0124ba
commit a55021539a
2 changed files with 281 additions and 0 deletions

View File

@@ -21,20 +21,27 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.net.URI;
import java.util.Iterator;
import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationHandler;
import io.micrometer.observation.ObservationRegistry;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import reactor.core.publisher.Mono;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.rsocket.annotation.support.RSocketMessageHandler;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.authentication.password.CompromisedPasswordDecision;
import org.springframework.security.authentication.password.CompromisedPasswordException;
import org.springframework.security.authentication.password.ReactiveCompromisedPasswordChecker;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.rsocket.EnableRSocketSecurity;
import org.springframework.security.config.test.SpringTestContext;
import org.springframework.security.config.test.SpringTestContextExtension;
import org.springframework.security.config.users.ReactiveAuthenticationTestConfiguration;
@@ -60,6 +67,12 @@ import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.springframework.security.config.Customizer.withDefaults;
import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.csrf;
import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.mockAuthentication;
import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.springSecurity;
@@ -205,6 +218,30 @@ public class ServerHttpSecurityConfigurationTests {
.isEqualTo("harold");
}
@Test
public void getWhenUsingObservationRegistryThenObservesRequest() {
this.spring.register(ObservationRegistryConfig.class).autowire();
// @formatter:off
this.webClient
.get()
.uri("/hello")
.headers((headers) -> headers.setBasicAuth("user", "password"))
.exchange()
.expectStatus()
.isNotFound();
// @formatter:on
ObservationHandler<Observation.Context> handler = this.spring.getContext().getBean(ObservationHandler.class);
ArgumentCaptor<Observation.Context> captor = ArgumentCaptor.forClass(Observation.Context.class);
verify(handler, times(6)).onStart(captor.capture());
Iterator<Observation.Context> contexts = captor.getAllValues().iterator();
assertThat(contexts.next().getContextualName()).isEqualTo("http get");
assertThat(contexts.next().getContextualName()).isEqualTo("security filterchain before");
assertThat(contexts.next().getName()).isEqualTo("spring.security.authentications");
assertThat(contexts.next().getName()).isEqualTo("spring.security.authorizations");
assertThat(contexts.next().getName()).isEqualTo("spring.security.http.secured.requests");
assertThat(contexts.next().getContextualName()).isEqualTo("security filterchain after");
}
@Configuration
static class SubclassConfig extends ServerHttpSecurityConfiguration {
@@ -368,4 +405,48 @@ public class ServerHttpSecurityConfigurationTests {
}
@Configuration
@EnableWebFlux
@EnableWebFluxSecurity
static class ObservationRegistryConfig {
private ObservationHandler<Observation.Context> handler = mock(ObservationHandler.class);
@Bean
SecurityWebFilterChain app(ServerHttpSecurity http) throws Exception {
http.httpBasic(withDefaults()).authorizeExchange((authorize) -> authorize.anyExchange().authenticated());
return http.build();
}
@Bean
ReactiveUserDetailsService userDetailsService() {
return new MapReactiveUserDetailsService(
User.withDefaultPasswordEncoder().username("user").password("password").authorities("app").build());
}
@Bean
ObservationHandler<Observation.Context> observationHandler() {
return this.handler;
}
@Bean
ObservationRegistry observationRegistry() {
given(this.handler.supportsContext(any())).willReturn(true);
ObservationRegistry registry = ObservationRegistry.create();
registry.observationConfig().observationHandler(this.handler);
return registry;
}
}
@EnableRSocketSecurity
static class RSocketSecurityConfig {
@Bean
RSocketMessageHandler messageHandler() {
return new RSocketMessageHandler();
}
}
}