Add Authorization Event Tests

- These ensure that the parameterized version of authorization events
can be listened to

Issue gh-16700
This commit is contained in:
Josh Cummings 2025-03-20 12:47:04 -06:00
parent ed79efc5fa
commit c91656c27a
2 changed files with 72 additions and 0 deletions

View File

@ -51,11 +51,13 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.AdviceMode; import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Role; import org.springframework.context.annotation.Role;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.AnnotationConfigurationException; import org.springframework.core.annotation.AnnotationConfigurationException;
import org.springframework.security.access.AccessDeniedException; import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.PermissionEvaluator; import org.springframework.security.access.PermissionEvaluator;
@ -76,6 +78,8 @@ import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationEventPublisher; import org.springframework.security.authorization.AuthorizationEventPublisher;
import org.springframework.security.authorization.AuthorizationManager; import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.authorization.AuthorizationResult; import org.springframework.security.authorization.AuthorizationResult;
import org.springframework.security.authorization.SpringAuthorizationEventPublisher;
import org.springframework.security.authorization.event.AuthorizationDeniedEvent;
import org.springframework.security.authorization.method.AuthorizationAdvisor; import org.springframework.security.authorization.method.AuthorizationAdvisor;
import org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory; import org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory;
import org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory.TargetVisitor; import org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory.TargetVisitor;
@ -1103,6 +1107,17 @@ public class PrePostMethodSecurityConfigurationTests {
verifyNoInteractions(handler); verifyNoInteractions(handler);
} }
@Test
@WithMockUser
public void preAuthorizeWhenDenyAllThenPublishesParameterizedAuthorizationDeniedEvent() {
this.spring
.register(MethodSecurityServiceConfig.class, EventPublisherConfig.class, AuthorizationDeniedListener.class)
.autowire();
assertThatExceptionOfType(AccessDeniedException.class)
.isThrownBy(() -> this.methodSecurityService.preAuthorize());
assertThat(this.spring.getContext().getBean(AuthorizationDeniedListener.class).invocations).isEqualTo(1);
}
private static Consumer<ConfigurableWebApplicationContext> disallowBeanOverriding() { private static Consumer<ConfigurableWebApplicationContext> disallowBeanOverriding() {
return (context) -> ((AnnotationConfigWebApplicationContext) context).setAllowBeanDefinitionOverriding(false); return (context) -> ((AnnotationConfigWebApplicationContext) context).setAllowBeanDefinitionOverriding(false);
} }
@ -1795,4 +1810,26 @@ public class PrePostMethodSecurityConfigurationTests {
} }
@Configuration
static class EventPublisherConfig {
@Bean
static AuthorizationEventPublisher eventPublisher(ApplicationEventPublisher publisher) {
return new SpringAuthorizationEventPublisher(publisher);
}
}
@Component
static class AuthorizationDeniedListener {
int invocations;
@EventListener
void onRequestDenied(AuthorizationDeniedEvent<? extends MethodInvocation> denied) {
this.invocations++;
}
}
} }

View File

@ -32,8 +32,10 @@ import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy; import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl; import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
import org.springframework.security.authentication.RememberMeAuthenticationToken; import org.springframework.security.authentication.RememberMeAuthenticationToken;
@ -43,6 +45,8 @@ import org.springframework.security.authorization.AuthorizationEventPublisher;
import org.springframework.security.authorization.AuthorizationManager; import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.authorization.AuthorizationObservationContext; import org.springframework.security.authorization.AuthorizationObservationContext;
import org.springframework.security.authorization.AuthorizationResult; import org.springframework.security.authorization.AuthorizationResult;
import org.springframework.security.authorization.SpringAuthorizationEventPublisher;
import org.springframework.security.authorization.event.AuthorizationDeniedEvent;
import org.springframework.security.config.ObjectPostProcessor; import org.springframework.security.config.ObjectPostProcessor;
import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry; import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity;
@ -66,6 +70,7 @@ import org.springframework.security.web.access.intercept.RequestAuthorizationCon
import org.springframework.security.web.access.intercept.RequestMatcherDelegatingAuthorizationManager; import org.springframework.security.web.access.intercept.RequestMatcherDelegatingAuthorizationManager;
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher; import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher; import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher;
import org.springframework.stereotype.Component;
import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.RequestPostProcessor; import org.springframework.test.web.servlet.request.RequestPostProcessor;
@ -670,6 +675,14 @@ public class AuthorizeHttpRequestsConfigurerTests {
verifyNoInteractions(handler); verifyNoInteractions(handler);
} }
@Test
public void getWhenDeniedThenParameterizedAuthorizationDeniedEventIsPublished() throws Exception {
this.spring.register(DenyAllConfig.class, EventPublisherConfig.class, AuthorizationDeniedListener.class)
.autowire();
this.mvc.perform(get("/").with(user("user")));
assertThat(this.spring.getContext().getBean(AuthorizationDeniedListener.class).invocations).isEqualTo(1);
}
@Test @Test
public void requestMatchersWhenMultipleDispatcherServletsAndPathBeanThenAllows() throws Exception { public void requestMatchersWhenMultipleDispatcherServletsAndPathBeanThenAllows() throws Exception {
this.spring.register(MvcRequestMatcherBuilderConfig.class, BasicController.class) this.spring.register(MvcRequestMatcherBuilderConfig.class, BasicController.class)
@ -1390,4 +1403,26 @@ public class AuthorizeHttpRequestsConfigurerTests {
} }
@Configuration
static class EventPublisherConfig {
@Bean
static AuthorizationEventPublisher eventPublisher(ApplicationEventPublisher publisher) {
return new SpringAuthorizationEventPublisher(publisher);
}
}
@Component
static class AuthorizationDeniedListener {
int invocations;
@EventListener
void onRequestDenied(AuthorizationDeniedEvent<? extends HttpServletRequest> denied) {
this.invocations++;
}
}
} }