Polish Authorization Event Support
- Added spring-security-config support - Renamed classes - Changed contracts to include the authenticated user and secured object - Added method security support Issue gh-9288
This commit is contained in:
parent
bd9434882f
commit
061f69eb70
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2021 the original author or authors.
|
* Copyright 2002-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -17,16 +17,16 @@
|
||||||
package org.springframework.security.config.annotation.method.configuration;
|
package org.springframework.security.config.annotation.method.configuration;
|
||||||
|
|
||||||
import org.springframework.aop.Advisor;
|
import org.springframework.aop.Advisor;
|
||||||
import org.springframework.beans.BeansException;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.config.BeanDefinition;
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
import org.springframework.context.ApplicationContext;
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.ApplicationContextAware;
|
|
||||||
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.Role;
|
import org.springframework.context.annotation.Role;
|
||||||
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
|
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
|
||||||
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
|
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
|
||||||
|
import org.springframework.security.authorization.AuthorizationEventPublisher;
|
||||||
|
import org.springframework.security.authorization.SpringAuthorizationEventPublisher;
|
||||||
import org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor;
|
import org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor;
|
||||||
import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
|
import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
|
||||||
import org.springframework.security.authorization.method.PostAuthorizeAuthorizationManager;
|
import org.springframework.security.authorization.method.PostAuthorizeAuthorizationManager;
|
||||||
|
|
@ -45,7 +45,7 @@ import org.springframework.security.config.core.GrantedAuthorityDefaults;
|
||||||
*/
|
*/
|
||||||
@Configuration(proxyBeanMethods = false)
|
@Configuration(proxyBeanMethods = false)
|
||||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||||
final class PrePostMethodSecurityConfiguration implements ApplicationContextAware {
|
final class PrePostMethodSecurityConfiguration {
|
||||||
|
|
||||||
private final PreFilterAuthorizationMethodInterceptor preFilterAuthorizationMethodInterceptor = new PreFilterAuthorizationMethodInterceptor();
|
private final PreFilterAuthorizationMethodInterceptor preFilterAuthorizationMethodInterceptor = new PreFilterAuthorizationMethodInterceptor();
|
||||||
|
|
||||||
|
|
@ -61,7 +61,8 @@ final class PrePostMethodSecurityConfiguration implements ApplicationContextAwar
|
||||||
|
|
||||||
private final DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
|
private final DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
|
||||||
|
|
||||||
PrePostMethodSecurityConfiguration() {
|
@Autowired
|
||||||
|
PrePostMethodSecurityConfiguration(ApplicationContext context) {
|
||||||
this.preAuthorizeAuthorizationManager.setExpressionHandler(this.expressionHandler);
|
this.preAuthorizeAuthorizationManager.setExpressionHandler(this.expressionHandler);
|
||||||
this.preAuthorizeAuthorizationMethodInterceptor = AuthorizationManagerBeforeMethodInterceptor
|
this.preAuthorizeAuthorizationMethodInterceptor = AuthorizationManagerBeforeMethodInterceptor
|
||||||
.preAuthorize(this.preAuthorizeAuthorizationManager);
|
.preAuthorize(this.preAuthorizeAuthorizationManager);
|
||||||
|
|
@ -70,6 +71,10 @@ final class PrePostMethodSecurityConfiguration implements ApplicationContextAwar
|
||||||
.postAuthorize(this.postAuthorizeAuthorizationManager);
|
.postAuthorize(this.postAuthorizeAuthorizationManager);
|
||||||
this.preFilterAuthorizationMethodInterceptor.setExpressionHandler(this.expressionHandler);
|
this.preFilterAuthorizationMethodInterceptor.setExpressionHandler(this.expressionHandler);
|
||||||
this.postFilterAuthorizationMethodInterceptor.setExpressionHandler(this.expressionHandler);
|
this.postFilterAuthorizationMethodInterceptor.setExpressionHandler(this.expressionHandler);
|
||||||
|
this.expressionHandler.setApplicationContext(context);
|
||||||
|
AuthorizationEventPublisher publisher = new SpringAuthorizationEventPublisher(context);
|
||||||
|
this.preAuthorizeAuthorizationMethodInterceptor.setAuthorizationEventPublisher(publisher);
|
||||||
|
this.postAuthorizeAuthorizaitonMethodInterceptor.setAuthorizationEventPublisher(publisher);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
|
|
@ -109,9 +114,10 @@ final class PrePostMethodSecurityConfiguration implements ApplicationContextAwar
|
||||||
this.expressionHandler.setDefaultRolePrefix(grantedAuthorityDefaults.getRolePrefix());
|
this.expressionHandler.setDefaultRolePrefix(grantedAuthorityDefaults.getRolePrefix());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Autowired(required = false)
|
||||||
public void setApplicationContext(ApplicationContext context) throws BeansException {
|
void setAuthorizationEventPublisher(AuthorizationEventPublisher eventPublisher) {
|
||||||
this.expressionHandler.setApplicationContext(context);
|
this.preAuthorizeAuthorizationMethodInterceptor.setAuthorizationEventPublisher(eventPublisher);
|
||||||
|
this.postAuthorizeAuthorizaitonMethodInterceptor.setAuthorizationEventPublisher(eventPublisher);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2021 the original author or authors.
|
* Copyright 2002-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -26,7 +26,9 @@ import org.springframework.http.HttpMethod;
|
||||||
import org.springframework.security.authorization.AuthenticatedAuthorizationManager;
|
import org.springframework.security.authorization.AuthenticatedAuthorizationManager;
|
||||||
import org.springframework.security.authorization.AuthorityAuthorizationManager;
|
import org.springframework.security.authorization.AuthorityAuthorizationManager;
|
||||||
import org.springframework.security.authorization.AuthorizationDecision;
|
import org.springframework.security.authorization.AuthorizationDecision;
|
||||||
|
import org.springframework.security.authorization.AuthorizationEventPublisher;
|
||||||
import org.springframework.security.authorization.AuthorizationManager;
|
import org.springframework.security.authorization.AuthorizationManager;
|
||||||
|
import org.springframework.security.authorization.SpringAuthorizationEventPublisher;
|
||||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||||
import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
|
import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
|
||||||
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
|
||||||
|
|
@ -52,12 +54,20 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
|
||||||
|
|
||||||
private final AuthorizationManagerRequestMatcherRegistry registry;
|
private final AuthorizationManagerRequestMatcherRegistry registry;
|
||||||
|
|
||||||
|
private final AuthorizationEventPublisher publisher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an instance.
|
* Creates an instance.
|
||||||
* @param context the {@link ApplicationContext} to use
|
* @param context the {@link ApplicationContext} to use
|
||||||
*/
|
*/
|
||||||
public AuthorizeHttpRequestsConfigurer(ApplicationContext context) {
|
public AuthorizeHttpRequestsConfigurer(ApplicationContext context) {
|
||||||
this.registry = new AuthorizationManagerRequestMatcherRegistry(context);
|
this.registry = new AuthorizationManagerRequestMatcherRegistry(context);
|
||||||
|
if (context.getBeanNamesForType(AuthorizationEventPublisher.class).length > 0) {
|
||||||
|
this.publisher = context.getBean(AuthorizationEventPublisher.class);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.publisher = new SpringAuthorizationEventPublisher(context);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -74,6 +84,7 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
|
||||||
public void configure(H http) {
|
public void configure(H http) {
|
||||||
AuthorizationManager<HttpServletRequest> authorizationManager = this.registry.createAuthorizationManager();
|
AuthorizationManager<HttpServletRequest> authorizationManager = this.registry.createAuthorizationManager();
|
||||||
AuthorizationFilter authorizationFilter = new AuthorizationFilter(authorizationManager);
|
AuthorizationFilter authorizationFilter = new AuthorizationFilter(authorizationManager);
|
||||||
|
authorizationFilter.setAuthorizationEventPublisher(this.publisher);
|
||||||
http.addFilter(postProcess(authorizationFilter));
|
http.addFilter(postProcess(authorizationFilter));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2021 the original author or authors.
|
* Copyright 2002-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -20,6 +20,7 @@ import java.io.Serializable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import org.aopalliance.intercept.MethodInterceptor;
|
import org.aopalliance.intercept.MethodInterceptor;
|
||||||
import org.aopalliance.intercept.MethodInvocation;
|
import org.aopalliance.intercept.MethodInvocation;
|
||||||
|
|
@ -32,6 +33,7 @@ import org.springframework.aop.support.JdkRegexpMethodPointcut;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.config.BeanDefinition;
|
import org.springframework.beans.factory.config.BeanDefinition;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.context.annotation.Role;
|
import org.springframework.context.annotation.Role;
|
||||||
import org.springframework.core.annotation.AnnotationConfigurationException;
|
import org.springframework.core.annotation.AnnotationConfigurationException;
|
||||||
import org.springframework.security.access.AccessDeniedException;
|
import org.springframework.security.access.AccessDeniedException;
|
||||||
|
|
@ -43,9 +45,11 @@ import org.springframework.security.access.annotation.Jsr250BusinessServiceImpl;
|
||||||
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
|
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
|
||||||
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
|
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
|
||||||
import org.springframework.security.authorization.AuthorizationDecision;
|
import org.springframework.security.authorization.AuthorizationDecision;
|
||||||
|
import org.springframework.security.authorization.AuthorizationEventPublisher;
|
||||||
import org.springframework.security.authorization.AuthorizationManager;
|
import org.springframework.security.authorization.AuthorizationManager;
|
||||||
import org.springframework.security.authorization.method.AuthorizationInterceptorsOrder;
|
import org.springframework.security.authorization.method.AuthorizationInterceptorsOrder;
|
||||||
import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
|
import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
|
||||||
|
import org.springframework.security.authorization.method.MethodInvocationResult;
|
||||||
import org.springframework.security.config.core.GrantedAuthorityDefaults;
|
import org.springframework.security.config.core.GrantedAuthorityDefaults;
|
||||||
import org.springframework.security.config.test.SpringTestContext;
|
import org.springframework.security.config.test.SpringTestContext;
|
||||||
import org.springframework.security.config.test.SpringTestContextExtension;
|
import org.springframework.security.config.test.SpringTestContextExtension;
|
||||||
|
|
@ -58,6 +62,9 @@ import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link PrePostMethodSecurityConfiguration}.
|
* Tests for {@link PrePostMethodSecurityConfiguration}.
|
||||||
|
|
@ -350,6 +357,27 @@ public class PrePostMethodSecurityConfigurationTests {
|
||||||
.isThrownBy(() -> this.businessService.repeatedAnnotations());
|
.isThrownBy(() -> this.businessService.repeatedAnnotations());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@WithMockUser
|
||||||
|
@Test
|
||||||
|
public void preAuthorizeWhenAuthorizationEventPublisherThenUses() {
|
||||||
|
this.spring.register(MethodSecurityServiceConfig.class, AuthorizationEventPublisherConfig.class).autowire();
|
||||||
|
assertThatExceptionOfType(AccessDeniedException.class)
|
||||||
|
.isThrownBy(() -> this.methodSecurityService.preAuthorize());
|
||||||
|
AuthorizationEventPublisher publisher = this.spring.getContext().getBean(AuthorizationEventPublisher.class);
|
||||||
|
verify(publisher).publishAuthorizationEvent(any(Supplier.class), any(MethodInvocation.class),
|
||||||
|
any(AuthorizationDecision.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@WithMockUser
|
||||||
|
@Test
|
||||||
|
public void postAuthorizeWhenAuthorizationEventPublisherThenUses() {
|
||||||
|
this.spring.register(MethodSecurityServiceConfig.class, AuthorizationEventPublisherConfig.class).autowire();
|
||||||
|
this.methodSecurityService.postAnnotation("grant");
|
||||||
|
AuthorizationEventPublisher publisher = this.spring.getContext().getBean(AuthorizationEventPublisher.class);
|
||||||
|
verify(publisher).publishAuthorizationEvent(any(Supplier.class), any(MethodInvocationResult.class),
|
||||||
|
any(AuthorizationDecision.class));
|
||||||
|
}
|
||||||
|
|
||||||
// gh-10305
|
// gh-10305
|
||||||
@WithMockUser
|
@WithMockUser
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -484,4 +512,16 @@ public class PrePostMethodSecurityConfigurationTests {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
static class AuthorizationEventPublisherConfig {
|
||||||
|
|
||||||
|
private final AuthorizationEventPublisher publisher = mock(AuthorizationEventPublisher.class);
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
AuthorizationEventPublisher authorizationEventPublisher() {
|
||||||
|
return this.publisher;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2021 the original author or authors.
|
* Copyright 2002-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -16,12 +16,19 @@
|
||||||
|
|
||||||
package org.springframework.security.config.annotation.web.configurers;
|
package org.springframework.security.config.annotation.web.configurers;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
|
||||||
import org.springframework.beans.factory.BeanCreationException;
|
import org.springframework.beans.factory.BeanCreationException;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
import org.springframework.security.authorization.AuthorizationDecision;
|
||||||
|
import org.springframework.security.authorization.AuthorizationEventPublisher;
|
||||||
import org.springframework.security.authorization.AuthorizationManager;
|
import org.springframework.security.authorization.AuthorizationManager;
|
||||||
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
import org.springframework.security.config.annotation.ObjectPostProcessor;
|
||||||
import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
|
import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
|
||||||
|
|
@ -129,9 +136,9 @@ public class AuthorizeHttpRequestsConfigurerTests {
|
||||||
@Test
|
@Test
|
||||||
public void configureWhenObjectPostProcessorRegisteredThenInvokedOnAuthorizationManagerAndAuthorizationFilter() {
|
public void configureWhenObjectPostProcessorRegisteredThenInvokedOnAuthorizationManagerAndAuthorizationFilter() {
|
||||||
this.spring.register(ObjectPostProcessorConfig.class).autowire();
|
this.spring.register(ObjectPostProcessorConfig.class).autowire();
|
||||||
verify(ObjectPostProcessorConfig.objectPostProcessor)
|
ObjectPostProcessor objectPostProcessor = this.spring.getContext().getBean(ObjectPostProcessor.class);
|
||||||
.postProcess(any(RequestMatcherDelegatingAuthorizationManager.class));
|
verify(objectPostProcessor).postProcess(any(RequestMatcherDelegatingAuthorizationManager.class));
|
||||||
verify(ObjectPostProcessorConfig.objectPostProcessor).postProcess(any(AuthorizationFilter.class));
|
verify(objectPostProcessor).postProcess(any(AuthorizationFilter.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -369,6 +376,15 @@ public class AuthorizeHttpRequestsConfigurerTests {
|
||||||
this.mvc.perform(get("/")).andExpect(status().isUnauthorized());
|
this.mvc.perform(get("/")).andExpect(status().isUnauthorized());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getWhenCustomAuthorizationEventPublisherThenUses() throws Exception {
|
||||||
|
this.spring.register(AuthenticatedConfig.class, AuthorizationEventPublisherConfig.class).autowire();
|
||||||
|
this.mvc.perform(get("/")).andExpect(status().isUnauthorized());
|
||||||
|
AuthorizationEventPublisher publisher = this.spring.getContext().getBean(AuthorizationEventPublisher.class);
|
||||||
|
verify(publisher).publishAuthorizationEvent(any(Supplier.class), any(HttpServletRequest.class),
|
||||||
|
any(AuthorizationDecision.class));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getWhenAnyRequestAuthenticatedConfiguredAndUserLoggedInThenRespondsWithOk() throws Exception {
|
public void getWhenAnyRequestAuthenticatedConfiguredAndUserLoggedInThenRespondsWithOk() throws Exception {
|
||||||
this.spring.register(AuthenticatedConfig.class, BasicController.class).autowire();
|
this.spring.register(AuthenticatedConfig.class, BasicController.class).autowire();
|
||||||
|
|
@ -495,7 +511,7 @@ public class AuthorizeHttpRequestsConfigurerTests {
|
||||||
@EnableWebSecurity
|
@EnableWebSecurity
|
||||||
static class ObjectPostProcessorConfig {
|
static class ObjectPostProcessorConfig {
|
||||||
|
|
||||||
static ObjectPostProcessor<Object> objectPostProcessor = spy(ReflectingObjectPostProcessor.class);
|
ObjectPostProcessor<Object> objectPostProcessor = spy(ReflectingObjectPostProcessor.class);
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
|
||||||
|
|
@ -509,8 +525,8 @@ public class AuthorizeHttpRequestsConfigurerTests {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
static ObjectPostProcessor<Object> objectPostProcessor() {
|
ObjectPostProcessor<Object> objectPostProcessor() {
|
||||||
return objectPostProcessor;
|
return this.objectPostProcessor;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -698,6 +714,18 @@ public class AuthorizeHttpRequestsConfigurerTests {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
static class AuthorizationEventPublisherConfig {
|
||||||
|
|
||||||
|
private final AuthorizationEventPublisher publisher = mock(AuthorizationEventPublisher.class);
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
AuthorizationEventPublisher authorizationEventPublisher() {
|
||||||
|
return this.publisher;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
static class BasicController {
|
static class BasicController {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2021 the original author or authors.
|
* Copyright 2002-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -16,14 +16,34 @@
|
||||||
|
|
||||||
package org.springframework.security.authorization;
|
package org.springframework.security.authorization;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import org.springframework.security.authorization.event.AuthorizationDeniedEvent;
|
||||||
|
import org.springframework.security.authorization.event.AuthorizationGrantedEvent;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* A contract for publishing authorization events
|
||||||
|
*
|
||||||
* @author Parikshit Dutta
|
* @author Parikshit Dutta
|
||||||
* @since 5.5
|
* @author Josh Cummings
|
||||||
|
* @since 5.7
|
||||||
|
* @see AuthorizationManager
|
||||||
*/
|
*/
|
||||||
public interface AuthorizationEventPublisher {
|
public interface AuthorizationEventPublisher {
|
||||||
|
|
||||||
void publishAuthorizationSuccess(AuthorizationDecision authorizationDecision);
|
/**
|
||||||
|
* Publish the given details in the form of an event, typically
|
||||||
void publishAuthorizationFailure(AuthorizationDecision authorizationDecision);
|
* {@link AuthorizationGrantedEvent} or {@link AuthorizationDeniedEvent}.
|
||||||
|
*
|
||||||
|
* Note that success events can be very noisy if enabled by default. Because of this
|
||||||
|
* implementations may choose to drop success events by default.
|
||||||
|
* @param authentication a {@link Supplier} for the current user
|
||||||
|
* @param object the secured object
|
||||||
|
* @param decision the decision about whether the user may access the secured object
|
||||||
|
* @param <T> the secured object's type
|
||||||
|
*/
|
||||||
|
<T> void publishAuthorizationEvent(Supplier<Authentication> authentication, T object,
|
||||||
|
AuthorizationDecision decision);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2002-2021 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.security.authorization;
|
|
||||||
|
|
||||||
import org.springframework.context.ApplicationEventPublisher;
|
|
||||||
import org.springframework.context.ApplicationEventPublisherAware;
|
|
||||||
import org.springframework.security.authorization.event.AuthorizationFailureEvent;
|
|
||||||
import org.springframework.security.authorization.event.AuthorizationSuccessEvent;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default implementation of {@link AuthorizationEventPublisher}
|
|
||||||
*
|
|
||||||
* @author Parikshit Dutta
|
|
||||||
* @since 5.5
|
|
||||||
*/
|
|
||||||
public class DefaultAuthorizationEventPublisher implements AuthorizationEventPublisher, ApplicationEventPublisherAware {
|
|
||||||
|
|
||||||
private ApplicationEventPublisher applicationEventPublisher;
|
|
||||||
|
|
||||||
public DefaultAuthorizationEventPublisher() {
|
|
||||||
this(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DefaultAuthorizationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
|
|
||||||
this.applicationEventPublisher = applicationEventPublisher;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
|
|
||||||
this.applicationEventPublisher = applicationEventPublisher;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void publishAuthorizationSuccess(AuthorizationDecision authorizationDecision) {
|
|
||||||
if (this.applicationEventPublisher != null) {
|
|
||||||
this.applicationEventPublisher.publishEvent(new AuthorizationSuccessEvent(authorizationDecision));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void publishAuthorizationFailure(AuthorizationDecision authorizationDecision) {
|
|
||||||
if (this.applicationEventPublisher != null) {
|
|
||||||
this.applicationEventPublisher.publishEvent(new AuthorizationFailureEvent(authorizationDecision));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2022 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.security.authorization;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import org.springframework.context.ApplicationEventPublisher;
|
||||||
|
import org.springframework.security.authorization.event.AuthorizationDeniedEvent;
|
||||||
|
import org.springframework.security.authorization.event.AuthorizationGrantedEvent;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An implementation of {@link AuthorizationEventPublisher} that uses Spring's event
|
||||||
|
* publishing support.
|
||||||
|
*
|
||||||
|
* Because {@link AuthorizationGrantedEvent}s typically require additional business logic
|
||||||
|
* to decide whether to publish, this implementation only publishes
|
||||||
|
* {@link AuthorizationDeniedEvent}s.
|
||||||
|
*
|
||||||
|
* @author Parikshit Dutta
|
||||||
|
* @author Josh Cummings
|
||||||
|
* @since 5.7
|
||||||
|
*/
|
||||||
|
public final class SpringAuthorizationEventPublisher implements AuthorizationEventPublisher {
|
||||||
|
|
||||||
|
private final ApplicationEventPublisher eventPublisher;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Construct this publisher using Spring's {@link ApplicationEventPublisher}
|
||||||
|
* @param eventPublisher
|
||||||
|
*/
|
||||||
|
public SpringAuthorizationEventPublisher(ApplicationEventPublisher eventPublisher) {
|
||||||
|
Assert.notNull(eventPublisher, "eventPublisher cannot be null");
|
||||||
|
this.eventPublisher = eventPublisher;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public <T> void publishAuthorizationEvent(Supplier<Authentication> authentication, T object,
|
||||||
|
AuthorizationDecision decision) {
|
||||||
|
if (decision == null || decision.isGranted()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
AuthorizationDeniedEvent<T> failure = new AuthorizationDeniedEvent<>(authentication, object, decision);
|
||||||
|
this.eventPublisher.publishEvent(failure);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2021 the original author or authors.
|
* Copyright 2002-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -16,19 +16,37 @@
|
||||||
|
|
||||||
package org.springframework.security.authorization.event;
|
package org.springframework.security.authorization.event;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import org.springframework.context.ApplicationEvent;
|
import org.springframework.context.ApplicationEvent;
|
||||||
import org.springframework.security.authorization.AuthorizationDecision;
|
import org.springframework.security.authorization.AuthorizationDecision;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An {@link ApplicationEvent} which indicates failed authorization.
|
* An {@link ApplicationEvent} which indicates failed authorization.
|
||||||
*
|
*
|
||||||
* @author Parikshit Dutta
|
* @author Parikshit Dutta
|
||||||
* @since 5.5
|
* @author Josh Cummings
|
||||||
|
* @since 5.7
|
||||||
*/
|
*/
|
||||||
public class AuthorizationFailureEvent extends ApplicationEvent {
|
public class AuthorizationDeniedEvent<T> extends ApplicationEvent {
|
||||||
|
|
||||||
public AuthorizationFailureEvent(AuthorizationDecision authorizationDecision) {
|
private final Supplier<Authentication> authentication;
|
||||||
super(authorizationDecision);
|
|
||||||
|
private final AuthorizationDecision decision;
|
||||||
|
|
||||||
|
public AuthorizationDeniedEvent(Supplier<Authentication> authentication, T object, AuthorizationDecision decision) {
|
||||||
|
super(object);
|
||||||
|
this.authentication = authentication;
|
||||||
|
this.decision = decision;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Supplier<Authentication> getAuthentication() {
|
||||||
|
return this.authentication;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuthorizationDecision getAuthorizationDecision() {
|
||||||
|
return this.decision;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2021 the original author or authors.
|
* Copyright 2002-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -16,19 +16,40 @@
|
||||||
|
|
||||||
package org.springframework.security.authorization.event;
|
package org.springframework.security.authorization.event;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import org.springframework.context.ApplicationEvent;
|
import org.springframework.context.ApplicationEvent;
|
||||||
import org.springframework.security.authorization.AuthorizationDecision;
|
import org.springframework.security.authorization.AuthorizationDecision;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An {@link ApplicationEvent} which indicates successful authorization.
|
* An {@link ApplicationEvent} which indicates successful authorization.
|
||||||
*
|
*
|
||||||
* @author Parikshit Dutta
|
* @author Parikshit Dutta
|
||||||
* @since 5.5
|
* @author Josh Cummings
|
||||||
|
* @since 5.7
|
||||||
*/
|
*/
|
||||||
public class AuthorizationSuccessEvent extends ApplicationEvent {
|
public class AuthorizationGrantedEvent<T> extends ApplicationEvent {
|
||||||
|
|
||||||
public AuthorizationSuccessEvent(AuthorizationDecision authorizationDecision) {
|
private final Supplier<Authentication> authentication;
|
||||||
super(authorizationDecision);
|
|
||||||
|
private final AuthorizationDecision decision;
|
||||||
|
|
||||||
|
public AuthorizationGrantedEvent(Supplier<Authentication> authentication, T object,
|
||||||
|
AuthorizationDecision decision) {
|
||||||
|
super(object);
|
||||||
|
Assert.notNull(authentication, "authentication supplier cannot be null");
|
||||||
|
this.authentication = authentication;
|
||||||
|
this.decision = decision;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Supplier<Authentication> getAuthentication() {
|
||||||
|
return this.authentication;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuthorizationDecision getAuthorizationDecision() {
|
||||||
|
return this.decision;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2021 the original author or authors.
|
* Copyright 2002-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -33,6 +33,7 @@ import org.springframework.security.access.AccessDeniedException;
|
||||||
import org.springframework.security.access.prepost.PostAuthorize;
|
import org.springframework.security.access.prepost.PostAuthorize;
|
||||||
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
|
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
|
||||||
import org.springframework.security.authorization.AuthorizationDecision;
|
import org.springframework.security.authorization.AuthorizationDecision;
|
||||||
|
import org.springframework.security.authorization.AuthorizationEventPublisher;
|
||||||
import org.springframework.security.authorization.AuthorizationManager;
|
import org.springframework.security.authorization.AuthorizationManager;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
|
@ -66,6 +67,8 @@ public final class AuthorizationManagerAfterMethodInterceptor
|
||||||
|
|
||||||
private int order;
|
private int order;
|
||||||
|
|
||||||
|
private AuthorizationEventPublisher eventPublisher = AuthorizationManagerAfterMethodInterceptor::noPublish;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an instance.
|
* Creates an instance.
|
||||||
* @param pointcut the {@link Pointcut} to use
|
* @param pointcut the {@link Pointcut} to use
|
||||||
|
|
@ -122,6 +125,17 @@ public final class AuthorizationManagerAfterMethodInterceptor
|
||||||
this.order = order;
|
this.order = order;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this {@link AuthorizationEventPublisher} to publish the
|
||||||
|
* {@link AuthorizationManager} result.
|
||||||
|
* @param eventPublisher
|
||||||
|
* @since 5.7
|
||||||
|
*/
|
||||||
|
public void setAuthorizationEventPublisher(AuthorizationEventPublisher eventPublisher) {
|
||||||
|
Assert.notNull(eventPublisher, "eventPublisher cannot be null");
|
||||||
|
this.eventPublisher = eventPublisher;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
|
|
@ -142,8 +156,9 @@ public final class AuthorizationManagerAfterMethodInterceptor
|
||||||
|
|
||||||
private void attemptAuthorization(MethodInvocation mi, Object result) {
|
private void attemptAuthorization(MethodInvocation mi, Object result) {
|
||||||
this.logger.debug(LogMessage.of(() -> "Authorizing method invocation " + mi));
|
this.logger.debug(LogMessage.of(() -> "Authorizing method invocation " + mi));
|
||||||
AuthorizationDecision decision = this.authorizationManager.check(AUTHENTICATION_SUPPLIER,
|
MethodInvocationResult object = new MethodInvocationResult(mi, result);
|
||||||
new MethodInvocationResult(mi, result));
|
AuthorizationDecision decision = this.authorizationManager.check(AUTHENTICATION_SUPPLIER, object);
|
||||||
|
this.eventPublisher.publishAuthorizationEvent(AUTHENTICATION_SUPPLIER, object, decision);
|
||||||
if (decision != null && !decision.isGranted()) {
|
if (decision != null && !decision.isGranted()) {
|
||||||
this.logger.debug(LogMessage.of(() -> "Failed to authorize " + mi + " with authorization manager "
|
this.logger.debug(LogMessage.of(() -> "Failed to authorize " + mi + " with authorization manager "
|
||||||
+ this.authorizationManager + " and decision " + decision));
|
+ this.authorizationManager + " and decision " + decision));
|
||||||
|
|
@ -152,4 +167,9 @@ public final class AuthorizationManagerAfterMethodInterceptor
|
||||||
this.logger.debug(LogMessage.of(() -> "Authorized method invocation " + mi));
|
this.logger.debug(LogMessage.of(() -> "Authorized method invocation " + mi));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static <T> void noPublish(Supplier<Authentication> authentication, T object,
|
||||||
|
AuthorizationDecision decision) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2021 the original author or authors.
|
* Copyright 2002-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -38,6 +38,7 @@ import org.springframework.security.access.annotation.Secured;
|
||||||
import org.springframework.security.access.prepost.PreAuthorize;
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
|
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
|
||||||
import org.springframework.security.authorization.AuthorizationDecision;
|
import org.springframework.security.authorization.AuthorizationDecision;
|
||||||
|
import org.springframework.security.authorization.AuthorizationEventPublisher;
|
||||||
import org.springframework.security.authorization.AuthorizationManager;
|
import org.springframework.security.authorization.AuthorizationManager;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
|
@ -71,6 +72,8 @@ public final class AuthorizationManagerBeforeMethodInterceptor
|
||||||
|
|
||||||
private int order = AuthorizationInterceptorsOrder.FIRST.getOrder();
|
private int order = AuthorizationInterceptorsOrder.FIRST.getOrder();
|
||||||
|
|
||||||
|
private AuthorizationEventPublisher eventPublisher = AuthorizationManagerBeforeMethodInterceptor::noPublish;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an instance.
|
* Creates an instance.
|
||||||
* @param pointcut the {@link Pointcut} to use
|
* @param pointcut the {@link Pointcut} to use
|
||||||
|
|
@ -168,6 +171,17 @@ public final class AuthorizationManagerBeforeMethodInterceptor
|
||||||
this.order = order;
|
this.order = order;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this {@link AuthorizationEventPublisher} to publish the
|
||||||
|
* {@link AuthorizationManager} result.
|
||||||
|
* @param eventPublisher
|
||||||
|
* @since 5.7
|
||||||
|
*/
|
||||||
|
public void setAuthorizationEventPublisher(AuthorizationEventPublisher eventPublisher) {
|
||||||
|
Assert.notNull(eventPublisher, "eventPublisher cannot be null");
|
||||||
|
this.eventPublisher = eventPublisher;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@inheritDoc}
|
* {@inheritDoc}
|
||||||
*/
|
*/
|
||||||
|
|
@ -189,6 +203,7 @@ public final class AuthorizationManagerBeforeMethodInterceptor
|
||||||
private void attemptAuthorization(MethodInvocation mi) {
|
private void attemptAuthorization(MethodInvocation mi) {
|
||||||
this.logger.debug(LogMessage.of(() -> "Authorizing method invocation " + mi));
|
this.logger.debug(LogMessage.of(() -> "Authorizing method invocation " + mi));
|
||||||
AuthorizationDecision decision = this.authorizationManager.check(AUTHENTICATION_SUPPLIER, mi);
|
AuthorizationDecision decision = this.authorizationManager.check(AUTHENTICATION_SUPPLIER, mi);
|
||||||
|
this.eventPublisher.publishAuthorizationEvent(AUTHENTICATION_SUPPLIER, mi, decision);
|
||||||
if (decision != null && !decision.isGranted()) {
|
if (decision != null && !decision.isGranted()) {
|
||||||
this.logger.debug(LogMessage.of(() -> "Failed to authorize " + mi + " with authorization manager "
|
this.logger.debug(LogMessage.of(() -> "Failed to authorize " + mi + " with authorization manager "
|
||||||
+ this.authorizationManager + " and decision " + decision));
|
+ this.authorizationManager + " and decision " + decision));
|
||||||
|
|
@ -197,4 +212,9 @@ public final class AuthorizationManagerBeforeMethodInterceptor
|
||||||
this.logger.debug(LogMessage.of(() -> "Authorized method invocation " + mi));
|
this.logger.debug(LogMessage.of(() -> "Authorized method invocation " + mi));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static <T> void noPublish(Supplier<Authentication> authentication, T object,
|
||||||
|
AuthorizationDecision decision) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,70 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2002-2021 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.security.authorization;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import org.springframework.context.ApplicationEventPublisher;
|
|
||||||
import org.springframework.security.authorization.event.AuthorizationFailureEvent;
|
|
||||||
import org.springframework.security.authorization.event.AuthorizationSuccessEvent;
|
|
||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
|
||||||
import static org.mockito.ArgumentMatchers.isA;
|
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
import static org.mockito.Mockito.never;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests for {@link DefaultAuthorizationEventPublisher}
|
|
||||||
*
|
|
||||||
* @author Parikshit Dutta
|
|
||||||
*/
|
|
||||||
public class DefaultAuthorizationEventPublisherTests {
|
|
||||||
|
|
||||||
ApplicationEventPublisher applicationEventPublisher;
|
|
||||||
|
|
||||||
DefaultAuthorizationEventPublisher authorizationEventPublisher;
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
public void init() {
|
|
||||||
this.applicationEventPublisher = mock(ApplicationEventPublisher.class);
|
|
||||||
this.authorizationEventPublisher = new DefaultAuthorizationEventPublisher();
|
|
||||||
this.authorizationEventPublisher.setApplicationEventPublisher(this.applicationEventPublisher);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAuthenticationSuccessIsPublished() {
|
|
||||||
this.authorizationEventPublisher.publishAuthorizationSuccess(mock(AuthorizationDecision.class));
|
|
||||||
verify(this.applicationEventPublisher).publishEvent(isA(AuthorizationSuccessEvent.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAuthenticationFailureIsPublished() {
|
|
||||||
this.authorizationEventPublisher.publishAuthorizationFailure(mock(AuthorizationDecision.class));
|
|
||||||
verify(this.applicationEventPublisher).publishEvent(isA(AuthorizationFailureEvent.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testNullPublisherNotInvoked() {
|
|
||||||
this.authorizationEventPublisher.setApplicationEventPublisher(null);
|
|
||||||
this.authorizationEventPublisher.publishAuthorizationSuccess(mock(AuthorizationDecision.class));
|
|
||||||
this.authorizationEventPublisher.publishAuthorizationFailure(mock(AuthorizationDecision.class));
|
|
||||||
verify(this.applicationEventPublisher, never()).publishEvent(any());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2022 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.security.authorization;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import org.springframework.context.ApplicationEventPublisher;
|
||||||
|
import org.springframework.security.authentication.TestAuthentication;
|
||||||
|
import org.springframework.security.authorization.event.AuthorizationDeniedEvent;
|
||||||
|
import org.springframework.security.core.Authentication;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.isA;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.verifyNoInteractions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link SpringAuthorizationEventPublisher}
|
||||||
|
*
|
||||||
|
* @author Parikshit Dutta
|
||||||
|
*/
|
||||||
|
public class SpringAuthorizationEventPublisherTests {
|
||||||
|
|
||||||
|
Supplier<Authentication> authentication = () -> TestAuthentication.authenticatedUser();
|
||||||
|
|
||||||
|
ApplicationEventPublisher applicationEventPublisher;
|
||||||
|
|
||||||
|
SpringAuthorizationEventPublisher authorizationEventPublisher;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void init() {
|
||||||
|
this.applicationEventPublisher = mock(ApplicationEventPublisher.class);
|
||||||
|
this.authorizationEventPublisher = new SpringAuthorizationEventPublisher(this.applicationEventPublisher);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAuthenticationSuccessIsNotPublished() {
|
||||||
|
AuthorizationDecision decision = new AuthorizationDecision(true);
|
||||||
|
this.authorizationEventPublisher.publishAuthorizationEvent(this.authentication, mock(Object.class), decision);
|
||||||
|
verifyNoInteractions(this.applicationEventPublisher);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAuthenticationFailureIsPublished() {
|
||||||
|
AuthorizationDecision decision = new AuthorizationDecision(false);
|
||||||
|
this.authorizationEventPublisher.publishAuthorizationEvent(this.authentication, mock(Object.class), decision);
|
||||||
|
verify(this.applicationEventPublisher).publishEvent(isA(AuthorizationDeniedEvent.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2021 the original author or authors.
|
* Copyright 2002-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -16,11 +16,20 @@
|
||||||
|
|
||||||
package org.springframework.security.authorization.method;
|
package org.springframework.security.authorization.method;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import org.aopalliance.intercept.MethodInvocation;
|
import org.aopalliance.intercept.MethodInvocation;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import org.springframework.aop.Pointcut;
|
import org.springframework.aop.Pointcut;
|
||||||
|
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||||
|
import org.springframework.security.authorization.AuthenticatedAuthorizationManager;
|
||||||
|
import org.springframework.security.authorization.AuthorizationDecision;
|
||||||
|
import org.springframework.security.authorization.AuthorizationEventPublisher;
|
||||||
import org.springframework.security.authorization.AuthorizationManager;
|
import org.springframework.security.authorization.AuthorizationManager;
|
||||||
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.core.context.SecurityContextImpl;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||||
|
|
@ -66,4 +75,32 @@ public class AuthorizationManagerAfterMethodInterceptorTests {
|
||||||
any(MethodInvocationResult.class));
|
any(MethodInvocationResult.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void configureWhenAuthorizationEventPublisherIsNullThenIllegalArgument() {
|
||||||
|
AuthorizationManagerAfterMethodInterceptor advice = new AuthorizationManagerAfterMethodInterceptor(
|
||||||
|
Pointcut.TRUE, AuthenticatedAuthorizationManager.authenticated());
|
||||||
|
assertThatIllegalArgumentException().isThrownBy(() -> advice.setAuthorizationEventPublisher(null))
|
||||||
|
.withMessage("eventPublisher cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void invokeWhenAuthorizationEventPublisherThenUses() throws Throwable {
|
||||||
|
AuthorizationManagerAfterMethodInterceptor advice = new AuthorizationManagerAfterMethodInterceptor(
|
||||||
|
Pointcut.TRUE, AuthenticatedAuthorizationManager.authenticated());
|
||||||
|
AuthorizationEventPublisher eventPublisher = mock(AuthorizationEventPublisher.class);
|
||||||
|
advice.setAuthorizationEventPublisher(eventPublisher);
|
||||||
|
|
||||||
|
SecurityContext securityContext = new SecurityContextImpl();
|
||||||
|
securityContext.setAuthentication(new TestingAuthenticationToken("user", "password", "ROLE_USER"));
|
||||||
|
SecurityContextHolder.setContext(securityContext);
|
||||||
|
|
||||||
|
MethodInvocation mockMethodInvocation = mock(MethodInvocation.class);
|
||||||
|
MethodInvocationResult result = new MethodInvocationResult(mockMethodInvocation, new Object());
|
||||||
|
given(mockMethodInvocation.proceed()).willReturn(result.getResult());
|
||||||
|
|
||||||
|
advice.invoke(mockMethodInvocation);
|
||||||
|
verify(eventPublisher).publishAuthorizationEvent(any(Supplier.class), any(MethodInvocationResult.class),
|
||||||
|
any(AuthorizationDecision.class));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2021 the original author or authors.
|
* Copyright 2002-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -16,13 +16,24 @@
|
||||||
|
|
||||||
package org.springframework.security.authorization.method;
|
package org.springframework.security.authorization.method;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import org.aopalliance.intercept.MethodInvocation;
|
import org.aopalliance.intercept.MethodInvocation;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import org.springframework.aop.Pointcut;
|
import org.springframework.aop.Pointcut;
|
||||||
|
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||||
|
import org.springframework.security.authorization.AuthenticatedAuthorizationManager;
|
||||||
|
import org.springframework.security.authorization.AuthorizationDecision;
|
||||||
|
import org.springframework.security.authorization.AuthorizationEventPublisher;
|
||||||
import org.springframework.security.authorization.AuthorizationManager;
|
import org.springframework.security.authorization.AuthorizationManager;
|
||||||
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
|
import org.springframework.security.core.context.SecurityContextImpl;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.BDDMockito.given;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
|
@ -59,4 +70,32 @@ public class AuthorizationManagerBeforeMethodInterceptorTests {
|
||||||
mockMethodInvocation);
|
mockMethodInvocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void configureWhenAuthorizationEventPublisherIsNullThenIllegalArgument() {
|
||||||
|
AuthorizationManagerBeforeMethodInterceptor advice = new AuthorizationManagerBeforeMethodInterceptor(
|
||||||
|
Pointcut.TRUE, AuthenticatedAuthorizationManager.authenticated());
|
||||||
|
assertThatIllegalArgumentException().isThrownBy(() -> advice.setAuthorizationEventPublisher(null))
|
||||||
|
.withMessage("eventPublisher cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void invokeWhenAuthorizationEventPublisherThenUses() throws Throwable {
|
||||||
|
AuthorizationManagerBeforeMethodInterceptor advice = new AuthorizationManagerBeforeMethodInterceptor(
|
||||||
|
Pointcut.TRUE, AuthenticatedAuthorizationManager.authenticated());
|
||||||
|
AuthorizationEventPublisher eventPublisher = mock(AuthorizationEventPublisher.class);
|
||||||
|
advice.setAuthorizationEventPublisher(eventPublisher);
|
||||||
|
|
||||||
|
SecurityContext securityContext = new SecurityContextImpl();
|
||||||
|
securityContext.setAuthentication(new TestingAuthenticationToken("user", "password", "ROLE_USER"));
|
||||||
|
SecurityContextHolder.setContext(securityContext);
|
||||||
|
|
||||||
|
MethodInvocation mockMethodInvocation = mock(MethodInvocation.class);
|
||||||
|
MethodInvocationResult result = new MethodInvocationResult(mockMethodInvocation, new Object());
|
||||||
|
given(mockMethodInvocation.proceed()).willReturn(result.getResult());
|
||||||
|
|
||||||
|
advice.invoke(mockMethodInvocation);
|
||||||
|
verify(eventPublisher).publishAuthorizationEvent(any(Supplier.class), any(MethodInvocation.class),
|
||||||
|
any(AuthorizationDecision.class));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2021 the original author or authors.
|
* Copyright 2002-2022 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -17,14 +17,21 @@
|
||||||
package org.springframework.security.web.access.intercept;
|
package org.springframework.security.web.access.intercept;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import javax.servlet.FilterChain;
|
import javax.servlet.FilterChain;
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.springframework.context.ApplicationEventPublisher;
|
||||||
|
import org.springframework.security.access.AccessDeniedException;
|
||||||
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
|
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
|
||||||
|
import org.springframework.security.authorization.AuthorizationDecision;
|
||||||
|
import org.springframework.security.authorization.AuthorizationEventPublisher;
|
||||||
import org.springframework.security.authorization.AuthorizationManager;
|
import org.springframework.security.authorization.AuthorizationManager;
|
||||||
|
import org.springframework.security.authorization.event.AuthorizationDeniedEvent;
|
||||||
|
import org.springframework.security.authorization.event.AuthorizationGrantedEvent;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
@ -41,6 +48,8 @@ public class AuthorizationFilter extends OncePerRequestFilter {
|
||||||
|
|
||||||
private final AuthorizationManager<HttpServletRequest> authorizationManager;
|
private final AuthorizationManager<HttpServletRequest> authorizationManager;
|
||||||
|
|
||||||
|
private AuthorizationEventPublisher eventPublisher = AuthorizationFilter::noPublish;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an instance.
|
* Creates an instance.
|
||||||
* @param authorizationManager the {@link AuthorizationManager} to use
|
* @param authorizationManager the {@link AuthorizationManager} to use
|
||||||
|
|
@ -54,7 +63,11 @@ public class AuthorizationFilter extends OncePerRequestFilter {
|
||||||
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
|
||||||
throws ServletException, IOException {
|
throws ServletException, IOException {
|
||||||
|
|
||||||
this.authorizationManager.verify(this::getAuthentication, request);
|
AuthorizationDecision decision = this.authorizationManager.check(this::getAuthentication, request);
|
||||||
|
this.eventPublisher.publishAuthorizationEvent(this::getAuthentication, request, decision);
|
||||||
|
if (decision != null && !decision.isGranted()) {
|
||||||
|
throw new AccessDeniedException("Access Denied");
|
||||||
|
}
|
||||||
filterChain.doFilter(request, response);
|
filterChain.doFilter(request, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -67,6 +80,17 @@ public class AuthorizationFilter extends OncePerRequestFilter {
|
||||||
return authentication;
|
return authentication;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use this {@link AuthorizationEventPublisher} to publish
|
||||||
|
* {@link AuthorizationDeniedEvent}s and {@link AuthorizationGrantedEvent}s.
|
||||||
|
* @param eventPublisher the {@link ApplicationEventPublisher} to use
|
||||||
|
* @since 5.7
|
||||||
|
*/
|
||||||
|
public void setAuthorizationEventPublisher(AuthorizationEventPublisher eventPublisher) {
|
||||||
|
Assert.notNull(eventPublisher, "eventPublisher cannot be null");
|
||||||
|
this.eventPublisher = eventPublisher;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the {@link AuthorizationManager} used by this filter
|
* Gets the {@link AuthorizationManager} used by this filter
|
||||||
* @return the {@link AuthorizationManager}
|
* @return the {@link AuthorizationManager}
|
||||||
|
|
@ -75,4 +99,9 @@ public class AuthorizationFilter extends OncePerRequestFilter {
|
||||||
return this.authorizationManager;
|
return this.authorizationManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static <T> void noPublish(Supplier<Authentication> authentication, T object,
|
||||||
|
AuthorizationDecision decision) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,6 @@ import org.apache.commons.logging.LogFactory;
|
||||||
|
|
||||||
import org.springframework.core.log.LogMessage;
|
import org.springframework.core.log.LogMessage;
|
||||||
import org.springframework.security.authorization.AuthorizationDecision;
|
import org.springframework.security.authorization.AuthorizationDecision;
|
||||||
import org.springframework.security.authorization.AuthorizationEventPublisher;
|
|
||||||
import org.springframework.security.authorization.AuthorizationManager;
|
import org.springframework.security.authorization.AuthorizationManager;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.web.util.matcher.RequestMatcher;
|
import org.springframework.security.web.util.matcher.RequestMatcher;
|
||||||
|
|
@ -49,8 +48,6 @@ public final class RequestMatcherDelegatingAuthorizationManager implements Autho
|
||||||
|
|
||||||
private final Map<RequestMatcher, AuthorizationManager<RequestAuthorizationContext>> mappings;
|
private final Map<RequestMatcher, AuthorizationManager<RequestAuthorizationContext>> mappings;
|
||||||
|
|
||||||
private AuthorizationEventPublisher authorizationEventPublisher;
|
|
||||||
|
|
||||||
private RequestMatcherDelegatingAuthorizationManager(
|
private RequestMatcherDelegatingAuthorizationManager(
|
||||||
Map<RequestMatcher, AuthorizationManager<RequestAuthorizationContext>> mappings) {
|
Map<RequestMatcher, AuthorizationManager<RequestAuthorizationContext>> mappings) {
|
||||||
Assert.notEmpty(mappings, "mappings cannot be empty");
|
Assert.notEmpty(mappings, "mappings cannot be empty");
|
||||||
|
|
@ -81,36 +78,14 @@ public final class RequestMatcherDelegatingAuthorizationManager implements Autho
|
||||||
if (this.logger.isTraceEnabled()) {
|
if (this.logger.isTraceEnabled()) {
|
||||||
this.logger.trace(LogMessage.format("Checking authorization on %s using %s", request, manager));
|
this.logger.trace(LogMessage.format("Checking authorization on %s using %s", request, manager));
|
||||||
}
|
}
|
||||||
AuthorizationDecision authorizationDecision = manager.check(authentication,
|
return manager.check(authentication,
|
||||||
new RequestAuthorizationContext(request, matchResult.getVariables()));
|
new RequestAuthorizationContext(request, matchResult.getVariables()));
|
||||||
publishAuthorizationEvent(authorizationDecision);
|
|
||||||
return authorizationDecision;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.logger.trace("Abstaining since did not find matching RequestMatcher");
|
this.logger.trace("Abstaining since did not find matching RequestMatcher");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void publishAuthorizationEvent(AuthorizationDecision authorizationDecision) {
|
|
||||||
if (this.authorizationEventPublisher != null) {
|
|
||||||
if (authorizationDecision.isGranted()) {
|
|
||||||
this.authorizationEventPublisher.publishAuthorizationSuccess(authorizationDecision);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this.authorizationEventPublisher.publishAuthorizationFailure(authorizationDecision);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set implementation of an {@link AuthorizationEventPublisher}
|
|
||||||
* @param authorizationEventPublisher
|
|
||||||
*/
|
|
||||||
public void setAuthorizationEventPublisher(AuthorizationEventPublisher authorizationEventPublisher) {
|
|
||||||
Assert.notNull(authorizationEventPublisher, "AuthorizationEventPublisher cannot be null");
|
|
||||||
this.authorizationEventPublisher = authorizationEventPublisher;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a builder for {@link RequestMatcherDelegatingAuthorizationManager}.
|
* Creates a builder for {@link RequestMatcherDelegatingAuthorizationManager}.
|
||||||
* @return the new {@link Builder} instance
|
* @return the new {@link Builder} instance
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,8 @@ import org.springframework.security.access.AccessDeniedException;
|
||||||
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
|
import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException;
|
||||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||||
import org.springframework.security.authorization.AuthenticatedAuthorizationManager;
|
import org.springframework.security.authorization.AuthenticatedAuthorizationManager;
|
||||||
|
import org.springframework.security.authorization.AuthorizationDecision;
|
||||||
|
import org.springframework.security.authorization.AuthorizationEventPublisher;
|
||||||
import org.springframework.security.authorization.AuthorizationManager;
|
import org.springframework.security.authorization.AuthorizationManager;
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.core.context.SecurityContext;
|
import org.springframework.security.core.context.SecurityContext;
|
||||||
|
|
@ -39,8 +41,10 @@ import org.springframework.security.core.context.SecurityContextImpl;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.BDDMockito.given;
|
||||||
import static org.mockito.BDDMockito.willThrow;
|
import static org.mockito.BDDMockito.willThrow;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
@ -61,6 +65,8 @@ public class AuthorizationFilterTests {
|
||||||
@Test
|
@Test
|
||||||
public void filterWhenAuthorizationManagerVerifyPassesThenNextFilter() throws Exception {
|
public void filterWhenAuthorizationManagerVerifyPassesThenNextFilter() throws Exception {
|
||||||
AuthorizationManager<HttpServletRequest> mockAuthorizationManager = mock(AuthorizationManager.class);
|
AuthorizationManager<HttpServletRequest> mockAuthorizationManager = mock(AuthorizationManager.class);
|
||||||
|
given(mockAuthorizationManager.check(any(Supplier.class), any(HttpServletRequest.class)))
|
||||||
|
.willReturn(new AuthorizationDecision(true));
|
||||||
AuthorizationFilter filter = new AuthorizationFilter(mockAuthorizationManager);
|
AuthorizationFilter filter = new AuthorizationFilter(mockAuthorizationManager);
|
||||||
TestingAuthenticationToken authenticationToken = new TestingAuthenticationToken("user", "password");
|
TestingAuthenticationToken authenticationToken = new TestingAuthenticationToken("user", "password");
|
||||||
|
|
||||||
|
|
@ -75,7 +81,7 @@ public class AuthorizationFilterTests {
|
||||||
filter.doFilter(mockRequest, mockResponse, mockFilterChain);
|
filter.doFilter(mockRequest, mockResponse, mockFilterChain);
|
||||||
|
|
||||||
ArgumentCaptor<Supplier<Authentication>> authenticationCaptor = ArgumentCaptor.forClass(Supplier.class);
|
ArgumentCaptor<Supplier<Authentication>> authenticationCaptor = ArgumentCaptor.forClass(Supplier.class);
|
||||||
verify(mockAuthorizationManager).verify(authenticationCaptor.capture(), eq(mockRequest));
|
verify(mockAuthorizationManager).check(authenticationCaptor.capture(), eq(mockRequest));
|
||||||
Supplier<Authentication> authentication = authenticationCaptor.getValue();
|
Supplier<Authentication> authentication = authenticationCaptor.getValue();
|
||||||
assertThat(authentication.get()).isEqualTo(authenticationToken);
|
assertThat(authentication.get()).isEqualTo(authenticationToken);
|
||||||
|
|
||||||
|
|
@ -96,7 +102,7 @@ public class AuthorizationFilterTests {
|
||||||
MockHttpServletResponse mockResponse = new MockHttpServletResponse();
|
MockHttpServletResponse mockResponse = new MockHttpServletResponse();
|
||||||
FilterChain mockFilterChain = mock(FilterChain.class);
|
FilterChain mockFilterChain = mock(FilterChain.class);
|
||||||
|
|
||||||
willThrow(new AccessDeniedException("Access Denied")).given(mockAuthorizationManager).verify(any(),
|
willThrow(new AccessDeniedException("Access Denied")).given(mockAuthorizationManager).check(any(),
|
||||||
eq(mockRequest));
|
eq(mockRequest));
|
||||||
|
|
||||||
assertThatExceptionOfType(AccessDeniedException.class)
|
assertThatExceptionOfType(AccessDeniedException.class)
|
||||||
|
|
@ -104,7 +110,7 @@ public class AuthorizationFilterTests {
|
||||||
.withMessage("Access Denied");
|
.withMessage("Access Denied");
|
||||||
|
|
||||||
ArgumentCaptor<Supplier<Authentication>> authenticationCaptor = ArgumentCaptor.forClass(Supplier.class);
|
ArgumentCaptor<Supplier<Authentication>> authenticationCaptor = ArgumentCaptor.forClass(Supplier.class);
|
||||||
verify(mockAuthorizationManager).verify(authenticationCaptor.capture(), eq(mockRequest));
|
verify(mockAuthorizationManager).check(authenticationCaptor.capture(), eq(mockRequest));
|
||||||
Supplier<Authentication> authentication = authenticationCaptor.getValue();
|
Supplier<Authentication> authentication = authenticationCaptor.getValue();
|
||||||
assertThat(authentication.get()).isEqualTo(authenticationToken);
|
assertThat(authentication.get()).isEqualTo(authenticationToken);
|
||||||
|
|
||||||
|
|
@ -132,4 +138,31 @@ public class AuthorizationFilterTests {
|
||||||
assertThat(authorizationFilter.getAuthorizationManager()).isSameAs(authorizationManager);
|
assertThat(authorizationFilter.getAuthorizationManager()).isSameAs(authorizationManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void configureWhenAuthorizationEventPublisherIsNullThenIllegalArgument() {
|
||||||
|
AuthorizationManager<HttpServletRequest> authorizationManager = mock(AuthorizationManager.class);
|
||||||
|
AuthorizationFilter authorizationFilter = new AuthorizationFilter(authorizationManager);
|
||||||
|
assertThatIllegalArgumentException().isThrownBy(() -> authorizationFilter.setAuthorizationEventPublisher(null))
|
||||||
|
.withMessage("eventPublisher cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void doFilterWhenAuthorizationEventPublisherThenUses() throws Exception {
|
||||||
|
AuthorizationFilter authorizationFilter = new AuthorizationFilter(
|
||||||
|
AuthenticatedAuthorizationManager.authenticated());
|
||||||
|
MockHttpServletRequest mockRequest = new MockHttpServletRequest(null, "/path");
|
||||||
|
MockHttpServletResponse mockResponse = new MockHttpServletResponse();
|
||||||
|
FilterChain mockFilterChain = mock(FilterChain.class);
|
||||||
|
|
||||||
|
SecurityContext securityContext = new SecurityContextImpl();
|
||||||
|
securityContext.setAuthentication(new TestingAuthenticationToken("user", "password", "ROLE_USER"));
|
||||||
|
SecurityContextHolder.setContext(securityContext);
|
||||||
|
|
||||||
|
AuthorizationEventPublisher eventPublisher = mock(AuthorizationEventPublisher.class);
|
||||||
|
authorizationFilter.setAuthorizationEventPublisher(eventPublisher);
|
||||||
|
authorizationFilter.doFilter(mockRequest, mockResponse, mockFilterChain);
|
||||||
|
verify(eventPublisher).publishAuthorizationEvent(any(Supplier.class), any(HttpServletRequest.class),
|
||||||
|
any(AuthorizationDecision.class));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,15 +24,12 @@ import org.springframework.mock.web.MockHttpServletRequest;
|
||||||
import org.springframework.security.authentication.TestingAuthenticationToken;
|
import org.springframework.security.authentication.TestingAuthenticationToken;
|
||||||
import org.springframework.security.authorization.AuthorityAuthorizationManager;
|
import org.springframework.security.authorization.AuthorityAuthorizationManager;
|
||||||
import org.springframework.security.authorization.AuthorizationDecision;
|
import org.springframework.security.authorization.AuthorizationDecision;
|
||||||
import org.springframework.security.authorization.AuthorizationEventPublisher;
|
|
||||||
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.Authentication;
|
||||||
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
|
import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher;
|
||||||
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
|
import org.springframework.security.web.util.matcher.AnyRequestMatcher;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link RequestMatcherDelegatingAuthorizationManager}.
|
* Tests for {@link RequestMatcherDelegatingAuthorizationManager}.
|
||||||
|
|
@ -126,40 +123,4 @@ public class RequestMatcherDelegatingAuthorizationManagerTests {
|
||||||
.withMessage("mappingsConsumer cannot be null");
|
.withMessage("mappingsConsumer cannot be null");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAuthorizationEventPublisherIsNotNull() {
|
|
||||||
RequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder()
|
|
||||||
.add(new MvcRequestMatcher(null, "/grant"), (a, o) -> new AuthorizationDecision(true)).build();
|
|
||||||
assertThatIllegalArgumentException().isThrownBy(() -> manager.setAuthorizationEventPublisher(null))
|
|
||||||
.withMessage("AuthorizationEventPublisher cannot be null");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAuthorizationSuccessEventWhenAuthorizationGranted() {
|
|
||||||
RequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder()
|
|
||||||
.add(new MvcRequestMatcher(null, "/grant"), (a, o) -> new AuthorizationDecision(true)).build();
|
|
||||||
|
|
||||||
AuthorizationEventPublisher authorizationEventPublisher = mock(AuthorizationEventPublisher.class);
|
|
||||||
manager.setAuthorizationEventPublisher(authorizationEventPublisher);
|
|
||||||
|
|
||||||
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_USER");
|
|
||||||
|
|
||||||
AuthorizationDecision grant = manager.check(authentication, new MockHttpServletRequest(null, "/grant"));
|
|
||||||
verify(authorizationEventPublisher).publishAuthorizationSuccess(grant);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testAuthorizationFailureEventWhenAuthorizationNotGranted() {
|
|
||||||
RequestMatcherDelegatingAuthorizationManager manager = RequestMatcherDelegatingAuthorizationManager.builder()
|
|
||||||
.add(new MvcRequestMatcher(null, "/deny"), (a, o) -> new AuthorizationDecision(false)).build();
|
|
||||||
|
|
||||||
AuthorizationEventPublisher authorizationEventPublisher = mock(AuthorizationEventPublisher.class);
|
|
||||||
manager.setAuthorizationEventPublisher(authorizationEventPublisher);
|
|
||||||
|
|
||||||
Supplier<Authentication> authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_USER");
|
|
||||||
|
|
||||||
AuthorizationDecision grant = manager.check(authentication, new MockHttpServletRequest(null, "/deny"));
|
|
||||||
verify(authorizationEventPublisher).publishAuthorizationFailure(grant);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue