Polish Method Authorization Denied Handling
- Renamed @AuthorizationDeniedHandler to @HandleAuthorizationDenied - Merged the post processor interface into MethodAuthorizationDeniedHandler , it now has two methods handleDeniedInvocation and handleDeniedInvocationResult - @HandleAuthorizationDenied now handles AuthorizationDeniedException thrown from the method Issue gh-14601
This commit is contained in:
@@ -39,10 +39,9 @@ import org.springframework.security.access.prepost.PostFilter;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.access.prepost.PreFilter;
|
||||
import org.springframework.security.authorization.AuthorizationResult;
|
||||
import org.springframework.security.authorization.method.AuthorizationDeniedHandler;
|
||||
import org.springframework.security.authorization.method.AuthorizeReturnObject;
|
||||
import org.springframework.security.authorization.method.HandleAuthorizationDenied;
|
||||
import org.springframework.security.authorization.method.MethodAuthorizationDeniedHandler;
|
||||
import org.springframework.security.authorization.method.MethodAuthorizationDeniedPostProcessor;
|
||||
import org.springframework.security.authorization.method.MethodInvocationResult;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
@@ -129,32 +128,32 @@ public interface MethodSecurityService {
|
||||
void repeatedAnnotations();
|
||||
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
@AuthorizationDeniedHandler(handlerClass = StarMaskingHandler.class)
|
||||
@HandleAuthorizationDenied(handlerClass = StarMaskingHandler.class)
|
||||
String preAuthorizeGetCardNumberIfAdmin(String cardNumber);
|
||||
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
@AuthorizationDeniedHandler(handlerClass = StartMaskingHandlerChild.class)
|
||||
@HandleAuthorizationDenied(handlerClass = StartMaskingHandlerChild.class)
|
||||
String preAuthorizeWithHandlerChildGetCardNumberIfAdmin(String cardNumber);
|
||||
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
@AuthorizationDeniedHandler(handlerClass = StarMaskingHandler.class)
|
||||
@HandleAuthorizationDenied(handlerClass = StarMaskingHandler.class)
|
||||
String preAuthorizeThrowAccessDeniedManually();
|
||||
|
||||
@PostAuthorize("hasRole('ADMIN')")
|
||||
@AuthorizationDeniedHandler(postProcessorClass = CardNumberMaskingPostProcessor.class)
|
||||
@HandleAuthorizationDenied(handlerClass = CardNumberMaskingPostProcessor.class)
|
||||
String postAuthorizeGetCardNumberIfAdmin(String cardNumber);
|
||||
|
||||
@PostAuthorize("hasRole('ADMIN')")
|
||||
@AuthorizationDeniedHandler(postProcessorClass = PostMaskingPostProcessor.class)
|
||||
@HandleAuthorizationDenied(handlerClass = PostMaskingPostProcessor.class)
|
||||
String postAuthorizeThrowAccessDeniedManually();
|
||||
|
||||
@PreAuthorize("denyAll()")
|
||||
@Mask("methodmask")
|
||||
@AuthorizationDeniedHandler(handlerClass = MaskAnnotationHandler.class)
|
||||
@HandleAuthorizationDenied(handlerClass = MaskAnnotationHandler.class)
|
||||
String preAuthorizeDeniedMethodWithMaskAnnotation();
|
||||
|
||||
@PreAuthorize("denyAll()")
|
||||
@AuthorizationDeniedHandler(handlerClass = MaskAnnotationHandler.class)
|
||||
@HandleAuthorizationDenied(handlerClass = MaskAnnotationHandler.class)
|
||||
String preAuthorizeDeniedMethodWithNoMaskAnnotation();
|
||||
|
||||
@NullDenied(role = "ADMIN")
|
||||
@@ -162,40 +161,39 @@ public interface MethodSecurityService {
|
||||
|
||||
@PostAuthorize("denyAll()")
|
||||
@Mask("methodmask")
|
||||
@AuthorizationDeniedHandler(postProcessorClass = MaskAnnotationPostProcessor.class)
|
||||
@HandleAuthorizationDenied(handlerClass = MaskAnnotationPostProcessor.class)
|
||||
String postAuthorizeDeniedMethodWithMaskAnnotation();
|
||||
|
||||
@PostAuthorize("denyAll()")
|
||||
@AuthorizationDeniedHandler(postProcessorClass = MaskAnnotationPostProcessor.class)
|
||||
@HandleAuthorizationDenied(handlerClass = MaskAnnotationPostProcessor.class)
|
||||
String postAuthorizeDeniedMethodWithNoMaskAnnotation();
|
||||
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
@Mask(expression = "@myMasker.getMask()")
|
||||
@AuthorizationDeniedHandler(handlerClass = MaskAnnotationHandler.class)
|
||||
@HandleAuthorizationDenied(handlerClass = MaskAnnotationHandler.class)
|
||||
String preAuthorizeWithMaskAnnotationUsingBean();
|
||||
|
||||
@PostAuthorize("hasRole('ADMIN')")
|
||||
@Mask(expression = "@myMasker.getMask(returnObject)")
|
||||
@AuthorizationDeniedHandler(postProcessorClass = MaskAnnotationPostProcessor.class)
|
||||
@HandleAuthorizationDenied(handlerClass = MaskAnnotationPostProcessor.class)
|
||||
String postAuthorizeWithMaskAnnotationUsingBean();
|
||||
|
||||
@AuthorizeReturnObject
|
||||
UserRecordWithEmailProtected getUserRecordWithEmailProtected();
|
||||
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
@AuthorizationDeniedHandler(handlerClass = UserFallbackDeniedHandler.class)
|
||||
@HandleAuthorizationDenied(handlerClass = UserFallbackDeniedHandler.class)
|
||||
UserRecordWithEmailProtected getUserWithFallbackWhenUnauthorized();
|
||||
|
||||
@PreAuthorize("@authz.checkResult(#result)")
|
||||
@PostAuthorize("@authz.checkResult(!#result)")
|
||||
@AuthorizationDeniedHandler(handlerClass = MethodAuthorizationDeniedHandler.class,
|
||||
postProcessorClass = MethodAuthorizationDeniedPostProcessor.class)
|
||||
@HandleAuthorizationDenied(handlerClass = MethodAuthorizationDeniedHandler.class)
|
||||
String checkCustomResult(boolean result);
|
||||
|
||||
class StarMaskingHandler implements MethodAuthorizationDeniedHandler {
|
||||
|
||||
@Override
|
||||
public Object handle(MethodInvocation methodInvocation, AuthorizationResult result) {
|
||||
public Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult result) {
|
||||
return "***";
|
||||
}
|
||||
|
||||
@@ -204,8 +202,8 @@ public interface MethodSecurityService {
|
||||
class StartMaskingHandlerChild extends StarMaskingHandler {
|
||||
|
||||
@Override
|
||||
public Object handle(MethodInvocation methodInvocation, AuthorizationResult result) {
|
||||
return super.handle(methodInvocation, result) + "-child";
|
||||
public Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult result) {
|
||||
return super.handleDeniedInvocation(methodInvocation, result) + "-child";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -218,7 +216,6 @@ public interface MethodSecurityService {
|
||||
this.maskValueResolver = new MaskValueResolver(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object handle(MethodInvocation methodInvocation, AuthorizationResult result) {
|
||||
Mask mask = AnnotationUtils.getAnnotation(methodInvocation.getMethod(), Mask.class);
|
||||
if (mask == null) {
|
||||
@@ -227,9 +224,15 @@ public interface MethodSecurityService {
|
||||
return this.maskValueResolver.resolveValue(mask, methodInvocation, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object handleDeniedInvocation(MethodInvocation methodInvocation,
|
||||
AuthorizationResult authorizationResult) {
|
||||
return handle(methodInvocation, authorizationResult);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class MaskAnnotationPostProcessor implements MethodAuthorizationDeniedPostProcessor {
|
||||
class MaskAnnotationPostProcessor implements MethodAuthorizationDeniedHandler {
|
||||
|
||||
MaskValueResolver maskValueResolver;
|
||||
|
||||
@@ -238,7 +241,16 @@ public interface MethodSecurityService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessResult(MethodInvocationResult methodInvocationResult,
|
||||
public Object handleDeniedInvocation(MethodInvocation mi, AuthorizationResult authorizationResult) {
|
||||
Mask mask = AnnotationUtils.getAnnotation(mi.getMethod(), Mask.class);
|
||||
if (mask == null) {
|
||||
mask = AnnotationUtils.getAnnotation(mi.getMethod().getDeclaringClass(), Mask.class);
|
||||
}
|
||||
return this.maskValueResolver.resolveValue(mask, mi, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object handleDeniedInvocationResult(MethodInvocationResult methodInvocationResult,
|
||||
AuthorizationResult authorizationResult) {
|
||||
MethodInvocation mi = methodInvocationResult.getMethodInvocation();
|
||||
Mask mask = AnnotationUtils.getAnnotation(mi.getMethod(), Mask.class);
|
||||
@@ -274,31 +286,38 @@ public interface MethodSecurityService {
|
||||
|
||||
}
|
||||
|
||||
class PostMaskingPostProcessor implements MethodAuthorizationDeniedPostProcessor {
|
||||
class PostMaskingPostProcessor implements MethodAuthorizationDeniedHandler {
|
||||
|
||||
@Override
|
||||
public Object postProcessResult(MethodInvocationResult contextObject, AuthorizationResult result) {
|
||||
public Object handleDeniedInvocation(MethodInvocation methodInvocation,
|
||||
AuthorizationResult authorizationResult) {
|
||||
return "***";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class CardNumberMaskingPostProcessor implements MethodAuthorizationDeniedPostProcessor {
|
||||
class CardNumberMaskingPostProcessor implements MethodAuthorizationDeniedHandler {
|
||||
|
||||
static String MASK = "****-****-****-";
|
||||
|
||||
@Override
|
||||
public Object postProcessResult(MethodInvocationResult contextObject, AuthorizationResult result) {
|
||||
public Object handleDeniedInvocation(MethodInvocation methodInvocation,
|
||||
AuthorizationResult authorizationResult) {
|
||||
return "***";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object handleDeniedInvocationResult(MethodInvocationResult contextObject, AuthorizationResult result) {
|
||||
String cardNumber = (String) contextObject.getResult();
|
||||
return MASK + cardNumber.substring(cardNumber.length() - 4);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class NullPostProcessor implements MethodAuthorizationDeniedPostProcessor {
|
||||
class NullPostProcessor implements MethodAuthorizationDeniedHandler {
|
||||
|
||||
@Override
|
||||
public Object postProcessResult(MethodInvocationResult methodInvocationResult,
|
||||
public Object handleDeniedInvocation(MethodInvocation methodInvocation,
|
||||
AuthorizationResult authorizationResult) {
|
||||
return null;
|
||||
}
|
||||
@@ -320,7 +339,7 @@ public interface MethodSecurityService {
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@PostAuthorize("hasRole('{role}')")
|
||||
@AuthorizationDeniedHandler(postProcessorClass = NullPostProcessor.class)
|
||||
@HandleAuthorizationDenied(handlerClass = NullPostProcessor.class)
|
||||
@interface NullDenied {
|
||||
|
||||
String role();
|
||||
@@ -333,7 +352,8 @@ public interface MethodSecurityService {
|
||||
"Protected");
|
||||
|
||||
@Override
|
||||
public Object handle(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
|
||||
public Object handleDeniedInvocation(MethodInvocation methodInvocation,
|
||||
AuthorizationResult authorizationResult) {
|
||||
return FALLBACK;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,8 @@ package org.springframework.security.config.annotation.method.configuration;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.authorization.AuthorizationDecision;
|
||||
import org.springframework.security.authorization.AuthorizationDeniedException;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
|
||||
@@ -144,12 +145,12 @@ public class MethodSecurityServiceImpl implements MethodSecurityService {
|
||||
|
||||
@Override
|
||||
public String preAuthorizeThrowAccessDeniedManually() {
|
||||
throw new AccessDeniedException("Access Denied");
|
||||
throw new AuthorizationDeniedException("Access Denied", new AuthorizationDecision(false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String postAuthorizeThrowAccessDeniedManually() {
|
||||
throw new AccessDeniedException("Access Denied");
|
||||
throw new AuthorizationDeniedException("Access Denied", new AuthorizationDecision(false));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -67,7 +67,6 @@ import org.springframework.security.authorization.method.AuthorizationIntercepto
|
||||
import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
|
||||
import org.springframework.security.authorization.method.AuthorizeReturnObject;
|
||||
import org.springframework.security.authorization.method.MethodAuthorizationDeniedHandler;
|
||||
import org.springframework.security.authorization.method.MethodAuthorizationDeniedPostProcessor;
|
||||
import org.springframework.security.authorization.method.MethodInvocationResult;
|
||||
import org.springframework.security.authorization.method.PrePostTemplateDefaults;
|
||||
import org.springframework.security.config.Customizer;
|
||||
@@ -92,10 +91,10 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.assertj.core.api.Assertions.assertThatNoException;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.clearInvocations;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
||||
/**
|
||||
* Tests for {@link PrePostMethodSecurityConfiguration}.
|
||||
@@ -783,12 +782,21 @@ public class PrePostMethodSecurityConfigurationTests {
|
||||
|
||||
@Test
|
||||
@WithMockUser(roles = "ADMIN")
|
||||
void preAuthorizeWhenHandlerAndAccessDeniedNotThrownFromPreAuthorizeThenNotHandled() {
|
||||
void preAuthorizeWhenHandlerAndAccessDeniedNotThrownFromPreAuthorizeThenHandled() {
|
||||
this.spring.register(MethodSecurityServiceEnabledConfig.class, MethodSecurityService.StarMaskingHandler.class)
|
||||
.autowire();
|
||||
MethodSecurityService service = this.spring.getContext().getBean(MethodSecurityService.class);
|
||||
assertThatExceptionOfType(AccessDeniedException.class)
|
||||
.isThrownBy(service::preAuthorizeThrowAccessDeniedManually);
|
||||
assertThat(service.preAuthorizeThrowAccessDeniedManually()).isEqualTo("***");
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(roles = "ADMIN")
|
||||
void postAuthorizeWhenHandlerAndAccessDeniedNotThrownFromPostAuthorizeThenHandled() {
|
||||
this.spring
|
||||
.register(MethodSecurityServiceEnabledConfig.class, MethodSecurityService.PostMaskingPostProcessor.class)
|
||||
.autowire();
|
||||
MethodSecurityService service = this.spring.getContext().getBean(MethodSecurityService.class);
|
||||
assertThat(service.postAuthorizeThrowAccessDeniedManually()).isEqualTo("***");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -813,17 +821,6 @@ public class PrePostMethodSecurityConfigurationTests {
|
||||
assertThat(result).isEqualTo("classmask");
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(roles = "ADMIN")
|
||||
void postAuthorizeWhenHandlerAndAccessDeniedNotThrownFromPostAuthorizeThenNotHandled() {
|
||||
this.spring
|
||||
.register(MethodSecurityServiceEnabledConfig.class, MethodSecurityService.PostMaskingPostProcessor.class)
|
||||
.autowire();
|
||||
MethodSecurityService service = this.spring.getContext().getBean(MethodSecurityService.class);
|
||||
assertThatExceptionOfType(AccessDeniedException.class)
|
||||
.isThrownBy(service::postAuthorizeThrowAccessDeniedManually);
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser
|
||||
void postAuthorizeWhenNullDeniedMetaAnnotationThanWorks() {
|
||||
@@ -938,14 +935,13 @@ public class PrePostMethodSecurityConfigurationTests {
|
||||
MethodSecurityService service = this.spring.getContext().getBean(MethodSecurityService.class);
|
||||
MethodAuthorizationDeniedHandler handler = this.spring.getContext()
|
||||
.getBean(MethodAuthorizationDeniedHandler.class);
|
||||
MethodAuthorizationDeniedPostProcessor postProcessor = this.spring.getContext()
|
||||
.getBean(MethodAuthorizationDeniedPostProcessor.class);
|
||||
assertThat(service.checkCustomResult(false)).isNull();
|
||||
verify(handler).handle(any(), any(Authz.AuthzResult.class));
|
||||
verifyNoInteractions(postProcessor);
|
||||
verify(handler).handleDeniedInvocation(any(), any(Authz.AuthzResult.class));
|
||||
verify(handler, never()).handleDeniedInvocationResult(any(), any(Authz.AuthzResult.class));
|
||||
clearInvocations(handler);
|
||||
assertThat(service.checkCustomResult(true)).isNull();
|
||||
verify(postProcessor).postProcessResult(any(), any(Authz.AuthzResult.class));
|
||||
verifyNoMoreInteractions(handler);
|
||||
verify(handler).handleDeniedInvocationResult(any(), any(Authz.AuthzResult.class));
|
||||
verify(handler, never()).handleDeniedInvocation(any(), any(Authz.AuthzResult.class));
|
||||
}
|
||||
|
||||
private static Consumer<ConfigurableWebApplicationContext> disallowBeanOverriding() {
|
||||
@@ -1477,18 +1473,11 @@ public class PrePostMethodSecurityConfigurationTests {
|
||||
|
||||
MethodAuthorizationDeniedHandler handler = mock(MethodAuthorizationDeniedHandler.class);
|
||||
|
||||
MethodAuthorizationDeniedPostProcessor postProcessor = mock(MethodAuthorizationDeniedPostProcessor.class);
|
||||
|
||||
@Bean
|
||||
MethodAuthorizationDeniedHandler methodAuthorizationDeniedHandler() {
|
||||
return this.handler;
|
||||
}
|
||||
|
||||
@Bean
|
||||
MethodAuthorizationDeniedPostProcessor methodAuthorizationDeniedPostProcessor() {
|
||||
return this.postProcessor;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.config.test.SpringTestContext;
|
||||
import org.springframework.security.config.test.SpringTestContextExtension;
|
||||
import org.springframework.security.test.context.annotation.SecurityTestExecutionListeners;
|
||||
@@ -73,18 +72,6 @@ public class PrePostReactiveMethodSecurityConfigurationTests {
|
||||
.verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(roles = "ADMIN")
|
||||
void preAuthorizeWhenHandlerAndAccessDeniedNotThrownFromPreAuthorizeThenNotHandled() {
|
||||
this.spring
|
||||
.register(MethodSecurityServiceEnabledConfig.class, ReactiveMethodSecurityService.StarMaskingHandler.class)
|
||||
.autowire();
|
||||
ReactiveMethodSecurityService service = this.spring.getContext().getBean(ReactiveMethodSecurityService.class);
|
||||
StepVerifier.create(service.preAuthorizeThrowAccessDeniedManually())
|
||||
.expectError(AccessDeniedException.class)
|
||||
.verify();
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser
|
||||
void preAuthorizeWhenDeniedAndHandlerWithCustomAnnotationThenHandlerCanUseMaskFromOtherAnnotation() {
|
||||
@@ -119,9 +106,17 @@ public class PrePostReactiveMethodSecurityConfigurationTests {
|
||||
ReactiveMethodSecurityService.PostMaskingPostProcessor.class)
|
||||
.autowire();
|
||||
ReactiveMethodSecurityService service = this.spring.getContext().getBean(ReactiveMethodSecurityService.class);
|
||||
StepVerifier.create(service.postAuthorizeThrowAccessDeniedManually())
|
||||
.expectError(AccessDeniedException.class)
|
||||
.verify();
|
||||
StepVerifier.create(service.postAuthorizeThrowAccessDeniedManually()).expectNext("***").verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithMockUser(roles = "ADMIN")
|
||||
void preAuthorizeWhenHandlerAndAccessDeniedNotThrownFromPreAuthorizeThenHandled() {
|
||||
this.spring
|
||||
.register(MethodSecurityServiceEnabledConfig.class, ReactiveMethodSecurityService.StarMaskingHandler.class)
|
||||
.autowire();
|
||||
ReactiveMethodSecurityService service = this.spring.getContext().getBean(ReactiveMethodSecurityService.class);
|
||||
StepVerifier.create(service.preAuthorizeThrowAccessDeniedManually()).expectNext("***").verifyComplete();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -48,7 +48,6 @@ import org.springframework.security.authorization.method.AuthorizationAdvisorPro
|
||||
import org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory.TargetVisitor;
|
||||
import org.springframework.security.authorization.method.AuthorizeReturnObject;
|
||||
import org.springframework.security.authorization.method.MethodAuthorizationDeniedHandler;
|
||||
import org.springframework.security.authorization.method.MethodAuthorizationDeniedPostProcessor;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.core.GrantedAuthorityDefaults;
|
||||
import org.springframework.security.config.test.SpringTestContext;
|
||||
@@ -60,10 +59,10 @@ import org.springframework.security.test.context.support.WithMockUser;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.clearInvocations;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
|
||||
/**
|
||||
* @author Tadaya Tsuyukubo
|
||||
@@ -227,14 +226,13 @@ public class ReactiveMethodSecurityConfigurationTests {
|
||||
ReactiveMethodSecurityService service = this.spring.getContext().getBean(ReactiveMethodSecurityService.class);
|
||||
MethodAuthorizationDeniedHandler handler = this.spring.getContext()
|
||||
.getBean(MethodAuthorizationDeniedHandler.class);
|
||||
MethodAuthorizationDeniedPostProcessor postProcessor = this.spring.getContext()
|
||||
.getBean(MethodAuthorizationDeniedPostProcessor.class);
|
||||
assertThat(service.checkCustomResult(false).block()).isNull();
|
||||
verify(handler).handle(any(), any(Authz.AuthzResult.class));
|
||||
verifyNoInteractions(postProcessor);
|
||||
verify(handler).handleDeniedInvocation(any(), any(Authz.AuthzResult.class));
|
||||
verify(handler, never()).handleDeniedInvocationResult(any(), any(Authz.AuthzResult.class));
|
||||
clearInvocations(handler);
|
||||
assertThat(service.checkCustomResult(true).block()).isNull();
|
||||
verify(postProcessor).postProcessResult(any(), any(Authz.AuthzResult.class));
|
||||
verifyNoMoreInteractions(handler);
|
||||
verify(handler).handleDeniedInvocationResult(any(), any(Authz.AuthzResult.class));
|
||||
verify(handler, never()).handleDeniedInvocation(any(), any(Authz.AuthzResult.class));
|
||||
}
|
||||
|
||||
private static Consumer<User.UserBuilder> authorities(String... authorities) {
|
||||
@@ -383,18 +381,11 @@ public class ReactiveMethodSecurityConfigurationTests {
|
||||
|
||||
MethodAuthorizationDeniedHandler handler = mock(MethodAuthorizationDeniedHandler.class);
|
||||
|
||||
MethodAuthorizationDeniedPostProcessor postProcessor = mock(MethodAuthorizationDeniedPostProcessor.class);
|
||||
|
||||
@Bean
|
||||
MethodAuthorizationDeniedHandler methodAuthorizationDeniedHandler() {
|
||||
return this.handler;
|
||||
}
|
||||
|
||||
@Bean
|
||||
MethodAuthorizationDeniedPostProcessor methodAuthorizationDeniedPostProcessor() {
|
||||
return this.postProcessor;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -33,9 +33,8 @@ import org.springframework.security.access.expression.method.DefaultMethodSecuri
|
||||
import org.springframework.security.access.prepost.PostAuthorize;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.authorization.AuthorizationResult;
|
||||
import org.springframework.security.authorization.method.AuthorizationDeniedHandler;
|
||||
import org.springframework.security.authorization.method.HandleAuthorizationDenied;
|
||||
import org.springframework.security.authorization.method.MethodAuthorizationDeniedHandler;
|
||||
import org.springframework.security.authorization.method.MethodAuthorizationDeniedPostProcessor;
|
||||
import org.springframework.security.authorization.method.MethodInvocationResult;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -47,32 +46,32 @@ import org.springframework.util.StringUtils;
|
||||
public interface ReactiveMethodSecurityService {
|
||||
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
@AuthorizationDeniedHandler(handlerClass = StarMaskingHandler.class)
|
||||
@HandleAuthorizationDenied(handlerClass = StarMaskingHandler.class)
|
||||
Mono<String> preAuthorizeGetCardNumberIfAdmin(String cardNumber);
|
||||
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
@AuthorizationDeniedHandler(handlerClass = StartMaskingHandlerChild.class)
|
||||
@HandleAuthorizationDenied(handlerClass = StartMaskingHandlerChild.class)
|
||||
Mono<String> preAuthorizeWithHandlerChildGetCardNumberIfAdmin(String cardNumber);
|
||||
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
@AuthorizationDeniedHandler(handlerClass = StarMaskingHandler.class)
|
||||
@HandleAuthorizationDenied(handlerClass = StarMaskingHandler.class)
|
||||
Mono<String> preAuthorizeThrowAccessDeniedManually();
|
||||
|
||||
@PostAuthorize("hasRole('ADMIN')")
|
||||
@AuthorizationDeniedHandler(postProcessorClass = CardNumberMaskingPostProcessor.class)
|
||||
@HandleAuthorizationDenied(handlerClass = CardNumberMaskingPostProcessor.class)
|
||||
Mono<String> postAuthorizeGetCardNumberIfAdmin(String cardNumber);
|
||||
|
||||
@PostAuthorize("hasRole('ADMIN')")
|
||||
@AuthorizationDeniedHandler(postProcessorClass = PostMaskingPostProcessor.class)
|
||||
@HandleAuthorizationDenied(handlerClass = PostMaskingPostProcessor.class)
|
||||
Mono<String> postAuthorizeThrowAccessDeniedManually();
|
||||
|
||||
@PreAuthorize("denyAll()")
|
||||
@Mask("methodmask")
|
||||
@AuthorizationDeniedHandler(handlerClass = MaskAnnotationHandler.class)
|
||||
@HandleAuthorizationDenied(handlerClass = MaskAnnotationHandler.class)
|
||||
Mono<String> preAuthorizeDeniedMethodWithMaskAnnotation();
|
||||
|
||||
@PreAuthorize("denyAll()")
|
||||
@AuthorizationDeniedHandler(handlerClass = MaskAnnotationHandler.class)
|
||||
@HandleAuthorizationDenied(handlerClass = MaskAnnotationHandler.class)
|
||||
Mono<String> preAuthorizeDeniedMethodWithNoMaskAnnotation();
|
||||
|
||||
@NullDenied(role = "ADMIN")
|
||||
@@ -80,33 +79,32 @@ public interface ReactiveMethodSecurityService {
|
||||
|
||||
@PostAuthorize("denyAll()")
|
||||
@Mask("methodmask")
|
||||
@AuthorizationDeniedHandler(postProcessorClass = MaskAnnotationPostProcessor.class)
|
||||
@HandleAuthorizationDenied(handlerClass = MaskAnnotationPostProcessor.class)
|
||||
Mono<String> postAuthorizeDeniedMethodWithMaskAnnotation();
|
||||
|
||||
@PostAuthorize("denyAll()")
|
||||
@AuthorizationDeniedHandler(postProcessorClass = MaskAnnotationPostProcessor.class)
|
||||
@HandleAuthorizationDenied(handlerClass = MaskAnnotationPostProcessor.class)
|
||||
Mono<String> postAuthorizeDeniedMethodWithNoMaskAnnotation();
|
||||
|
||||
@PreAuthorize("hasRole('ADMIN')")
|
||||
@Mask(expression = "@myMasker.getMask()")
|
||||
@AuthorizationDeniedHandler(handlerClass = MaskAnnotationHandler.class)
|
||||
@HandleAuthorizationDenied(handlerClass = MaskAnnotationHandler.class)
|
||||
Mono<String> preAuthorizeWithMaskAnnotationUsingBean();
|
||||
|
||||
@PostAuthorize("hasRole('ADMIN')")
|
||||
@Mask(expression = "@myMasker.getMask(returnObject)")
|
||||
@AuthorizationDeniedHandler(postProcessorClass = MaskAnnotationPostProcessor.class)
|
||||
@HandleAuthorizationDenied(handlerClass = MaskAnnotationPostProcessor.class)
|
||||
Mono<String> postAuthorizeWithMaskAnnotationUsingBean();
|
||||
|
||||
@PreAuthorize("@authz.checkReactiveResult(#result)")
|
||||
@PostAuthorize("@authz.checkReactiveResult(!#result)")
|
||||
@AuthorizationDeniedHandler(handlerClass = MethodAuthorizationDeniedHandler.class,
|
||||
postProcessorClass = MethodAuthorizationDeniedPostProcessor.class)
|
||||
@HandleAuthorizationDenied(handlerClass = MethodAuthorizationDeniedHandler.class)
|
||||
Mono<String> checkCustomResult(boolean result);
|
||||
|
||||
class StarMaskingHandler implements MethodAuthorizationDeniedHandler {
|
||||
|
||||
@Override
|
||||
public Object handle(MethodInvocation methodInvocation, AuthorizationResult result) {
|
||||
public Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult result) {
|
||||
return "***";
|
||||
}
|
||||
|
||||
@@ -115,8 +113,8 @@ public interface ReactiveMethodSecurityService {
|
||||
class StartMaskingHandlerChild extends StarMaskingHandler {
|
||||
|
||||
@Override
|
||||
public Object handle(MethodInvocation methodInvocation, AuthorizationResult result) {
|
||||
return super.handle(methodInvocation, result) + "-child";
|
||||
public Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult result) {
|
||||
return super.handleDeniedInvocation(methodInvocation, result) + "-child";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -130,7 +128,7 @@ public interface ReactiveMethodSecurityService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object handle(MethodInvocation methodInvocation, AuthorizationResult result) {
|
||||
public Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult result) {
|
||||
Mask mask = AnnotationUtils.getAnnotation(methodInvocation.getMethod(), Mask.class);
|
||||
if (mask == null) {
|
||||
mask = AnnotationUtils.getAnnotation(methodInvocation.getMethod().getDeclaringClass(), Mask.class);
|
||||
@@ -140,7 +138,7 @@ public interface ReactiveMethodSecurityService {
|
||||
|
||||
}
|
||||
|
||||
class MaskAnnotationPostProcessor implements MethodAuthorizationDeniedPostProcessor {
|
||||
class MaskAnnotationPostProcessor implements MethodAuthorizationDeniedHandler {
|
||||
|
||||
MaskValueResolver maskValueResolver;
|
||||
|
||||
@@ -149,7 +147,16 @@ public interface ReactiveMethodSecurityService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object postProcessResult(MethodInvocationResult methodInvocationResult,
|
||||
public Object handleDeniedInvocation(MethodInvocation mi, AuthorizationResult authorizationResult) {
|
||||
Mask mask = AnnotationUtils.getAnnotation(mi.getMethod(), Mask.class);
|
||||
if (mask == null) {
|
||||
mask = AnnotationUtils.getAnnotation(mi.getMethod().getDeclaringClass(), Mask.class);
|
||||
}
|
||||
return this.maskValueResolver.resolveValue(mask, mi, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object handleDeniedInvocationResult(MethodInvocationResult methodInvocationResult,
|
||||
AuthorizationResult authorizationResult) {
|
||||
MethodInvocation mi = methodInvocationResult.getMethodInvocation();
|
||||
Mask mask = AnnotationUtils.getAnnotation(mi.getMethod(), Mask.class);
|
||||
@@ -185,31 +192,38 @@ public interface ReactiveMethodSecurityService {
|
||||
|
||||
}
|
||||
|
||||
class PostMaskingPostProcessor implements MethodAuthorizationDeniedPostProcessor {
|
||||
class PostMaskingPostProcessor implements MethodAuthorizationDeniedHandler {
|
||||
|
||||
@Override
|
||||
public Object postProcessResult(MethodInvocationResult contextObject, AuthorizationResult result) {
|
||||
public Object handleDeniedInvocation(MethodInvocation methodInvocation,
|
||||
AuthorizationResult authorizationResult) {
|
||||
return "***";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class CardNumberMaskingPostProcessor implements MethodAuthorizationDeniedPostProcessor {
|
||||
class CardNumberMaskingPostProcessor implements MethodAuthorizationDeniedHandler {
|
||||
|
||||
static String MASK = "****-****-****-";
|
||||
|
||||
@Override
|
||||
public Object postProcessResult(MethodInvocationResult contextObject, AuthorizationResult result) {
|
||||
public Object handleDeniedInvocation(MethodInvocation methodInvocation,
|
||||
AuthorizationResult authorizationResult) {
|
||||
return "***";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object handleDeniedInvocationResult(MethodInvocationResult contextObject, AuthorizationResult result) {
|
||||
String cardNumber = (String) contextObject.getResult();
|
||||
return MASK + cardNumber.substring(cardNumber.length() - 4);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class NullPostProcessor implements MethodAuthorizationDeniedPostProcessor {
|
||||
class NullPostProcessor implements MethodAuthorizationDeniedHandler {
|
||||
|
||||
@Override
|
||||
public Object postProcessResult(MethodInvocationResult methodInvocationResult,
|
||||
public Object handleDeniedInvocation(MethodInvocation methodInvocation,
|
||||
AuthorizationResult authorizationResult) {
|
||||
return null;
|
||||
}
|
||||
@@ -231,7 +245,7 @@ public interface ReactiveMethodSecurityService {
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@PostAuthorize("hasRole('{value}')")
|
||||
@AuthorizationDeniedHandler(postProcessorClass = NullPostProcessor.class)
|
||||
@HandleAuthorizationDenied(handlerClass = NullPostProcessor.class)
|
||||
@interface NullDenied {
|
||||
|
||||
String role();
|
||||
|
||||
@@ -18,7 +18,8 @@ package org.springframework.security.config.annotation.method.configuration;
|
||||
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.authorization.AuthorizationDecision;
|
||||
import org.springframework.security.authorization.AuthorizationDeniedException;
|
||||
|
||||
public class ReactiveMethodSecurityServiceImpl implements ReactiveMethodSecurityService {
|
||||
|
||||
@@ -34,7 +35,7 @@ public class ReactiveMethodSecurityServiceImpl implements ReactiveMethodSecurity
|
||||
|
||||
@Override
|
||||
public Mono<String> preAuthorizeThrowAccessDeniedManually() {
|
||||
return Mono.error(new AccessDeniedException("Access Denied"));
|
||||
return Mono.error(new AuthorizationDeniedException("Access Denied", new AuthorizationDecision(false)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -44,7 +45,7 @@ public class ReactiveMethodSecurityServiceImpl implements ReactiveMethodSecurity
|
||||
|
||||
@Override
|
||||
public Mono<String> postAuthorizeThrowAccessDeniedManually() {
|
||||
return Mono.error(new AccessDeniedException("Access Denied"));
|
||||
return Mono.error(new AuthorizationDeniedException("Access Denied", new AuthorizationDecision(false)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -16,10 +16,12 @@
|
||||
|
||||
package org.springframework.security.config.annotation.method.configuration;
|
||||
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
import org.springframework.security.access.prepost.PostAuthorize;
|
||||
import org.springframework.security.authorization.AuthorizationResult;
|
||||
import org.springframework.security.authorization.method.AuthorizationDeniedHandler;
|
||||
import org.springframework.security.authorization.method.MethodAuthorizationDeniedPostProcessor;
|
||||
import org.springframework.security.authorization.method.HandleAuthorizationDenied;
|
||||
import org.springframework.security.authorization.method.MethodAuthorizationDeniedHandler;
|
||||
import org.springframework.security.authorization.method.MethodInvocationResult;
|
||||
|
||||
public class UserRecordWithEmailProtected {
|
||||
@@ -38,15 +40,21 @@ public class UserRecordWithEmailProtected {
|
||||
}
|
||||
|
||||
@PostAuthorize("hasRole('ADMIN')")
|
||||
@AuthorizationDeniedHandler(postProcessorClass = EmailMaskingPostProcessor.class)
|
||||
@HandleAuthorizationDenied(handlerClass = EmailMaskingPostProcessor.class)
|
||||
public String email() {
|
||||
return this.email;
|
||||
}
|
||||
|
||||
public static class EmailMaskingPostProcessor implements MethodAuthorizationDeniedPostProcessor {
|
||||
public static class EmailMaskingPostProcessor implements MethodAuthorizationDeniedHandler {
|
||||
|
||||
@Override
|
||||
public Object postProcessResult(MethodInvocationResult methodInvocationResult,
|
||||
public Object handleDeniedInvocation(MethodInvocation methodInvocation,
|
||||
AuthorizationResult authorizationResult) {
|
||||
return "***";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object handleDeniedInvocationResult(MethodInvocationResult methodInvocationResult,
|
||||
AuthorizationResult authorizationResult) {
|
||||
String email = (String) methodInvocationResult.getResult();
|
||||
return email.replaceAll("(^[^@]{3}|(?!^)\\G)[^@]", "$1*");
|
||||
|
||||
Reference in New Issue
Block a user