Skip to content

Commit

Permalink
Add AuthorizationResult support for AuthorizationManager
Browse files Browse the repository at this point in the history
Closes gh-14843
  • Loading branch information
franticticktick committed Oct 11, 2024
1 parent e006507 commit 1a5f29c
Show file tree
Hide file tree
Showing 9 changed files with 33 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationEventPublisher;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.authorization.AuthorizationResult;
import org.springframework.security.authorization.method.AuthorizationAdvisor;
import org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory;
import org.springframework.security.authorization.method.AuthorizationAdvisorProxyFactory.TargetVisitor;
Expand Down Expand Up @@ -110,6 +111,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
Expand Down Expand Up @@ -1293,6 +1295,8 @@ static class AuthorizationEventPublisherConfig {

@Bean
AuthorizationEventPublisher authorizationEventPublisher() {
doCallRealMethod().when(this.publisher)
.publishAuthorizationEvent(any(), any(), any(AuthorizationResult.class));
return this.publisher;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 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.
Expand Down Expand Up @@ -42,6 +42,7 @@
import org.springframework.security.authorization.AuthorizationEventPublisher;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.authorization.AuthorizationObservationContext;
import org.springframework.security.authorization.AuthorizationResult;
import org.springframework.security.config.ObjectPostProcessor;
import org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
Expand Down Expand Up @@ -78,6 +79,7 @@
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doCallRealMethod;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
Expand Down Expand Up @@ -1241,6 +1243,8 @@ static class AuthorizationEventPublisherConfig {

@Bean
AuthorizationEventPublisher authorizationEventPublisher() {
doCallRealMethod().when(this.publisher)
.publishAuthorizationEvent(any(), any(), any(AuthorizationResult.class));
return this.publisher;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 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.
Expand Down Expand Up @@ -113,6 +113,7 @@ public void validateCheckLoginPageIsntProtectedThrowsIllegalArgumentException()
@Test
public void validateCheckLoginPageAllowsAnonymous() {
given(this.authorizationManager.check(any(), any())).willReturn(new AuthorizationDecision(false));
given(this.authorizationManager.authorize(any(), any())).willCallRealMethod();
this.validator.validate(this.chainAuthorizationFilter);
verify(this.logger).warn("Anonymous access to the login page doesn't appear to be enabled. "
+ "This is almost certainly an error. Please check your configuration allows unauthenticated "
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,7 @@ public void sendWhenCustomAuthorizationManagerThenAuthorizesAccordingly() {
AuthorizationManager<Message<?>> authorizationManager = this.spring.getContext()
.getBean(AuthorizationManager.class);
given(authorizationManager.check(any(), any())).willReturn(new AuthorizationDecision(false));
given(authorizationManager.authorize(any(), any())).willCallRealMethod();
Message<?> message = message("/any");
assertThatExceptionOfType(Exception.class).isThrownBy(send(message))
.withCauseInstanceOf(AccessDeniedException.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,10 @@ <T> void publishAuthorizationEvent(Supplier<Authentication> authentication, T ob
*/
default <T> void publishAuthorizationEvent(Supplier<Authentication> authentication, T object,
AuthorizationResult decision) {
if (decision == null) {
return;
}
if (!(decision instanceof AuthorizationDecision result)) {
if (decision != null && !(decision instanceof AuthorizationDecision)) {
throw new UnsupportedOperationException("Decision must be of type AuthorizationDecision");
}
publishAuthorizationEvent(authentication, object, result);
publishAuthorizationEvent(authentication, object, (AuthorizationDecision) decision);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -73,20 +73,24 @@ public T getObject() {
/**
* Get the observed {@link AuthorizationDecision}
* @return the observed {@link AuthorizationDecision}
* @deprecated please use {@link #getAuthorizationResult()} instead
*/
@Deprecated
public AuthorizationDecision getDecision() {
Assert.isInstanceOf(AuthorizationDecision.class,
Assert.isInstanceOf(AuthorizationDecision.class, this.authorizationResult,
"Please call getAuthorizationResult instead. If you must call getDecision, please ensure that the result you provide is of type AuthorizationDecision");
return (AuthorizationDecision) this.authorizationResult;
}

/**
* Set the observed {@link AuthorizationDecision}
* @param decision the observed {@link AuthorizationDecision}
* @deprecated
* @deprecated please use {@link #setAuthorizationResult(AuthorizationResult)} instead
*/
@Deprecated
public void setDecision(AuthorizationDecision decision) {
Assert.isInstanceOf(AuthorizationDecision.class, decision,
"Please call setAuthorizationResult instead. If you must call getDecision, please ensure that the result you provide is of type AuthorizationDecision");
this.decision = decision;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 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.
Expand Down Expand Up @@ -71,7 +71,7 @@ public AuthorizationDecision check(Supplier<Authentication> authentication, T ob
Observation observation = Observation.createNotStarted(this.convention, () -> context, this.registry).start();
try (Observation.Scope scope = observation.openScope()) {
AuthorizationDecision decision = this.delegate.check(wrapped, object);
context.setDecision(decision);
context.setAuthorizationResult(decision);
if (decision != null && !decision.isGranted()) {
observation.error(new AccessDeniedException(
this.messages.getMessage("AbstractAccessDecisionManager.accessDenied", "Access Denied")));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 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.
Expand Down Expand Up @@ -68,7 +68,7 @@ public Mono<AuthorizationDecision> check(Mono<Authentication> authentication, T
.parentObservation(contextView.getOrDefault(ObservationThreadLocalAccessor.KEY, null))
.start();
return this.delegate.check(wrapped, object).doOnSuccess((decision) -> {
context.setDecision(decision);
context.setAuthorizationResult(decision);
if (decision == null || !decision.isGranted()) {
observation.error(new AccessDeniedException("Access Denied"));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2024 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.
Expand Down Expand Up @@ -30,6 +30,7 @@
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationEventPublisher;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.authorization.AuthorizationResult;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;

Expand All @@ -38,6 +39,7 @@
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.lenient;
import static org.mockito.Mockito.verify;

/**
Expand Down Expand Up @@ -82,12 +84,14 @@ public void constructorWhenAuthorizationManagerNullThenIllegalArgument() {
@Test
public void preSendWhenAllowThenSameMessage() {
given(this.authorizationManager.check(any(), any())).willReturn(new AuthorizationDecision(true));
given(this.authorizationManager.authorize(any(), any())).willCallRealMethod();
assertThat(this.interceptor.preSend(this.message, this.channel)).isSameAs(this.message);
}

@Test
public void preSendWhenDenyThenException() {
given(this.authorizationManager.check(any(), any())).willReturn(new AuthorizationDecision(false));
given(this.authorizationManager.authorize(any(), any())).willCallRealMethod();
assertThatExceptionOfType(AccessDeniedException.class)
.isThrownBy(() -> this.interceptor.preSend(this.message, this.channel));
}
Expand All @@ -102,6 +106,10 @@ public void setEventPublisherWhenNullThenException() {
public void preSendWhenAuthorizationEventPublisherThenPublishes() {
this.interceptor.setAuthorizationEventPublisher(this.eventPublisher);
given(this.authorizationManager.check(any(), any())).willReturn(new AuthorizationDecision(true));
given(this.authorizationManager.authorize(any(), any())).willCallRealMethod();
lenient().doCallRealMethod()
.when(this.eventPublisher)
.publishAuthorizationEvent(any(), any(), any(AuthorizationResult.class));
this.interceptor.preSend(this.message, this.channel);
verify(this.eventPublisher).publishAuthorizationEvent(any(), any(), any());
}
Expand Down

0 comments on commit 1a5f29c

Please sign in to comment.