Skip to content

Commit

Permalink
Polish Method Authorization Denied Handling
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
marcusdacoregio committed Apr 12, 2024
1 parent 14da8f4 commit 2fbbcc4
Show file tree
Hide file tree
Showing 31 changed files with 425 additions and 440 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,22 +27,18 @@
import org.springframework.security.authorization.AuthorizationResult;
import org.springframework.security.authorization.ObservationAuthorizationManager;
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.ThrowingMethodAuthorizationDeniedHandler;
import org.springframework.security.authorization.method.ThrowingMethodAuthorizationDeniedPostProcessor;
import org.springframework.security.core.Authentication;
import org.springframework.util.function.SingletonSupplier;

final class DeferringObservationAuthorizationManager<T>
implements AuthorizationManager<T>, MethodAuthorizationDeniedHandler, MethodAuthorizationDeniedPostProcessor {
implements AuthorizationManager<T>, MethodAuthorizationDeniedHandler {

private final Supplier<AuthorizationManager<T>> delegate;

private MethodAuthorizationDeniedHandler handler = new ThrowingMethodAuthorizationDeniedHandler();

private MethodAuthorizationDeniedPostProcessor postProcessor = new ThrowingMethodAuthorizationDeniedPostProcessor();

DeferringObservationAuthorizationManager(ObjectProvider<ObservationRegistry> provider,
AuthorizationManager<T> delegate) {
this.delegate = SingletonSupplier.of(() -> {
Expand All @@ -55,9 +51,6 @@ final class DeferringObservationAuthorizationManager<T>
if (delegate instanceof MethodAuthorizationDeniedHandler h) {
this.handler = h;
}
if (delegate instanceof MethodAuthorizationDeniedPostProcessor p) {
this.postProcessor = p;
}
}

@Override
Expand All @@ -66,14 +59,14 @@ public AuthorizationDecision check(Supplier<Authentication> authentication, T ob
}

@Override
public Object handle(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
return this.handler.handle(methodInvocation, authorizationResult);
public Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
return this.handler.handleDeniedInvocation(methodInvocation, authorizationResult);
}

@Override
public Object postProcessResult(MethodInvocationResult methodInvocationResult,
public Object handleDeniedInvocationResult(MethodInvocationResult methodInvocationResult,
AuthorizationResult authorizationResult) {
return this.postProcessor.postProcessResult(methodInvocationResult, authorizationResult);
return this.handler.handleDeniedInvocationResult(methodInvocationResult, authorizationResult);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -28,22 +28,18 @@
import org.springframework.security.authorization.ObservationReactiveAuthorizationManager;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
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.ThrowingMethodAuthorizationDeniedHandler;
import org.springframework.security.authorization.method.ThrowingMethodAuthorizationDeniedPostProcessor;
import org.springframework.security.core.Authentication;
import org.springframework.util.function.SingletonSupplier;

final class DeferringObservationReactiveAuthorizationManager<T> implements ReactiveAuthorizationManager<T>,
MethodAuthorizationDeniedHandler, MethodAuthorizationDeniedPostProcessor {
final class DeferringObservationReactiveAuthorizationManager<T>
implements ReactiveAuthorizationManager<T>, MethodAuthorizationDeniedHandler {

private final Supplier<ReactiveAuthorizationManager<T>> delegate;

private MethodAuthorizationDeniedHandler handler = new ThrowingMethodAuthorizationDeniedHandler();

private MethodAuthorizationDeniedPostProcessor postProcessor = new ThrowingMethodAuthorizationDeniedPostProcessor();

DeferringObservationReactiveAuthorizationManager(ObjectProvider<ObservationRegistry> provider,
ReactiveAuthorizationManager<T> delegate) {
this.delegate = SingletonSupplier.of(() -> {
Expand All @@ -56,9 +52,6 @@ final class DeferringObservationReactiveAuthorizationManager<T> implements React
if (delegate instanceof MethodAuthorizationDeniedHandler h) {
this.handler = h;
}
if (delegate instanceof MethodAuthorizationDeniedPostProcessor p) {
this.postProcessor = p;
}
}

@Override
Expand All @@ -67,14 +60,14 @@ public Mono<AuthorizationDecision> check(Mono<Authentication> authentication, T
}

@Override
public Object handle(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
return this.handler.handle(methodInvocation, authorizationResult);
public Object handleDeniedInvocation(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
return this.handler.handleDeniedInvocation(methodInvocation, authorizationResult);
}

@Override
public Object postProcessResult(MethodInvocationResult methodInvocationResult,
public Object handleDeniedInvocationResult(MethodInvocationResult methodInvocationResult,
AuthorizationResult authorizationResult) {
return this.postProcessor.postProcessResult(methodInvocationResult, authorizationResult);
return this.handler.handleDeniedInvocationResult(methodInvocationResult, authorizationResult);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,9 @@
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;
Expand Down Expand Up @@ -129,73 +128,72 @@ 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")
String postAuthorizeDeniedWithNullDenied();

@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 "***";
}

Expand All @@ -204,8 +202,8 @@ public Object handle(MethodInvocation methodInvocation, AuthorizationResult resu
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";
}

}
Expand All @@ -218,7 +216,6 @@ class MaskAnnotationHandler implements MethodAuthorizationDeniedHandler {
this.maskValueResolver = new MaskValueResolver(context);
}

@Override
public Object handle(MethodInvocation methodInvocation, AuthorizationResult result) {
Mask mask = AnnotationUtils.getAnnotation(methodInvocation.getMethod(), Mask.class);
if (mask == null) {
Expand All @@ -227,9 +224,15 @@ public Object handle(MethodInvocation methodInvocation, AuthorizationResult resu
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;

Expand All @@ -238,7 +241,16 @@ class MaskAnnotationPostProcessor implements MethodAuthorizationDeniedPostProces
}

@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);
Expand Down Expand Up @@ -274,31 +286,38 @@ String resolveValue(Mask mask, MethodInvocation mi, Object returnObject) {

}

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;
}
Expand All @@ -320,7 +339,7 @@ public Object postProcessResult(MethodInvocationResult methodInvocationResult,
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@PostAuthorize("hasRole('{role}')")
@AuthorizationDeniedHandler(postProcessorClass = NullPostProcessor.class)
@HandleAuthorizationDenied(handlerClass = NullPostProcessor.class)
@interface NullDenied {

String role();
Expand All @@ -333,7 +352,8 @@ class UserFallbackDeniedHandler implements MethodAuthorizationDeniedHandler {
"Protected");

@Override
public Object handle(MethodInvocation methodInvocation, AuthorizationResult authorizationResult) {
public Object handleDeniedInvocation(MethodInvocation methodInvocation,
AuthorizationResult authorizationResult) {
return FALLBACK;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@

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;

Expand Down Expand Up @@ -144,12 +145,12 @@ public String preAuthorizeWithHandlerChildGetCardNumberIfAdmin(String cardNumber

@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
Expand Down
Loading

0 comments on commit 2fbbcc4

Please sign in to comment.