Add ObservationRegistry Tests
Issue gh-11989 Issue gh-11990
This commit is contained in:
parent
717529deb4
commit
1ed20aa210
|
@ -28,6 +28,10 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import io.micrometer.observation.Observation;
|
||||
import io.micrometer.observation.ObservationHandler;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import io.micrometer.observation.ObservationTextPublisher;
|
||||
import jakarta.annotation.security.DenyAll;
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
@ -40,9 +44,12 @@ import org.springframework.aop.Advisor;
|
|||
import org.springframework.aop.config.AopConfigUtils;
|
||||
import org.springframework.aop.support.DefaultPointcutAdvisor;
|
||||
import org.springframework.aop.support.JdkRegexpMethodPointcut;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
|
||||
import org.springframework.context.annotation.AdviceMode;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
@ -1018,6 +1025,43 @@ public class PrePostMethodSecurityConfigurationTests {
|
|||
assertThat(service.getIdPath("uid")).isEqualTo("uid");
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser
|
||||
public void prePostMethodWhenObservationRegistryThenObserved() {
|
||||
this.spring.register(MethodSecurityServiceEnabledConfig.class, ObservationRegistryConfig.class).autowire();
|
||||
this.methodSecurityService.preAuthorizePermitAll();
|
||||
ObservationHandler<?> handler = this.spring.getContext().getBean(ObservationHandler.class);
|
||||
verify(handler).onStart(any());
|
||||
verify(handler).onStop(any());
|
||||
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::preAuthorize);
|
||||
verify(handler).onError(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser
|
||||
public void securedMethodWhenObservationRegistryThenObserved() {
|
||||
this.spring.register(MethodSecurityServiceEnabledConfig.class, ObservationRegistryConfig.class).autowire();
|
||||
this.methodSecurityService.securedUser();
|
||||
ObservationHandler<?> handler = this.spring.getContext().getBean(ObservationHandler.class);
|
||||
verify(handler).onStart(any());
|
||||
verify(handler).onStop(any());
|
||||
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(this.methodSecurityService::secured);
|
||||
verify(handler).onError(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser
|
||||
public void jsr250MethodWhenObservationRegistryThenObserved() {
|
||||
this.spring.register(MethodSecurityServiceEnabledConfig.class, ObservationRegistryConfig.class).autowire();
|
||||
this.methodSecurityService.jsr250RolesAllowedUser();
|
||||
ObservationHandler<?> handler = this.spring.getContext().getBean(ObservationHandler.class);
|
||||
verify(handler).onStart(any());
|
||||
verify(handler).onStop(any());
|
||||
assertThatExceptionOfType(AccessDeniedException.class)
|
||||
.isThrownBy(this.methodSecurityService::jsr250RolesAllowed);
|
||||
verify(handler).onError(any());
|
||||
}
|
||||
|
||||
private static Consumer<ConfigurableWebApplicationContext> disallowBeanOverriding() {
|
||||
return (context) -> ((AnnotationConfigWebApplicationContext) context).setAllowBeanDefinitionOverriding(false);
|
||||
}
|
||||
|
@ -1655,4 +1699,47 @@ public class PrePostMethodSecurityConfigurationTests {
|
|||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class ObservationRegistryConfig {
|
||||
|
||||
private final ObservationRegistry registry = ObservationRegistry.create();
|
||||
|
||||
private final ObservationHandler<Observation.Context> handler = spy(new ObservationTextPublisher());
|
||||
|
||||
@Bean
|
||||
ObservationRegistry observationRegistry() {
|
||||
return this.registry;
|
||||
}
|
||||
|
||||
@Bean
|
||||
ObservationHandler<Observation.Context> observationHandler() {
|
||||
return this.handler;
|
||||
}
|
||||
|
||||
@Bean
|
||||
ObservationRegistryPostProcessor observationRegistryPostProcessor(
|
||||
ObjectProvider<ObservationHandler<Observation.Context>> handler) {
|
||||
return new ObservationRegistryPostProcessor(handler);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class ObservationRegistryPostProcessor implements BeanPostProcessor {
|
||||
|
||||
private final ObjectProvider<ObservationHandler<Observation.Context>> handler;
|
||||
|
||||
ObservationRegistryPostProcessor(ObjectProvider<ObservationHandler<Observation.Context>> handler) {
|
||||
this.handler = handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||
if (bean instanceof ObservationRegistry registry) {
|
||||
registry.observationConfig().observationHandler(this.handler.getObject());
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -23,12 +23,17 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import io.micrometer.observation.Observation;
|
||||
import io.micrometer.observation.ObservationHandler;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import io.micrometer.observation.ObservationTextPublisher;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
@ -62,6 +67,7 @@ import static org.mockito.ArgumentMatchers.any;
|
|||
import static org.mockito.Mockito.clearInvocations;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
/**
|
||||
|
@ -235,6 +241,25 @@ public class ReactiveMethodSecurityConfigurationTests {
|
|||
verify(handler, never()).handleDeniedInvocation(any(), any(Authz.AuthzResult.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void prePostMethodWhenObservationRegistryThenObserved() {
|
||||
this.spring.register(MethodSecurityServiceConfig.class, ObservationRegistryConfig.class).autowire();
|
||||
ReactiveMethodSecurityService service = this.spring.getContext().getBean(ReactiveMethodSecurityService.class);
|
||||
Authentication user = TestAuthentication.authenticatedUser();
|
||||
StepVerifier
|
||||
.create(service.preAuthorizeUser().contextWrite(ReactiveSecurityContextHolder.withAuthentication(user)))
|
||||
.expectNextCount(1)
|
||||
.verifyComplete();
|
||||
ObservationHandler<?> handler = this.spring.getContext().getBean(ObservationHandler.class);
|
||||
verify(handler).onStart(any());
|
||||
verify(handler).onStop(any());
|
||||
StepVerifier
|
||||
.create(service.preAuthorizeAdmin().contextWrite(ReactiveSecurityContextHolder.withAuthentication(user)))
|
||||
.expectError()
|
||||
.verify();
|
||||
verify(handler).onError(any());
|
||||
}
|
||||
|
||||
private static Consumer<User.UserBuilder> authorities(String... authorities) {
|
||||
return (builder) -> builder.authorities(authorities);
|
||||
}
|
||||
|
@ -388,4 +413,30 @@ public class ReactiveMethodSecurityConfigurationTests {
|
|||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableReactiveMethodSecurity
|
||||
static class ObservationRegistryConfig {
|
||||
|
||||
private final ObservationRegistry registry = ObservationRegistry.create();
|
||||
|
||||
private final ObservationHandler<Observation.Context> handler = spy(new ObservationTextPublisher());
|
||||
|
||||
@Bean
|
||||
ObservationRegistry observationRegistry() {
|
||||
return this.registry;
|
||||
}
|
||||
|
||||
@Bean
|
||||
ObservationHandler<Observation.Context> observationHandler() {
|
||||
return this.handler;
|
||||
}
|
||||
|
||||
@Bean
|
||||
PrePostMethodSecurityConfigurationTests.ObservationRegistryPostProcessor observationRegistryPostProcessor(
|
||||
ObjectProvider<ObservationHandler<Observation.Context>> handler) {
|
||||
return new PrePostMethodSecurityConfigurationTests.ObservationRegistryPostProcessor(handler);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -48,6 +48,12 @@ import org.springframework.util.StringUtils;
|
|||
@ReactiveMethodSecurityService.Mask("classmask")
|
||||
public interface ReactiveMethodSecurityService {
|
||||
|
||||
@PreAuthorize("hasRole('USER')")
|
||||
Mono<String> preAuthorizeUser();
|
||||
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
Mono<String> preAuthorizeAdmin();
|
||||
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
@HandleAuthorizationDenied(handlerClass = StarMaskingHandler.class)
|
||||
Mono<String> preAuthorizeGetCardNumberIfAdmin(String cardNumber);
|
||||
|
|
|
@ -25,6 +25,16 @@ import org.springframework.security.authorization.AuthorizationDeniedException;
|
|||
|
||||
public class ReactiveMethodSecurityServiceImpl implements ReactiveMethodSecurityService {
|
||||
|
||||
@Override
|
||||
public Mono<String> preAuthorizeUser() {
|
||||
return Mono.just("user");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<String> preAuthorizeAdmin() {
|
||||
return Mono.just("admin");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mono<String> preAuthorizeGetCardNumberIfAdmin(String cardNumber) {
|
||||
return Mono.just(cardNumber);
|
||||
|
|
|
@ -18,12 +18,20 @@ package org.springframework.security.config.annotation.web.configurers;
|
|||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import io.micrometer.observation.Observation;
|
||||
import io.micrometer.observation.ObservationHandler;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import io.micrometer.observation.ObservationTextPublisher;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
|
||||
|
@ -33,6 +41,7 @@ import org.springframework.security.authentication.TestAuthentication;
|
|||
import org.springframework.security.authorization.AuthorizationDecision;
|
||||
import org.springframework.security.authorization.AuthorizationEventPublisher;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
import org.springframework.security.authorization.AuthorizationObservationContext;
|
||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||
import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
|
||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||
|
@ -43,6 +52,7 @@ import org.springframework.security.config.test.SpringTestContextExtension;
|
|||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
||||
import org.springframework.security.core.userdetails.User;
|
||||
import org.springframework.security.core.userdetails.UserDetails;
|
||||
import org.springframework.security.core.userdetails.UserDetailsService;
|
||||
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
||||
|
@ -63,8 +73,10 @@ import org.springframework.web.bind.annotation.RestController;
|
|||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
@ -153,7 +165,8 @@ public class AuthorizeHttpRequestsConfigurerTests {
|
|||
@Test
|
||||
public void configureWhenObjectPostProcessorRegisteredThenInvokedOnAuthorizationManagerAndAuthorizationFilter() {
|
||||
this.spring.register(ObjectPostProcessorConfig.class).autowire();
|
||||
ObjectPostProcessor objectPostProcessor = this.spring.getContext().getBean(ObjectPostProcessor.class);
|
||||
ObjectPostProcessor<Object> objectPostProcessor = this.spring.getContext()
|
||||
.getBean(ObjectPostProcessorConfig.class).objectPostProcessor;
|
||||
verify(objectPostProcessor).postProcess(any(RequestMatcherDelegatingAuthorizationManager.class));
|
||||
verify(objectPostProcessor).postProcess(any(AuthorizationFilter.class));
|
||||
}
|
||||
|
@ -623,6 +636,20 @@ public class AuthorizeHttpRequestsConfigurerTests {
|
|||
this.mvc.perform(requestWithUser).andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getWhenObservationRegistryThenObserves() throws Exception {
|
||||
this.spring.register(RoleUserConfig.class, BasicController.class, ObservationRegistryConfig.class).autowire();
|
||||
ObservationHandler<Observation.Context> handler = this.spring.getContext().getBean(ObservationHandler.class);
|
||||
this.mvc.perform(get("/").with(user("user").roles("USER"))).andExpect(status().isOk());
|
||||
ArgumentCaptor<Observation.Context> context = ArgumentCaptor.forClass(Observation.Context.class);
|
||||
verify(handler, atLeastOnce()).onStart(context.capture());
|
||||
assertThat(context.getAllValues()).anyMatch((c) -> c instanceof AuthorizationObservationContext);
|
||||
verify(handler, atLeastOnce()).onStop(context.capture());
|
||||
assertThat(context.getAllValues()).anyMatch((c) -> c instanceof AuthorizationObservationContext);
|
||||
this.mvc.perform(get("/").with(user("user").roles("WRONG"))).andExpect(status().isForbidden());
|
||||
verify(handler).onError(any());
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class GrantedAuthorityDefaultHasRoleConfig {
|
||||
|
@ -1015,6 +1042,12 @@ public class AuthorizeHttpRequestsConfigurerTests {
|
|||
// @formatter:on
|
||||
}
|
||||
|
||||
@Bean
|
||||
UserDetailsService users() {
|
||||
return new InMemoryUserDetailsManager(
|
||||
User.withUsername("user").password("{noop}password").roles("USER").build());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
|
@ -1212,4 +1245,47 @@ public class AuthorizeHttpRequestsConfigurerTests {
|
|||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class ObservationRegistryConfig {
|
||||
|
||||
private final ObservationRegistry registry = ObservationRegistry.create();
|
||||
|
||||
private final ObservationHandler<Observation.Context> handler = spy(new ObservationTextPublisher());
|
||||
|
||||
@Bean
|
||||
ObservationRegistry observationRegistry() {
|
||||
return this.registry;
|
||||
}
|
||||
|
||||
@Bean
|
||||
ObservationHandler<Observation.Context> observationHandler() {
|
||||
return this.handler;
|
||||
}
|
||||
|
||||
@Bean
|
||||
ObservationRegistryPostProcessor observationRegistryPostProcessor(
|
||||
ObjectProvider<ObservationHandler<Observation.Context>> handler) {
|
||||
return new ObservationRegistryPostProcessor(handler);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class ObservationRegistryPostProcessor implements BeanPostProcessor {
|
||||
|
||||
private final ObjectProvider<ObservationHandler<Observation.Context>> handler;
|
||||
|
||||
ObservationRegistryPostProcessor(ObjectProvider<ObservationHandler<Observation.Context>> handler) {
|
||||
this.handler = handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||
if (bean instanceof ObservationRegistry registry) {
|
||||
registry.observationConfig().observationHandler(this.handler.getObject());
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,14 +16,23 @@
|
|||
|
||||
package org.springframework.security.config.annotation.web.configurers;
|
||||
|
||||
import io.micrometer.observation.Observation;
|
||||
import io.micrometer.observation.ObservationHandler;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import io.micrometer.observation.ObservationTextPublisher;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.authentication.AuthenticationObservationContext;
|
||||
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||
|
@ -50,7 +59,9 @@ import org.springframework.web.bind.annotation.GetMapping;
|
|||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
@ -161,6 +172,23 @@ public class HttpBasicConfigurerTests {
|
|||
.saveContext(any(SecurityContext.class), any(HttpServletRequest.class), any(HttpServletResponse.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void httpBasicWhenObservationRegistryThenObserves() throws Exception {
|
||||
this.spring.register(HttpBasic.class, Users.class, Home.class, ObservationRegistryConfig.class).autowire();
|
||||
ObservationHandler<Observation.Context> handler = this.spring.getContext().getBean(ObservationHandler.class);
|
||||
this.mvc.perform(get("/").with(httpBasic("user", "password")))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().string("user"));
|
||||
ArgumentCaptor<Observation.Context> context = ArgumentCaptor.forClass(Observation.Context.class);
|
||||
verify(handler, atLeastOnce()).onStart(context.capture());
|
||||
assertThat(context.getAllValues()).anyMatch((c) -> c instanceof AuthenticationObservationContext);
|
||||
verify(handler, atLeastOnce()).onStop(context.capture());
|
||||
assertThat(context.getAllValues()).anyMatch((c) -> c instanceof AuthenticationObservationContext);
|
||||
this.mvc.perform(get("/").with(httpBasic("user", "wrong"))).andExpect(status().isUnauthorized());
|
||||
verify(handler).onError(context.capture());
|
||||
assertThat(context.getValue()).isInstanceOf(AuthenticationObservationContext.class);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableWebSecurity
|
||||
static class ObjectPostProcessorConfig {
|
||||
|
@ -384,4 +412,47 @@ public class HttpBasicConfigurerTests {
|
|||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class ObservationRegistryConfig {
|
||||
|
||||
private final ObservationRegistry registry = ObservationRegistry.create();
|
||||
|
||||
private final ObservationHandler<Observation.Context> handler = spy(new ObservationTextPublisher());
|
||||
|
||||
@Bean
|
||||
ObservationRegistry observationRegistry() {
|
||||
return this.registry;
|
||||
}
|
||||
|
||||
@Bean
|
||||
ObservationHandler<Observation.Context> observationHandler() {
|
||||
return this.handler;
|
||||
}
|
||||
|
||||
@Bean
|
||||
ObservationRegistryPostProcessor observationRegistryPostProcessor(
|
||||
ObjectProvider<ObservationHandler<Observation.Context>> handler) {
|
||||
return new ObservationRegistryPostProcessor(handler);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class ObservationRegistryPostProcessor implements BeanPostProcessor {
|
||||
|
||||
private final ObjectProvider<ObservationHandler<Observation.Context>> handler;
|
||||
|
||||
ObservationRegistryPostProcessor(ObjectProvider<ObservationHandler<Observation.Context>> handler) {
|
||||
this.handler = handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||
if (bean instanceof ObservationRegistry registry) {
|
||||
registry.observationConfig().observationHandler(this.handler.getObject());
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -26,13 +26,20 @@ import java.util.Map;
|
|||
import java.util.function.Consumer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import io.micrometer.observation.Observation;
|
||||
import io.micrometer.observation.ObservationHandler;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import io.micrometer.observation.ObservationTextPublisher;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
@ -95,7 +102,9 @@ import org.springframework.web.socket.sockjs.transport.session.WebSocketServerSo
|
|||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.fail;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.springframework.security.web.csrf.CsrfTokenAssert.assertThatCsrfToken;
|
||||
|
||||
|
@ -381,6 +390,30 @@ public class WebSocketMessageBrokerSecurityConfigurationTests {
|
|||
clientInboundChannel().send(message("/anonymous"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sendMessageWhenObservationRegistryThenObserves() {
|
||||
loadConfig(WebSocketSecurityConfig.class, ObservationRegistryConfig.class);
|
||||
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create(SimpMessageType.CONNECT);
|
||||
headers.setNativeHeader(this.token.getHeaderName(), XOR_CSRF_TOKEN_VALUE);
|
||||
Message<?> message = message(headers, "/authenticated");
|
||||
headers.getSessionAttributes().put(CsrfToken.class.getName(), this.token);
|
||||
clientInboundChannel().send(message);
|
||||
ObservationHandler<Observation.Context> observationHandler = this.context.getBean(ObservationHandler.class);
|
||||
verify(observationHandler).onStart(any());
|
||||
verify(observationHandler).onStop(any());
|
||||
headers = SimpMessageHeaderAccessor.create(SimpMessageType.CONNECT);
|
||||
headers.setNativeHeader(this.token.getHeaderName(), XOR_CSRF_TOKEN_VALUE);
|
||||
message = message(headers, "/denyAll");
|
||||
headers.getSessionAttributes().put(CsrfToken.class.getName(), this.token);
|
||||
try {
|
||||
clientInboundChannel().send(message);
|
||||
}
|
||||
catch (MessageDeliveryException ex) {
|
||||
// okay
|
||||
}
|
||||
verify(observationHandler).onError(any());
|
||||
}
|
||||
|
||||
private void assertHandshake(HttpServletRequest request) {
|
||||
TestHandshakeHandler handshakeHandler = this.context.getBean(TestHandshakeHandler.class);
|
||||
assertThatCsrfToken(handshakeHandler.attributes.get(CsrfToken.class.getName())).isEqualTo(this.token);
|
||||
|
@ -892,4 +925,47 @@ public class WebSocketMessageBrokerSecurityConfigurationTests {
|
|||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class ObservationRegistryConfig {
|
||||
|
||||
private final ObservationRegistry registry = ObservationRegistry.create();
|
||||
|
||||
private final ObservationHandler<Observation.Context> handler = spy(new ObservationTextPublisher());
|
||||
|
||||
@Bean
|
||||
ObservationRegistry observationRegistry() {
|
||||
return this.registry;
|
||||
}
|
||||
|
||||
@Bean
|
||||
ObservationHandler<Observation.Context> observationHandler() {
|
||||
return this.handler;
|
||||
}
|
||||
|
||||
@Bean
|
||||
ObservationRegistryPostProcessor observationRegistryPostProcessor(
|
||||
ObjectProvider<ObservationHandler<Observation.Context>> handler) {
|
||||
return new ObservationRegistryPostProcessor(handler);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class ObservationRegistryPostProcessor implements BeanPostProcessor {
|
||||
|
||||
private final ObjectProvider<ObservationHandler<Observation.Context>> handler;
|
||||
|
||||
ObservationRegistryPostProcessor(ObjectProvider<ObservationHandler<Observation.Context>> handler) {
|
||||
this.handler = handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||
if (bean instanceof ObservationRegistry registry) {
|
||||
registry.observationConfig().observationHandler(this.handler.getObject());
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue