|
18 | 18 |
|
19 | 19 | import java.util.function.Supplier;
|
20 | 20 |
|
| 21 | +import io.micrometer.observation.Observation; |
| 22 | +import io.micrometer.observation.ObservationHandler; |
| 23 | +import io.micrometer.observation.ObservationRegistry; |
| 24 | +import io.micrometer.observation.ObservationTextPublisher; |
21 | 25 | import jakarta.servlet.http.HttpServletRequest;
|
22 | 26 | import org.junit.jupiter.api.Test;
|
23 | 27 | import org.junit.jupiter.api.extension.ExtendWith;
|
| 28 | +import org.mockito.ArgumentCaptor; |
24 | 29 |
|
| 30 | +import org.springframework.beans.BeansException; |
25 | 31 | import org.springframework.beans.factory.BeanCreationException;
|
| 32 | +import org.springframework.beans.factory.ObjectProvider; |
26 | 33 | import org.springframework.beans.factory.annotation.Autowired;
|
| 34 | +import org.springframework.beans.factory.config.BeanPostProcessor; |
27 | 35 | import org.springframework.context.annotation.Bean;
|
28 | 36 | import org.springframework.context.annotation.Configuration;
|
29 | 37 | import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
|
|
33 | 41 | import org.springframework.security.authorization.AuthorizationDecision;
|
34 | 42 | import org.springframework.security.authorization.AuthorizationEventPublisher;
|
35 | 43 | import org.springframework.security.authorization.AuthorizationManager;
|
| 44 | +import org.springframework.security.authorization.AuthorizationObservationContext; |
36 | 45 | import org.springframework.security.config.annotation.ObjectPostProcessor;
|
37 | 46 | import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
|
38 | 47 | import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
|
43 | 52 | import org.springframework.security.core.Authentication;
|
44 | 53 | import org.springframework.security.core.authority.AuthorityUtils;
|
45 | 54 | import org.springframework.security.core.authority.SimpleGrantedAuthority;
|
| 55 | +import org.springframework.security.core.userdetails.User; |
46 | 56 | import org.springframework.security.core.userdetails.UserDetails;
|
47 | 57 | import org.springframework.security.core.userdetails.UserDetailsService;
|
48 | 58 | import org.springframework.security.provisioning.InMemoryUserDetailsManager;
|
|
63 | 73 | import org.springframework.web.servlet.config.annotation.EnableWebMvc;
|
64 | 74 | import org.springframework.web.servlet.handler.HandlerMappingIntrospector;
|
65 | 75 |
|
| 76 | +import static org.assertj.core.api.Assertions.assertThat; |
66 | 77 | import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
67 | 78 | import static org.mockito.Mockito.any;
|
| 79 | +import static org.mockito.Mockito.atLeastOnce; |
68 | 80 | import static org.mockito.Mockito.mock;
|
69 | 81 | import static org.mockito.Mockito.spy;
|
70 | 82 | import static org.mockito.Mockito.verify;
|
@@ -153,7 +165,8 @@ public void configureMvcMatcherAccessAuthorizationManagerWhenNullThenException()
|
153 | 165 | @Test
|
154 | 166 | public void configureWhenObjectPostProcessorRegisteredThenInvokedOnAuthorizationManagerAndAuthorizationFilter() {
|
155 | 167 | this.spring.register(ObjectPostProcessorConfig.class).autowire();
|
156 |
| - ObjectPostProcessor objectPostProcessor = this.spring.getContext().getBean(ObjectPostProcessor.class); |
| 168 | + ObjectPostProcessor<Object> objectPostProcessor = this.spring.getContext() |
| 169 | + .getBean(ObjectPostProcessorConfig.class).objectPostProcessor; |
157 | 170 | verify(objectPostProcessor).postProcess(any(RequestMatcherDelegatingAuthorizationManager.class));
|
158 | 171 | verify(objectPostProcessor).postProcess(any(AuthorizationFilter.class));
|
159 | 172 | }
|
@@ -623,6 +636,20 @@ public void getWhenNotConfigAndNotAuthenticatedThenRespondsWithOk() throws Excep
|
623 | 636 | this.mvc.perform(requestWithUser).andExpect(status().isOk());
|
624 | 637 | }
|
625 | 638 |
|
| 639 | + @Test |
| 640 | + public void getWhenObservationRegistryThenObserves() throws Exception { |
| 641 | + this.spring.register(RoleUserConfig.class, BasicController.class, ObservationRegistryConfig.class).autowire(); |
| 642 | + ObservationHandler<Observation.Context> handler = this.spring.getContext().getBean(ObservationHandler.class); |
| 643 | + this.mvc.perform(get("/").with(user("user").roles("USER"))).andExpect(status().isOk()); |
| 644 | + ArgumentCaptor<Observation.Context> context = ArgumentCaptor.forClass(Observation.Context.class); |
| 645 | + verify(handler, atLeastOnce()).onStart(context.capture()); |
| 646 | + assertThat(context.getAllValues()).anyMatch((c) -> c instanceof AuthorizationObservationContext); |
| 647 | + verify(handler, atLeastOnce()).onStop(context.capture()); |
| 648 | + assertThat(context.getAllValues()).anyMatch((c) -> c instanceof AuthorizationObservationContext); |
| 649 | + this.mvc.perform(get("/").with(user("user").roles("WRONG"))).andExpect(status().isForbidden()); |
| 650 | + verify(handler).onError(any()); |
| 651 | + } |
| 652 | + |
626 | 653 | @Configuration
|
627 | 654 | @EnableWebSecurity
|
628 | 655 | static class GrantedAuthorityDefaultHasRoleConfig {
|
@@ -1015,6 +1042,12 @@ SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
1015 | 1042 | // @formatter:on
|
1016 | 1043 | }
|
1017 | 1044 |
|
| 1045 | + @Bean |
| 1046 | + UserDetailsService users() { |
| 1047 | + return new InMemoryUserDetailsManager( |
| 1048 | + User.withUsername("user").password("{noop}password").roles("USER").build()); |
| 1049 | + } |
| 1050 | + |
1018 | 1051 | }
|
1019 | 1052 |
|
1020 | 1053 | @Configuration
|
@@ -1212,4 +1245,47 @@ void rootPost() {
|
1212 | 1245 |
|
1213 | 1246 | }
|
1214 | 1247 |
|
| 1248 | + @Configuration |
| 1249 | + static class ObservationRegistryConfig { |
| 1250 | + |
| 1251 | + private final ObservationRegistry registry = ObservationRegistry.create(); |
| 1252 | + |
| 1253 | + private final ObservationHandler<Observation.Context> handler = spy(new ObservationTextPublisher()); |
| 1254 | + |
| 1255 | + @Bean |
| 1256 | + ObservationRegistry observationRegistry() { |
| 1257 | + return this.registry; |
| 1258 | + } |
| 1259 | + |
| 1260 | + @Bean |
| 1261 | + ObservationHandler<Observation.Context> observationHandler() { |
| 1262 | + return this.handler; |
| 1263 | + } |
| 1264 | + |
| 1265 | + @Bean |
| 1266 | + ObservationRegistryPostProcessor observationRegistryPostProcessor( |
| 1267 | + ObjectProvider<ObservationHandler<Observation.Context>> handler) { |
| 1268 | + return new ObservationRegistryPostProcessor(handler); |
| 1269 | + } |
| 1270 | + |
| 1271 | + } |
| 1272 | + |
| 1273 | + static class ObservationRegistryPostProcessor implements BeanPostProcessor { |
| 1274 | + |
| 1275 | + private final ObjectProvider<ObservationHandler<Observation.Context>> handler; |
| 1276 | + |
| 1277 | + ObservationRegistryPostProcessor(ObjectProvider<ObservationHandler<Observation.Context>> handler) { |
| 1278 | + this.handler = handler; |
| 1279 | + } |
| 1280 | + |
| 1281 | + @Override |
| 1282 | + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { |
| 1283 | + if (bean instanceof ObservationRegistry registry) { |
| 1284 | + registry.observationConfig().observationHandler(this.handler.getObject()); |
| 1285 | + } |
| 1286 | + return bean; |
| 1287 | + } |
| 1288 | + |
| 1289 | + } |
| 1290 | + |
1215 | 1291 | }
|
0 commit comments