diff --git a/config/src/main/java/org/springframework/security/config/http/DefaultFilterChainValidator.java b/config/src/main/java/org/springframework/security/config/http/DefaultFilterChainValidator.java index 1e89e78cb8a..ce7c50be584 100644 --- a/config/src/main/java/org/springframework/security/config/http/DefaultFilterChainValidator.java +++ b/config/src/main/java/org/springframework/security/config/http/DefaultFilterChainValidator.java @@ -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. @@ -32,8 +32,8 @@ import org.springframework.security.access.ConfigAttribute; import org.springframework.security.authentication.AnonymousAuthenticationToken; import org.springframework.security.authentication.TestingAuthenticationToken; -import org.springframework.security.authorization.AuthorizationDecision; import org.springframework.security.authorization.AuthorizationManager; +import org.springframework.security.authorization.AuthorizationResult; import org.springframework.security.core.Authentication; import org.springframework.security.web.DefaultSecurityFilterChain; import org.springframework.security.web.FilterChainProxy; @@ -221,8 +221,8 @@ private boolean checkLoginPageIsPublic(List filters, FilterInvocation lo AuthorizationManager authorizationManager = authorizationFilter .getAuthorizationManager(); try { - AuthorizationDecision decision = authorizationManager.check(() -> TEST, loginRequest.getHttpRequest()); - return decision != null && decision.isGranted(); + AuthorizationResult result = authorizationManager.authorize(() -> TEST, loginRequest.getHttpRequest()); + return result != null && result.isGranted(); } catch (Exception ex) { return false; @@ -252,8 +252,8 @@ private Supplier deriveAnonymousCheck(List filters, FilterInvoc return () -> { AuthorizationManager authorizationManager = authorizationFilter .getAuthorizationManager(); - AuthorizationDecision decision = authorizationManager.check(() -> token, loginRequest.getHttpRequest()); - return decision != null && decision.isGranted(); + AuthorizationResult result = authorizationManager.authorize(() -> token, loginRequest.getHttpRequest()); + return result != null && result.isGranted(); }; } return () -> true; diff --git a/config/src/main/java/org/springframework/security/config/method/PointcutDelegatingAuthorizationManager.java b/config/src/main/java/org/springframework/security/config/method/PointcutDelegatingAuthorizationManager.java index d6aa4767f8f..1a47fc455ab 100644 --- a/config/src/main/java/org/springframework/security/config/method/PointcutDelegatingAuthorizationManager.java +++ b/config/src/main/java/org/springframework/security/config/method/PointcutDelegatingAuthorizationManager.java @@ -25,6 +25,7 @@ import org.springframework.aop.support.AopUtils; import org.springframework.security.authorization.AuthorizationDecision; import org.springframework.security.authorization.AuthorizationManager; +import org.springframework.security.authorization.AuthorizationResult; import org.springframework.security.core.Authentication; class PointcutDelegatingAuthorizationManager implements AuthorizationManager { @@ -37,11 +38,24 @@ class PointcutDelegatingAuthorizationManager implements AuthorizationManager authentication, MethodInvocation object) { + AuthorizationResult result = authorize(authentication, object); + if (result == null) { + return null; + } + if (result instanceof AuthorizationDecision decision) { + return decision; + } + throw new IllegalArgumentException( + "Please either call authorize or ensure that the returned result is of type AuthorizationDecision"); + } + + @Override + public AuthorizationResult authorize(Supplier authentication, MethodInvocation object) { for (Map.Entry> entry : this.managers.entrySet()) { Class targetClass = (object.getThis() != null) ? AopUtils.getTargetClass(object.getThis()) : null; if (entry.getKey().getClassFilter().matches(targetClass) && entry.getKey().getMethodMatcher().matches(object.getMethod(), targetClass)) { - return entry.getValue().check(authentication, object); + return entry.getValue().authorize(authentication, object); } } return new AuthorizationDecision(false); diff --git a/config/src/test/java/org/springframework/security/config/annotation/method/configuration/PrePostMethodSecurityConfigurationTests.java b/config/src/test/java/org/springframework/security/config/annotation/method/configuration/PrePostMethodSecurityConfigurationTests.java index 73e65c78a7f..17d8f8a3a92 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/method/configuration/PrePostMethodSecurityConfigurationTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/method/configuration/PrePostMethodSecurityConfigurationTests.java @@ -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; @@ -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; @@ -1293,6 +1295,8 @@ static class AuthorizationEventPublisherConfig { @Bean AuthorizationEventPublisher authorizationEventPublisher() { + doCallRealMethod().when(this.publisher) + .publishAuthorizationEvent(any(), any(), any(AuthorizationResult.class)); return this.publisher; } diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurerTests.java index 168f89f1374..68c752a3943 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurerTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurerTests.java @@ -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. @@ -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; @@ -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; @@ -1241,6 +1243,8 @@ static class AuthorizationEventPublisherConfig { @Bean AuthorizationEventPublisher authorizationEventPublisher() { + doCallRealMethod().when(this.publisher) + .publishAuthorizationEvent(any(), any(), any(AuthorizationResult.class)); return this.publisher; } diff --git a/config/src/test/java/org/springframework/security/config/http/DefaultFilterChainValidatorTests.java b/config/src/test/java/org/springframework/security/config/http/DefaultFilterChainValidatorTests.java index a20fe5397f1..a5b899db48c 100644 --- a/config/src/test/java/org/springframework/security/config/http/DefaultFilterChainValidatorTests.java +++ b/config/src/test/java/org/springframework/security/config/http/DefaultFilterChainValidatorTests.java @@ -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. @@ -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 " diff --git a/config/src/test/java/org/springframework/security/config/http/HttpConfigTests.java b/config/src/test/java/org/springframework/security/config/http/HttpConfigTests.java index b8a86d2411c..9a4e3b041e1 100644 --- a/config/src/test/java/org/springframework/security/config/http/HttpConfigTests.java +++ b/config/src/test/java/org/springframework/security/config/http/HttpConfigTests.java @@ -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. @@ -91,6 +91,7 @@ public void getWhenUsingAuthorizationManagerThenRedirectsToLogin() throws Except AuthorizationManager authorizationManager = this.spring.getContext() .getBean(AuthorizationManager.class); given(authorizationManager.check(any(), any())).willReturn(new AuthorizationDecision(false)); + given(authorizationManager.authorize(any(), any())).willCallRealMethod(); // @formatter:off this.mvc.perform(get("/")) .andExpect(status().isFound()) diff --git a/config/src/test/java/org/springframework/security/config/method/InterceptMethodsBeanDefinitionDecoratorTests.java b/config/src/test/java/org/springframework/security/config/method/InterceptMethodsBeanDefinitionDecoratorTests.java index a4257f1732f..c71acbe5a44 100644 --- a/config/src/test/java/org/springframework/security/config/method/InterceptMethodsBeanDefinitionDecoratorTests.java +++ b/config/src/test/java/org/springframework/security/config/method/InterceptMethodsBeanDefinitionDecoratorTests.java @@ -168,6 +168,7 @@ public void transactionalAuthorizationManagerMethodsShouldBeSecured() { @Test public void targetCustomAuthorizationManagerUsed() { + given(this.mockAuthorizationManager.authorize(any(), any())).willCallRealMethod(); given(this.mockAuthorizationManager.check(any(), any())).willReturn(new AuthorizationDecision(true)); this.targetCustomAuthorizationManager.doSomething(); verify(this.mockAuthorizationManager).check(any(), any()); diff --git a/config/src/test/java/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests.java b/config/src/test/java/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests.java index 7a5e75e9385..6e999933a28 100644 --- a/config/src/test/java/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests.java +++ b/config/src/test/java/org/springframework/security/config/websocket/WebSocketMessageBrokerConfigTests.java @@ -514,6 +514,7 @@ public void sendWhenCustomAuthorizationManagerThenAuthorizesAccordingly() { AuthorizationManager> 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); diff --git a/core/src/main/java/org/springframework/security/authorization/AuthorityAuthorizationManager.java b/core/src/main/java/org/springframework/security/authorization/AuthorityAuthorizationManager.java index 1d93dffa08e..e71f99b37cc 100644 --- a/core/src/main/java/org/springframework/security/authorization/AuthorityAuthorizationManager.java +++ b/core/src/main/java/org/springframework/security/authorization/AuthorityAuthorizationManager.java @@ -139,7 +139,9 @@ private static String[] toNamedRolesArray(String rolePrefix, String[] roles) { * @param authentication the {@link Supplier} of the {@link Authentication} to check * @param object the {@link T} object to check * @return an {@link AuthorizationDecision} + * @deprecated please use {@link #authorize(Supplier, Object)} instead */ + @Deprecated @Override public AuthorizationDecision check(Supplier authentication, T object) { return this.delegate.check(authentication, this.authorities); diff --git a/core/src/main/java/org/springframework/security/authorization/AuthorizationEventPublisher.java b/core/src/main/java/org/springframework/security/authorization/AuthorizationEventPublisher.java index de972c59b22..b230d70a971 100644 --- a/core/src/main/java/org/springframework/security/authorization/AuthorizationEventPublisher.java +++ b/core/src/main/java/org/springframework/security/authorization/AuthorizationEventPublisher.java @@ -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. @@ -42,8 +42,37 @@ public interface AuthorizationEventPublisher { * @param object the secured object * @param decision the decision about whether the user may access the secured object * @param the secured object's type + * @deprecated use + * {@link #publishAuthorizationEvent(Supplier, Object, AuthorizationResult)} instead */ + @Deprecated void publishAuthorizationEvent(Supplier authentication, T object, AuthorizationDecision decision); + /** + * Publish the given details in the form of an event, typically + * {@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 result {@link AuthorizationResult} the result about whether the user may + * access the secured object + * @param the secured object's type + * @since 6.4 + */ + default void publishAuthorizationEvent(Supplier authentication, T object, + AuthorizationResult result) { + if (result == null) { + publishAuthorizationEvent(authentication, object, null); + return; + } + if (result instanceof AuthorizationDecision decision) { + publishAuthorizationEvent(authentication, object, decision); + return; + } + throw new UnsupportedOperationException("result must be of type AuthorizationDecision"); + } + } diff --git a/core/src/main/java/org/springframework/security/authorization/AuthorizationManager.java b/core/src/main/java/org/springframework/security/authorization/AuthorizationManager.java index cb0ba782cb2..758abce2c70 100644 --- a/core/src/main/java/org/springframework/security/authorization/AuthorizationManager.java +++ b/core/src/main/java/org/springframework/security/authorization/AuthorizationManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 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. @@ -50,8 +50,23 @@ default void verify(Supplier authentication, T object) { * @param authentication the {@link Supplier} of the {@link Authentication} to check * @param object the {@link T} object to check * @return an {@link AuthorizationDecision} or null if no decision could be made + * @deprecated please use {@link #authorize(Supplier, Object)} instead */ @Nullable + @Deprecated AuthorizationDecision check(Supplier authentication, T object); + /** + * Determines if access is granted for a specific authentication and object. + * @param authentication the {@link Supplier} of the {@link Authentication} to + * authorize + * @param object the {@link T} object to authorize + * @return an {@link AuthorizationResult} + * @since 6.4 + */ + @Nullable + default AuthorizationResult authorize(Supplier authentication, T object) { + return check(authentication, object); + } + } diff --git a/core/src/main/java/org/springframework/security/authorization/AuthorizationManagers.java b/core/src/main/java/org/springframework/security/authorization/AuthorizationManagers.java index b9031092050..f3893c97433 100644 --- a/core/src/main/java/org/springframework/security/authorization/AuthorizationManagers.java +++ b/core/src/main/java/org/springframework/security/authorization/AuthorizationManagers.java @@ -18,6 +18,9 @@ import java.util.ArrayList; import java.util.List; +import java.util.function.Supplier; + +import org.springframework.security.core.Authentication; /** * A factory class to create an {@link AuthorizationManager} instances. @@ -55,22 +58,22 @@ public static AuthorizationManager anyOf(AuthorizationManager... manag @SafeVarargs public static AuthorizationManager anyOf(AuthorizationDecision allAbstainDefaultDecision, AuthorizationManager... managers) { - return (authentication, object) -> { - List decisions = new ArrayList<>(); + return (AuthorizationManagerCheckAdapter) (authentication, object) -> { + List results = new ArrayList<>(); for (AuthorizationManager manager : managers) { - AuthorizationDecision decision = manager.check(authentication, object); - if (decision == null) { + AuthorizationResult result = manager.authorize(authentication, object); + if (result == null) { continue; } - if (decision.isGranted()) { - return decision; + if (result.isGranted()) { + return result; } - decisions.add(decision); + results.add(result); } - if (decisions.isEmpty()) { + if (results.isEmpty()) { return allAbstainDefaultDecision; } - return new CompositeAuthorizationDecision(false, decisions); + return new CompositeAuthorizationDecision(false, results); }; } @@ -101,22 +104,22 @@ public static AuthorizationManager allOf(AuthorizationManager... manag @SafeVarargs public static AuthorizationManager allOf(AuthorizationDecision allAbstainDefaultDecision, AuthorizationManager... managers) { - return (authentication, object) -> { - List decisions = new ArrayList<>(); + return (AuthorizationManagerCheckAdapter) (authentication, object) -> { + List results = new ArrayList<>(); for (AuthorizationManager manager : managers) { - AuthorizationDecision decision = manager.check(authentication, object); - if (decision == null) { + AuthorizationResult result = manager.authorize(authentication, object); + if (result == null) { continue; } - if (!decision.isGranted()) { - return decision; + if (!result.isGranted()) { + return result; } - decisions.add(decision); + results.add(result); } - if (decisions.isEmpty()) { + if (results.isEmpty()) { return allAbstainDefaultDecision; } - return new CompositeAuthorizationDecision(true, decisions); + return new CompositeAuthorizationDecision(true, results); }; } @@ -131,11 +134,11 @@ public static AuthorizationManager allOf(AuthorizationDecision allAbstain */ public static AuthorizationManager not(AuthorizationManager manager) { return (authentication, object) -> { - AuthorizationDecision decision = manager.check(authentication, object); - if (decision == null) { + AuthorizationResult result = manager.authorize(authentication, object); + if (result == null) { return null; } - return new NotAuthorizationDecision(decision); + return new NotAuthorizationDecision(result); }; } @@ -144,34 +147,53 @@ private AuthorizationManagers() { private static final class CompositeAuthorizationDecision extends AuthorizationDecision { - private final List decisions; + private final List results; - private CompositeAuthorizationDecision(boolean granted, List decisions) { + private CompositeAuthorizationDecision(boolean granted, List results) { super(granted); - this.decisions = decisions; + this.results = results; } @Override public String toString() { - return "CompositeAuthorizationDecision [decisions=" + this.decisions + ']'; + return "CompositeAuthorizationDecision [results=" + this.results + ']'; } } private static final class NotAuthorizationDecision extends AuthorizationDecision { - private final AuthorizationDecision decision; + private final AuthorizationResult result; - private NotAuthorizationDecision(AuthorizationDecision decision) { - super(!decision.isGranted()); - this.decision = decision; + private NotAuthorizationDecision(AuthorizationResult result) { + super(!result.isGranted()); + this.result = result; } @Override public String toString() { - return "NotAuthorizationDecision [decision=" + this.decision + ']'; + return "NotAuthorizationDecision [result=" + this.result + ']'; } } + private interface AuthorizationManagerCheckAdapter extends AuthorizationManager { + + @Override + default AuthorizationDecision check(Supplier authentication, T object) { + AuthorizationResult result = authorize(authentication, object); + if (result == null) { + return null; + } + if (result instanceof AuthorizationDecision decision) { + return decision; + } + throw new IllegalArgumentException( + "please call #authorize or ensure that the result is of type AuthorizationDecision"); + } + + AuthorizationResult authorize(Supplier authentication, T object); + + } + } diff --git a/core/src/main/java/org/springframework/security/authorization/AuthorizationObservationContext.java b/core/src/main/java/org/springframework/security/authorization/AuthorizationObservationContext.java index 8e5692213c0..06c40e8fe02 100644 --- a/core/src/main/java/org/springframework/security/authorization/AuthorizationObservationContext.java +++ b/core/src/main/java/org/springframework/security/authorization/AuthorizationObservationContext.java @@ -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. @@ -33,7 +33,7 @@ public class AuthorizationObservationContext extends Observation.Context { private final T object; - private AuthorizationDecision decision; + private AuthorizationResult authorizationResult; public AuthorizationObservationContext(T object) { Assert.notNull(object, "object cannot be null"); @@ -71,17 +71,46 @@ public T getObject() { /** * Get the observed {@link AuthorizationDecision} * @return the observed {@link AuthorizationDecision} + * @deprecated please use {@link #getAuthorizationResult()} instead */ + @Deprecated public AuthorizationDecision getDecision() { - return this.decision; + if (this.authorizationResult == null) { + return null; + } + if (this.authorizationResult instanceof AuthorizationDecision decision) { + return decision; + } + throw new IllegalArgumentException( + "Please call getAuthorizationResult instead. If you must call getDecision, please ensure that the result you provide is of type AuthorizationDecision"); } /** * Set the observed {@link AuthorizationDecision} * @param decision the observed {@link AuthorizationDecision} + * @deprecated please use {@link #setAuthorizationResult(AuthorizationResult)} instead */ + @Deprecated public void setDecision(AuthorizationDecision decision) { - this.decision = decision; + this.authorizationResult = decision; + } + + /** + * Get the observed {@link AuthorizationResult} + * @return the observed {@link AuthorizationResult} + * @since 6.4 + */ + public AuthorizationResult getAuthorizationResult() { + return this.authorizationResult; + } + + /** + * Set the observed {@link AuthorizationResult} + * @param authorizationResult the observed {@link AuthorizationResult} + * @since 6.4 + */ + public void setAuthorizationResult(AuthorizationResult authorizationResult) { + this.authorizationResult = authorizationResult; } } diff --git a/core/src/main/java/org/springframework/security/authorization/AuthorizationObservationConvention.java b/core/src/main/java/org/springframework/security/authorization/AuthorizationObservationConvention.java index 1fb819cd49e..9797c1c8001 100644 --- a/core/src/main/java/org/springframework/security/authorization/AuthorizationObservationConvention.java +++ b/core/src/main/java/org/springframework/security/authorization/AuthorizationObservationConvention.java @@ -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. @@ -100,10 +100,10 @@ private String getObjectType(AuthorizationObservationContext context) { } private String getAuthorizationDecision(AuthorizationObservationContext context) { - if (context.getDecision() == null) { + if (context.getAuthorizationResult() == null) { return "unknown"; } - return String.valueOf(context.getDecision().isGranted()); + return String.valueOf(context.getAuthorizationResult().isGranted()); } private String getAuthorities(AuthorizationObservationContext context) { @@ -114,10 +114,10 @@ private String getAuthorities(AuthorizationObservationContext context) { } private String getDecisionDetails(AuthorizationObservationContext context) { - if (context.getDecision() == null) { + if (context.getAuthorizationResult() == null) { return "unknown"; } - AuthorizationDecision decision = context.getDecision(); + AuthorizationResult decision = context.getAuthorizationResult(); return String.valueOf(decision); } diff --git a/core/src/main/java/org/springframework/security/authorization/ObservationAuthorizationManager.java b/core/src/main/java/org/springframework/security/authorization/ObservationAuthorizationManager.java index 00deb1c0353..0494dbfecb0 100644 --- a/core/src/main/java/org/springframework/security/authorization/ObservationAuthorizationManager.java +++ b/core/src/main/java/org/springframework/security/authorization/ObservationAuthorizationManager.java @@ -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. @@ -61,6 +61,10 @@ public ObservationAuthorizationManager(ObservationRegistry registry, Authorizati } } + /** + * @deprecated please use {@link #authorize(Supplier, Object)} instead + */ + @Deprecated @Override public AuthorizationDecision check(Supplier authentication, T object) { AuthorizationObservationContext context = new AuthorizationObservationContext<>(object); @@ -71,7 +75,7 @@ public AuthorizationDecision check(Supplier 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"))); diff --git a/core/src/main/java/org/springframework/security/authorization/ObservationReactiveAuthorizationManager.java b/core/src/main/java/org/springframework/security/authorization/ObservationReactiveAuthorizationManager.java index d6e7a2b2c63..a9d77c641b5 100644 --- a/core/src/main/java/org/springframework/security/authorization/ObservationReactiveAuthorizationManager.java +++ b/core/src/main/java/org/springframework/security/authorization/ObservationReactiveAuthorizationManager.java @@ -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. @@ -56,6 +56,10 @@ public ObservationReactiveAuthorizationManager(ObservationRegistry registry, } } + /** + * @deprecated please use {@link #authorize(Mono, Object)} instead + */ + @Deprecated @Override public Mono check(Mono authentication, T object) { AuthorizationObservationContext context = new AuthorizationObservationContext<>(object); @@ -68,7 +72,7 @@ public Mono check(Mono 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")); } diff --git a/core/src/main/java/org/springframework/security/authorization/ReactiveAuthorizationManager.java b/core/src/main/java/org/springframework/security/authorization/ReactiveAuthorizationManager.java index 97a7c3489bf..05662737d1a 100644 --- a/core/src/main/java/org/springframework/security/authorization/ReactiveAuthorizationManager.java +++ b/core/src/main/java/org/springframework/security/authorization/ReactiveAuthorizationManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 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. @@ -36,7 +36,9 @@ public interface ReactiveAuthorizationManager { * @param authentication the Authentication to check * @param object the object to check * @return an decision or empty Mono if no decision could be made. + * @deprecated please use {@link #authorize(Mono, Object)} instead */ + @Deprecated Mono check(Mono authentication, T object); /** @@ -55,4 +57,15 @@ default Mono verify(Mono authentication, T object) { // @formatter:on } + /** + * Determines if access is granted for a specific authentication and object. + * @param authentication the Authentication to authorize + * @param object the object to check + * @return an decision or empty Mono if no decision could be made. + * @since 6.4 + */ + default Mono authorize(Mono authentication, T object) { + return check(authentication, object).cast(AuthorizationResult.class); + } + } diff --git a/core/src/main/java/org/springframework/security/authorization/SpringAuthorizationEventPublisher.java b/core/src/main/java/org/springframework/security/authorization/SpringAuthorizationEventPublisher.java index 2ef21a28730..43e37c5523b 100644 --- a/core/src/main/java/org/springframework/security/authorization/SpringAuthorizationEventPublisher.java +++ b/core/src/main/java/org/springframework/security/authorization/SpringAuthorizationEventPublisher.java @@ -55,10 +55,16 @@ public SpringAuthorizationEventPublisher(ApplicationEventPublisher eventPublishe @Override public void publishAuthorizationEvent(Supplier authentication, T object, AuthorizationDecision decision) { - if (decision == null || decision.isGranted()) { + publishAuthorizationEvent(authentication, object, (AuthorizationResult) decision); + } + + @Override + public void publishAuthorizationEvent(Supplier authentication, T object, + AuthorizationResult result) { + if (result == null || result.isGranted()) { return; } - AuthorizationDeniedEvent failure = new AuthorizationDeniedEvent<>(authentication, object, decision); + AuthorizationDeniedEvent failure = new AuthorizationDeniedEvent<>(authentication, object, result); this.eventPublisher.publishEvent(failure); } diff --git a/core/src/main/java/org/springframework/security/authorization/event/AuthorizationDeniedEvent.java b/core/src/main/java/org/springframework/security/authorization/event/AuthorizationDeniedEvent.java index df964c61965..94e7d6a2312 100644 --- a/core/src/main/java/org/springframework/security/authorization/event/AuthorizationDeniedEvent.java +++ b/core/src/main/java/org/springframework/security/authorization/event/AuthorizationDeniedEvent.java @@ -20,6 +20,7 @@ import org.springframework.context.ApplicationEvent; import org.springframework.security.authorization.AuthorizationDecision; +import org.springframework.security.authorization.AuthorizationResult; import org.springframework.security.core.Authentication; /** @@ -31,10 +32,21 @@ */ public class AuthorizationDeniedEvent extends AuthorizationEvent { + /** + * @deprecated Please use an {@link AuthorizationResult} constructor instead + */ + @Deprecated public AuthorizationDeniedEvent(Supplier authentication, T object, AuthorizationDecision decision) { super(authentication, object, decision); } + /** + * @since 6.4 + */ + public AuthorizationDeniedEvent(Supplier authentication, T object, AuthorizationResult result) { + super(authentication, object, result); + } + /** * Get the object to which access was requested * @return the object to which access was requested diff --git a/core/src/main/java/org/springframework/security/authorization/event/AuthorizationEvent.java b/core/src/main/java/org/springframework/security/authorization/event/AuthorizationEvent.java index fd046a13122..a848dff4917 100644 --- a/core/src/main/java/org/springframework/security/authorization/event/AuthorizationEvent.java +++ b/core/src/main/java/org/springframework/security/authorization/event/AuthorizationEvent.java @@ -20,6 +20,7 @@ import org.springframework.context.ApplicationEvent; import org.springframework.security.authorization.AuthorizationDecision; +import org.springframework.security.authorization.AuthorizationResult; import org.springframework.security.core.Authentication; import org.springframework.util.Assert; @@ -34,19 +35,32 @@ public class AuthorizationEvent extends ApplicationEvent { private final Supplier authentication; - private final AuthorizationDecision decision; + private final AuthorizationResult result; /** * Construct an {@link AuthorizationEvent} * @param authentication the principal requiring access * @param object the object to which access was requested - * @param decision whether authorization was granted or denied + * @param result whether authorization was granted or denied */ - public AuthorizationEvent(Supplier authentication, Object object, AuthorizationDecision decision) { + public AuthorizationEvent(Supplier authentication, Object object, AuthorizationDecision result) { super(object); Assert.notNull(authentication, "authentication supplier cannot be null"); this.authentication = authentication; - this.decision = decision; + this.result = result; + } + + /** + * Construct an {@link AuthorizationEvent} + * @param authentication the principal requiring access + * @param object the object to which access was requested + * @param result whether authorization was granted or denied + */ + public AuthorizationEvent(Supplier authentication, Object object, AuthorizationResult result) { + super(object); + Assert.notNull(authentication, "authentication supplier cannot be null"); + this.authentication = authentication; + this.result = result; } /** @@ -68,9 +82,27 @@ public Object getObject() { /** * Get the response to the principal's request * @return the response to the principal's request + * @deprecated please use {@link #getAuthorizationResult()} */ + @Deprecated public AuthorizationDecision getAuthorizationDecision() { - return this.decision; + if (this.result == null) { + return null; + } + if (this.result instanceof AuthorizationDecision decision) { + return decision; + } + throw new IllegalArgumentException( + "Please either call getAuthorizationResult or ensure that the result is of type AuthorizationDecision"); + } + + /** + * Get the response to the principal's request + * @return the response to the principal's request + * @since 6.4 + */ + public AuthorizationResult getAuthorizationResult() { + return this.result; } } diff --git a/core/src/main/java/org/springframework/security/authorization/event/AuthorizationGrantedEvent.java b/core/src/main/java/org/springframework/security/authorization/event/AuthorizationGrantedEvent.java index f44bbb391ea..693bc7e4a76 100644 --- a/core/src/main/java/org/springframework/security/authorization/event/AuthorizationGrantedEvent.java +++ b/core/src/main/java/org/springframework/security/authorization/event/AuthorizationGrantedEvent.java @@ -20,6 +20,7 @@ import org.springframework.context.ApplicationEvent; import org.springframework.security.authorization.AuthorizationDecision; +import org.springframework.security.authorization.AuthorizationResult; import org.springframework.security.core.Authentication; /** @@ -31,11 +32,23 @@ */ public class AuthorizationGrantedEvent extends AuthorizationEvent { + /** + * @deprecated please use a constructor that takes an + * {@link org.springframework.security.authorization.AuthorizationResult} + */ + @Deprecated public AuthorizationGrantedEvent(Supplier authentication, T object, AuthorizationDecision decision) { super(authentication, object, decision); } + /** + * @since 6.4 + */ + public AuthorizationGrantedEvent(Supplier authentication, T object, AuthorizationResult result) { + super(authentication, object, result); + } + /** * Get the object to which access was requested * @return the object to which access was requested diff --git a/core/src/main/java/org/springframework/security/authorization/method/AuthorizationManagerAfterMethodInterceptor.java b/core/src/main/java/org/springframework/security/authorization/method/AuthorizationManagerAfterMethodInterceptor.java index 63448a6472a..a5604c5c15f 100644 --- a/core/src/main/java/org/springframework/security/authorization/method/AuthorizationManagerAfterMethodInterceptor.java +++ b/core/src/main/java/org/springframework/security/authorization/method/AuthorizationManagerAfterMethodInterceptor.java @@ -29,10 +29,10 @@ import org.springframework.security.access.AccessDeniedException; import org.springframework.security.access.prepost.PostAuthorize; import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException; -import org.springframework.security.authorization.AuthorizationDecision; import org.springframework.security.authorization.AuthorizationDeniedException; 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; import org.springframework.security.core.context.SecurityContextHolderStrategy; @@ -60,7 +60,7 @@ public final class AuthorizationManagerAfterMethodInterceptor implements Authori private int order; - private AuthorizationEventPublisher eventPublisher = AuthorizationManagerAfterMethodInterceptor::noPublish; + private AuthorizationEventPublisher eventPublisher = new NoOpAuthorizationEventPublisher(); /** * Creates an instance. @@ -182,22 +182,22 @@ public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy strat private Object attemptAuthorization(MethodInvocation mi, Object result) { this.logger.debug(LogMessage.of(() -> "Authorizing method invocation " + mi)); MethodInvocationResult object = new MethodInvocationResult(mi, result); - AuthorizationDecision decision = this.authorizationManager.check(this::getAuthentication, object); - this.eventPublisher.publishAuthorizationEvent(this::getAuthentication, object, decision); - if (decision != null && !decision.isGranted()) { + AuthorizationResult authorizationResult = this.authorizationManager.authorize(this::getAuthentication, object); + this.eventPublisher.publishAuthorizationEvent(this::getAuthentication, object, authorizationResult); + if (authorizationResult != null && !authorizationResult.isGranted()) { this.logger.debug(LogMessage.of(() -> "Failed to authorize " + mi + " with authorization manager " - + this.authorizationManager + " and decision " + decision)); - return handlePostInvocationDenied(object, decision); + + this.authorizationManager + " and authorizationResult " + authorizationResult)); + return handlePostInvocationDenied(object, authorizationResult); } this.logger.debug(LogMessage.of(() -> "Authorized method invocation " + mi)); return result; } - private Object handlePostInvocationDenied(MethodInvocationResult mi, AuthorizationDecision decision) { + private Object handlePostInvocationDenied(MethodInvocationResult mi, AuthorizationResult result) { if (this.authorizationManager instanceof MethodAuthorizationDeniedHandler deniedHandler) { - return deniedHandler.handleDeniedInvocationResult(mi, decision); + return deniedHandler.handleDeniedInvocationResult(mi, result); } - return this.defaultHandler.handleDeniedInvocationResult(mi, decision); + return this.defaultHandler.handleDeniedInvocationResult(mi, result); } private Authentication getAuthentication() { @@ -209,9 +209,4 @@ private Authentication getAuthentication() { return authentication; } - private static void noPublish(Supplier authentication, T object, - AuthorizationDecision decision) { - - } - } diff --git a/core/src/main/java/org/springframework/security/authorization/method/AuthorizationManagerAfterReactiveMethodInterceptor.java b/core/src/main/java/org/springframework/security/authorization/method/AuthorizationManagerAfterReactiveMethodInterceptor.java index fa53945a69d..8ac5a70661f 100644 --- a/core/src/main/java/org/springframework/security/authorization/method/AuthorizationManagerAfterReactiveMethodInterceptor.java +++ b/core/src/main/java/org/springframework/security/authorization/method/AuthorizationManagerAfterReactiveMethodInterceptor.java @@ -164,7 +164,7 @@ private boolean isMultiValue(Class returnType, ReactiveAdapter adapter) { private Mono postAuthorize(Mono authentication, MethodInvocation mi, Object result) { MethodInvocationResult invocationResult = new MethodInvocationResult(mi, result); - return this.authorizationManager.check(authentication, invocationResult) + return this.authorizationManager.authorize(authentication, invocationResult) .switchIfEmpty(Mono.just(new AuthorizationDecision(false))) .flatMap((decision) -> postProcess(decision, invocationResult)); } diff --git a/core/src/main/java/org/springframework/security/authorization/method/AuthorizationManagerBeforeMethodInterceptor.java b/core/src/main/java/org/springframework/security/authorization/method/AuthorizationManagerBeforeMethodInterceptor.java index cb753e34cd9..4e67ca9f802 100644 --- a/core/src/main/java/org/springframework/security/authorization/method/AuthorizationManagerBeforeMethodInterceptor.java +++ b/core/src/main/java/org/springframework/security/authorization/method/AuthorizationManagerBeforeMethodInterceptor.java @@ -33,7 +33,6 @@ import org.springframework.security.access.annotation.Secured; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException; -import org.springframework.security.authorization.AuthorizationDecision; import org.springframework.security.authorization.AuthorizationDeniedException; import org.springframework.security.authorization.AuthorizationEventPublisher; import org.springframework.security.authorization.AuthorizationManager; @@ -65,7 +64,7 @@ public final class AuthorizationManagerBeforeMethodInterceptor implements Author private int order = AuthorizationInterceptorsOrder.FIRST.getOrder(); - private AuthorizationEventPublisher eventPublisher = AuthorizationManagerBeforeMethodInterceptor::noPublish; + private AuthorizationEventPublisher eventPublisher = new NoOpAuthorizationEventPublisher(); /** * Creates an instance. @@ -247,18 +246,18 @@ public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy secur private Object attemptAuthorization(MethodInvocation mi) throws Throwable { this.logger.debug(LogMessage.of(() -> "Authorizing method invocation " + mi)); - AuthorizationDecision decision; + AuthorizationResult result; try { - decision = this.authorizationManager.check(this::getAuthentication, mi); + result = this.authorizationManager.authorize(this::getAuthentication, mi); } catch (AuthorizationDeniedException denied) { return handle(mi, denied); } - this.eventPublisher.publishAuthorizationEvent(this::getAuthentication, mi, decision); - if (decision != null && !decision.isGranted()) { + this.eventPublisher.publishAuthorizationEvent(this::getAuthentication, mi, result); + if (result != null && !result.isGranted()) { this.logger.debug(LogMessage.of(() -> "Failed to authorize " + mi + " with authorization manager " - + this.authorizationManager + " and decision " + decision)); - return handle(mi, decision); + + this.authorizationManager + " and result " + result)); + return handle(mi, result); } this.logger.debug(LogMessage.of(() -> "Authorized method invocation " + mi)); return proceed(mi); @@ -283,11 +282,11 @@ private Object handle(MethodInvocation mi, AuthorizationDeniedException denied) return this.defaultHandler.handleDeniedInvocation(mi, denied); } - private Object handle(MethodInvocation mi, AuthorizationResult decision) { + private Object handle(MethodInvocation mi, AuthorizationResult result) { if (this.authorizationManager instanceof MethodAuthorizationDeniedHandler handler) { - return handler.handleDeniedInvocation(mi, decision); + return handler.handleDeniedInvocation(mi, result); } - return this.defaultHandler.handleDeniedInvocation(mi, decision); + return this.defaultHandler.handleDeniedInvocation(mi, result); } private Authentication getAuthentication() { @@ -299,9 +298,4 @@ private Authentication getAuthentication() { return authentication; } - private static void noPublish(Supplier authentication, T object, - AuthorizationDecision decision) { - - } - } diff --git a/core/src/main/java/org/springframework/security/authorization/method/AuthorizationManagerBeforeReactiveMethodInterceptor.java b/core/src/main/java/org/springframework/security/authorization/method/AuthorizationManagerBeforeReactiveMethodInterceptor.java index ce9f94ae71b..50e1883e723 100644 --- a/core/src/main/java/org/springframework/security/authorization/method/AuthorizationManagerBeforeReactiveMethodInterceptor.java +++ b/core/src/main/java/org/springframework/security/authorization/method/AuthorizationManagerBeforeReactiveMethodInterceptor.java @@ -140,7 +140,7 @@ public Object invoke(MethodInvocation mi) throws Throwable { private Flux preAuthorized(MethodInvocation mi, Flux mapping) { Mono authentication = ReactiveAuthenticationUtils.getAuthentication(); - return this.authorizationManager.check(authentication, mi) + return this.authorizationManager.authorize(authentication, mi) .switchIfEmpty(Mono.just(new AuthorizationDecision(false))) .flatMapMany((decision) -> { if (decision.isGranted()) { @@ -153,7 +153,7 @@ private Flux preAuthorized(MethodInvocation mi, Flux mapping) { private Mono preAuthorized(MethodInvocation mi, Mono mapping) { Mono authentication = ReactiveAuthenticationUtils.getAuthentication(); - return this.authorizationManager.check(authentication, mi) + return this.authorizationManager.authorize(authentication, mi) .switchIfEmpty(Mono.just(new AuthorizationDecision(false))) .flatMap((decision) -> { if (decision.isGranted()) { diff --git a/core/src/main/java/org/springframework/security/authorization/method/Jsr250AuthorizationManager.java b/core/src/main/java/org/springframework/security/authorization/method/Jsr250AuthorizationManager.java index 74d03e78622..30c3977db5e 100644 --- a/core/src/main/java/org/springframework/security/authorization/method/Jsr250AuthorizationManager.java +++ b/core/src/main/java/org/springframework/security/authorization/method/Jsr250AuthorizationManager.java @@ -33,6 +33,7 @@ import org.springframework.security.authorization.AuthoritiesAuthorizationManager; import org.springframework.security.authorization.AuthorizationDecision; import org.springframework.security.authorization.AuthorizationManager; +import org.springframework.security.authorization.AuthorizationResult; import org.springframework.security.core.Authentication; import org.springframework.security.core.annotation.SecurityAnnotationScanner; import org.springframework.security.core.annotation.SecurityAnnotationScanners; @@ -86,7 +87,9 @@ public void setRolePrefix(String rolePrefix) { * @param methodInvocation the {@link MethodInvocation} to check * @return an {@link AuthorizationDecision} or null if the JSR-250 security * annotations is not present + * @deprecated please use {@link #authorize(Supplier, Object)} instead */ + @Deprecated @Override public AuthorizationDecision check(Supplier authentication, MethodInvocation methodInvocation) { AuthorizationManager delegate = this.registry.getManager(methodInvocation); @@ -109,8 +112,9 @@ AuthorizationManager resolveManager(Method method, Class ta return (a, o) -> new AuthorizationDecision(true); } if (annotation instanceof RolesAllowed rolesAllowed) { - return (a, o) -> Jsr250AuthorizationManager.this.authoritiesAuthorizationManager.check(a, - getAllowedRolesWithPrefix(rolesAllowed)); + return (AuthorizationManagerCheckAdapter) (a, + o) -> Jsr250AuthorizationManager.this.authoritiesAuthorizationManager.authorize(a, + getAllowedRolesWithPrefix(rolesAllowed)); } return NULL_MANAGER; } @@ -130,4 +134,23 @@ private Set getAllowedRolesWithPrefix(RolesAllowed rolesAllowed) { } + private interface AuthorizationManagerCheckAdapter extends AuthorizationManager { + + @Override + default AuthorizationDecision check(Supplier authentication, T object) { + AuthorizationResult result = authorize(authentication, object); + if (result == null) { + return null; + } + if (result instanceof AuthorizationDecision decision) { + return decision; + } + throw new IllegalArgumentException( + "please call #authorize or ensure that the result is of type AuthorizationDecision"); + } + + AuthorizationResult authorize(Supplier authentication, T object); + + } + } diff --git a/core/src/main/java/org/springframework/security/authorization/method/NoOpAuthorizationEventPublisher.java b/core/src/main/java/org/springframework/security/authorization/method/NoOpAuthorizationEventPublisher.java new file mode 100644 index 00000000000..298fbd8dc2b --- /dev/null +++ b/core/src/main/java/org/springframework/security/authorization/method/NoOpAuthorizationEventPublisher.java @@ -0,0 +1,45 @@ +/* + * 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. + * 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.method; + +import java.util.function.Supplier; + +import org.springframework.security.authorization.AuthorizationDecision; +import org.springframework.security.authorization.AuthorizationEventPublisher; +import org.springframework.security.authorization.AuthorizationResult; +import org.springframework.security.core.Authentication; + +/** + * An {@link AuthorizationEventPublisher} implementation that does nothing. + * + * @author Max Batischev + * @since 6.4 + */ +final class NoOpAuthorizationEventPublisher implements AuthorizationEventPublisher { + + @Override + public void publishAuthorizationEvent(Supplier authentication, T object, + AuthorizationDecision decision) { + } + + @Override + public void publishAuthorizationEvent(Supplier authentication, T object, + AuthorizationResult result) { + + } + +} diff --git a/core/src/main/java/org/springframework/security/authorization/method/SecuredAuthorizationManager.java b/core/src/main/java/org/springframework/security/authorization/method/SecuredAuthorizationManager.java index ba71b56626d..efd5a34faa7 100644 --- a/core/src/main/java/org/springframework/security/authorization/method/SecuredAuthorizationManager.java +++ b/core/src/main/java/org/springframework/security/authorization/method/SecuredAuthorizationManager.java @@ -73,7 +73,9 @@ public void setAuthoritiesAuthorizationManager( * @param mi the {@link MethodInvocation} to check * @return an {@link AuthorizationDecision} or null if the {@link Secured} annotation * is not present + * @deprecated please use {@link #authorize(Supplier, Object)} instead */ + @Deprecated @Override public AuthorizationDecision check(Supplier authentication, MethodInvocation mi) { Set authorities = getAuthorities(mi); diff --git a/core/src/test/java/org/springframework/security/authorization/method/AuthorizationManagerAfterMethodInterceptorTests.java b/core/src/test/java/org/springframework/security/authorization/method/AuthorizationManagerAfterMethodInterceptorTests.java index e34b45a33cb..d59b550f19b 100644 --- a/core/src/test/java/org/springframework/security/authorization/method/AuthorizationManagerAfterMethodInterceptorTests.java +++ b/core/src/test/java/org/springframework/security/authorization/method/AuthorizationManagerAfterMethodInterceptorTests.java @@ -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. @@ -42,6 +42,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.doCallRealMethod; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -73,6 +74,7 @@ public void beforeWhenMockAuthorizationManagerThenCheckAndReturnedObject() throw MethodInvocationResult result = new MethodInvocationResult(mockMethodInvocation, new Object()); given(mockMethodInvocation.proceed()).willReturn(result.getResult()); AuthorizationManager mockAuthorizationManager = mock(AuthorizationManager.class); + given(mockAuthorizationManager.authorize(any(), any())).willCallRealMethod(); AuthorizationManagerAfterMethodInterceptor advice = new AuthorizationManagerAfterMethodInterceptor( Pointcut.TRUE, mockAuthorizationManager); Object returnedObject = advice.invoke(mockMethodInvocation); @@ -127,6 +129,8 @@ public void invokeWhenAuthorizationEventPublisherThenUses() throws Throwable { AuthorizationManagerAfterMethodInterceptor advice = new AuthorizationManagerAfterMethodInterceptor( Pointcut.TRUE, AuthenticatedAuthorizationManager.authenticated()); AuthorizationEventPublisher eventPublisher = mock(AuthorizationEventPublisher.class); + doCallRealMethod().when(eventPublisher) + .publishAuthorizationEvent(any(Supplier.class), any(), any(AuthorizationResult.class)); advice.setAuthorizationEventPublisher(eventPublisher); SecurityContext securityContext = new SecurityContextImpl(); @@ -149,6 +153,7 @@ public void invokeWhenCustomAuthorizationDeniedExceptionThenThrows() throws Thro AuthorizationManager manager = mock(AuthorizationManager.class); given(manager.check(any(), any())) .willThrow(new MyAuthzDeniedException("denied", new AuthorizationDecision(false))); + given(manager.authorize(any(), any())).willCallRealMethod(); AuthorizationManagerAfterMethodInterceptor advice = new AuthorizationManagerAfterMethodInterceptor( Pointcut.TRUE, manager); assertThatExceptionOfType(MyAuthzDeniedException.class).isThrownBy(() -> advice.invoke(mi)); diff --git a/core/src/test/java/org/springframework/security/authorization/method/AuthorizationManagerAfterReactiveMethodInterceptorTests.java b/core/src/test/java/org/springframework/security/authorization/method/AuthorizationManagerAfterReactiveMethodInterceptorTests.java index b18fb4fee62..218d63bfb4b 100644 --- a/core/src/test/java/org/springframework/security/authorization/method/AuthorizationManagerAfterReactiveMethodInterceptorTests.java +++ b/core/src/test/java/org/springframework/security/authorization/method/AuthorizationManagerAfterReactiveMethodInterceptorTests.java @@ -72,6 +72,7 @@ public void invokeMonoWhenMockReactiveAuthorizationManagerThenVerify() throws Th ReactiveAuthorizationManager.class); given(mockReactiveAuthorizationManager.check(any(), any())) .willReturn(Mono.just(new AuthorizationDecision(true))); + given(mockReactiveAuthorizationManager.authorize(any(), any())).willCallRealMethod(); AuthorizationManagerAfterReactiveMethodInterceptor interceptor = new AuthorizationManagerAfterReactiveMethodInterceptor( Pointcut.TRUE, mockReactiveAuthorizationManager); Object result = interceptor.invoke(mockMethodInvocation); @@ -90,6 +91,7 @@ public void invokeFluxWhenMockReactiveAuthorizationManagerThenVerify() throws Th ReactiveAuthorizationManager.class); given(mockReactiveAuthorizationManager.check(any(), any())) .willReturn(Mono.just(new AuthorizationDecision(true))); + given(mockReactiveAuthorizationManager.authorize(any(), any())).willCallRealMethod(); AuthorizationManagerAfterReactiveMethodInterceptor interceptor = new AuthorizationManagerAfterReactiveMethodInterceptor( Pointcut.TRUE, mockReactiveAuthorizationManager); Object result = interceptor.invoke(mockMethodInvocation); @@ -109,6 +111,7 @@ public void invokeWhenMockReactiveAuthorizationManagerDeniedThenAccessDeniedExce ReactiveAuthorizationManager.class); given(mockReactiveAuthorizationManager.check(any(), any())) .willReturn(Mono.just(new AuthorizationDecision(false))); + given(mockReactiveAuthorizationManager.authorize(any(), any())).willCallRealMethod(); AuthorizationManagerAfterReactiveMethodInterceptor interceptor = new AuthorizationManagerAfterReactiveMethodInterceptor( Pointcut.TRUE, mockReactiveAuthorizationManager); Object result = interceptor.invoke(mockMethodInvocation); @@ -130,6 +133,7 @@ public void invokeFluxWhenAllValuesDeniedAndPostProcessorThenPostProcessorApplie given(mockReactiveAuthorizationManager.handleDeniedInvocationResult(any(), any(AuthorizationResult.class))) .willAnswer(this::masking); given(mockReactiveAuthorizationManager.check(any(), any())).willReturn(Mono.empty()); + given(mockReactiveAuthorizationManager.authorize(any(), any())).willCallRealMethod(); AuthorizationManagerAfterReactiveMethodInterceptor interceptor = new AuthorizationManagerAfterReactiveMethodInterceptor( Pointcut.TRUE, mockReactiveAuthorizationManager); Object result = interceptor.invoke(mockMethodInvocation); @@ -156,6 +160,7 @@ public void invokeFluxWhenOneValueDeniedAndPostProcessorThenPostProcessorApplied return Mono.just(argument.getResult()); }); given(mockReactiveAuthorizationManager.check(any(), any())).willReturn(Mono.empty()); + given(mockReactiveAuthorizationManager.authorize(any(), any())).willCallRealMethod(); AuthorizationManagerAfterReactiveMethodInterceptor interceptor = new AuthorizationManagerAfterReactiveMethodInterceptor( Pointcut.TRUE, mockReactiveAuthorizationManager); Object result = interceptor.invoke(mockMethodInvocation); @@ -176,6 +181,7 @@ public void invokeMonoWhenPostProcessableDecisionThenPostProcess() throws Throwa given(mockReactiveAuthorizationManager.handleDeniedInvocationResult(any(), any(AuthorizationResult.class))) .willAnswer(this::masking); given(mockReactiveAuthorizationManager.check(any(), any())).willReturn(Mono.empty()); + given(mockReactiveAuthorizationManager.authorize(any(), any())).willCallRealMethod(); AuthorizationManagerAfterReactiveMethodInterceptor interceptor = new AuthorizationManagerAfterReactiveMethodInterceptor( Pointcut.TRUE, mockReactiveAuthorizationManager); Object result = interceptor.invoke(mockMethodInvocation); @@ -195,6 +201,7 @@ public void invokeMonoWhenPostProcessableDecisionAndPostProcessResultIsMonoThenP given(mockReactiveAuthorizationManager.handleDeniedInvocationResult(any(), any(AuthorizationResult.class))) .willAnswer(this::monoMasking); given(mockReactiveAuthorizationManager.check(any(), any())).willReturn(Mono.empty()); + given(mockReactiveAuthorizationManager.authorize(any(), any())).willCallRealMethod(); AuthorizationManagerAfterReactiveMethodInterceptor interceptor = new AuthorizationManagerAfterReactiveMethodInterceptor( Pointcut.TRUE, mockReactiveAuthorizationManager); Object result = interceptor.invoke(mockMethodInvocation); @@ -214,6 +221,7 @@ public void invokeMonoWhenPostProcessableDecisionAndPostProcessResultIsNullThenP given(mockReactiveAuthorizationManager.handleDeniedInvocationResult(any(), any(AuthorizationResult.class))) .willReturn(null); given(mockReactiveAuthorizationManager.check(any(), any())).willReturn(Mono.empty()); + given(mockReactiveAuthorizationManager.authorize(any(), any())).willCallRealMethod(); AuthorizationManagerAfterReactiveMethodInterceptor interceptor = new AuthorizationManagerAfterReactiveMethodInterceptor( Pointcut.TRUE, mockReactiveAuthorizationManager); Object result = interceptor.invoke(mockMethodInvocation); @@ -231,6 +239,7 @@ public void invokeMonoWhenEmptyDecisionThenUseDefaultPostProcessor() throws Thro ReactiveAuthorizationManager mockReactiveAuthorizationManager = mock( ReactiveAuthorizationManager.class); given(mockReactiveAuthorizationManager.check(any(), any())).willReturn(Mono.empty()); + given(mockReactiveAuthorizationManager.authorize(any(), any())).willCallRealMethod(); AuthorizationManagerAfterReactiveMethodInterceptor interceptor = new AuthorizationManagerAfterReactiveMethodInterceptor( Pointcut.TRUE, mockReactiveAuthorizationManager); Object result = interceptor.invoke(mockMethodInvocation); @@ -249,6 +258,7 @@ public void invokeWhenCustomAuthorizationDeniedExceptionThenThrows() throws Thro ReactiveAuthorizationManager manager = mock(ReactiveAuthorizationManager.class); given(manager.check(any(), any())) .willReturn(Mono.error(new MyAuthzDeniedException("denied", new AuthorizationDecision(false)))); + given(manager.authorize(any(), any())).willCallRealMethod(); AuthorizationManagerAfterReactiveMethodInterceptor advice = new AuthorizationManagerAfterReactiveMethodInterceptor( Pointcut.TRUE, manager); assertThatExceptionOfType(MyAuthzDeniedException.class) diff --git a/core/src/test/java/org/springframework/security/authorization/method/AuthorizationManagerBeforeMethodInterceptorTests.java b/core/src/test/java/org/springframework/security/authorization/method/AuthorizationManagerBeforeMethodInterceptorTests.java index 8022609ac7b..eb0d1207b4e 100644 --- a/core/src/test/java/org/springframework/security/authorization/method/AuthorizationManagerBeforeMethodInterceptorTests.java +++ b/core/src/test/java/org/springframework/security/authorization/method/AuthorizationManagerBeforeMethodInterceptorTests.java @@ -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. @@ -41,6 +41,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.doCallRealMethod; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -69,6 +70,7 @@ public void instantiateWhenAuthorizationManagerNullThenException() { public void beforeWhenMockAuthorizationManagerThenCheck() throws Throwable { MethodInvocation mockMethodInvocation = mock(MethodInvocation.class); AuthorizationManager mockAuthorizationManager = mock(AuthorizationManager.class); + given(mockAuthorizationManager.authorize(any(), any())).willCallRealMethod(); AuthorizationManagerBeforeMethodInterceptor advice = new AuthorizationManagerBeforeMethodInterceptor( Pointcut.TRUE, mockAuthorizationManager); advice.invoke(mockMethodInvocation); @@ -121,6 +123,7 @@ public void invokeWhenAuthorizationEventPublisherThenUses() throws Throwable { AuthorizationManagerBeforeMethodInterceptor advice = new AuthorizationManagerBeforeMethodInterceptor( Pointcut.TRUE, AuthenticatedAuthorizationManager.authenticated()); AuthorizationEventPublisher eventPublisher = mock(AuthorizationEventPublisher.class); + doCallRealMethod().when(eventPublisher).publishAuthorizationEvent(any(), any(), any(AuthorizationResult.class)); advice.setAuthorizationEventPublisher(eventPublisher); SecurityContext securityContext = new SecurityContextImpl(); @@ -141,6 +144,7 @@ public void invokeWhenCustomAuthorizationDeniedExceptionThenThrows() { AuthorizationManager manager = mock(AuthorizationManager.class); given(manager.check(any(), any())) .willThrow(new MyAuthzDeniedException("denied", new AuthorizationDecision(false))); + given(manager.authorize(any(), any())).willCallRealMethod(); AuthorizationManagerBeforeMethodInterceptor advice = new AuthorizationManagerBeforeMethodInterceptor( Pointcut.TRUE, manager); assertThatExceptionOfType(MyAuthzDeniedException.class).isThrownBy(() -> advice.invoke(null)); diff --git a/core/src/test/java/org/springframework/security/authorization/method/AuthorizationManagerBeforeReactiveMethodInterceptorTests.java b/core/src/test/java/org/springframework/security/authorization/method/AuthorizationManagerBeforeReactiveMethodInterceptorTests.java index 400992eec6e..468e1bb3e53 100644 --- a/core/src/test/java/org/springframework/security/authorization/method/AuthorizationManagerBeforeReactiveMethodInterceptorTests.java +++ b/core/src/test/java/org/springframework/security/authorization/method/AuthorizationManagerBeforeReactiveMethodInterceptorTests.java @@ -72,6 +72,7 @@ public void invokeMonoWhenMockReactiveAuthorizationManagerThenVerify() throws Th ReactiveAuthorizationManager.class); given(mockReactiveAuthorizationManager.check(any(), eq(mockMethodInvocation))) .willReturn(Mono.just(new AuthorizationDecision(true))); + given(mockReactiveAuthorizationManager.authorize(any(), any())).willCallRealMethod(); AuthorizationManagerBeforeReactiveMethodInterceptor interceptor = new AuthorizationManagerBeforeReactiveMethodInterceptor( Pointcut.TRUE, mockReactiveAuthorizationManager); Object result = interceptor.invoke(mockMethodInvocation); @@ -90,6 +91,7 @@ public void invokeFluxWhenMockReactiveAuthorizationManagerThenVerify() throws Th ReactiveAuthorizationManager.class); given(mockReactiveAuthorizationManager.check(any(), eq(mockMethodInvocation))) .willReturn(Mono.just(new AuthorizationDecision((true)))); + given(mockReactiveAuthorizationManager.authorize(any(), any())).willCallRealMethod(); AuthorizationManagerBeforeReactiveMethodInterceptor interceptor = new AuthorizationManagerBeforeReactiveMethodInterceptor( Pointcut.TRUE, mockReactiveAuthorizationManager); Object result = interceptor.invoke(mockMethodInvocation); @@ -109,6 +111,7 @@ public void invokeWhenMockReactiveAuthorizationManagerDeniedThenAccessDeniedExce ReactiveAuthorizationManager.class); given(mockReactiveAuthorizationManager.check(any(), eq(mockMethodInvocation))) .willReturn(Mono.just(new AuthorizationDecision(false))); + given(mockReactiveAuthorizationManager.authorize(any(), any())).willCallRealMethod(); AuthorizationManagerBeforeReactiveMethodInterceptor interceptor = new AuthorizationManagerBeforeReactiveMethodInterceptor( Pointcut.TRUE, mockReactiveAuthorizationManager); Object result = interceptor.invoke(mockMethodInvocation); @@ -127,6 +130,7 @@ public void invokeMonoWhenDeniedAndPostProcessorThenInvokePostProcessor() throws HandlingReactiveAuthorizationManager mockReactiveAuthorizationManager = mock( HandlingReactiveAuthorizationManager.class); given(mockReactiveAuthorizationManager.check(any(), eq(mockMethodInvocation))).willReturn(Mono.empty()); + given(mockReactiveAuthorizationManager.authorize(any(), any())).willCallRealMethod(); given(mockReactiveAuthorizationManager.handleDeniedInvocation(any(), any(AuthorizationResult.class))) .willReturn("***"); AuthorizationManagerBeforeReactiveMethodInterceptor interceptor = new AuthorizationManagerBeforeReactiveMethodInterceptor( @@ -146,6 +150,7 @@ public void invokeMonoWhenDeniedAndMonoPostProcessorThenInvokePostProcessor() th HandlingReactiveAuthorizationManager mockReactiveAuthorizationManager = mock( HandlingReactiveAuthorizationManager.class); given(mockReactiveAuthorizationManager.check(any(), eq(mockMethodInvocation))).willReturn(Mono.empty()); + given(mockReactiveAuthorizationManager.authorize(any(), any())).willCallRealMethod(); given(mockReactiveAuthorizationManager.handleDeniedInvocation(any(), any(AuthorizationResult.class))) .willReturn(Mono.just("***")); AuthorizationManagerBeforeReactiveMethodInterceptor interceptor = new AuthorizationManagerBeforeReactiveMethodInterceptor( @@ -165,6 +170,7 @@ public void invokeFluxWhenDeniedAndPostProcessorThenInvokePostProcessor() throws HandlingReactiveAuthorizationManager mockReactiveAuthorizationManager = mock( HandlingReactiveAuthorizationManager.class); given(mockReactiveAuthorizationManager.check(any(), eq(mockMethodInvocation))).willReturn(Mono.empty()); + given(mockReactiveAuthorizationManager.authorize(any(), any())).willCallRealMethod(); given(mockReactiveAuthorizationManager.handleDeniedInvocation(any(), any(AuthorizationResult.class))) .willReturn(Mono.just("***")); AuthorizationManagerBeforeReactiveMethodInterceptor interceptor = new AuthorizationManagerBeforeReactiveMethodInterceptor( @@ -185,6 +191,7 @@ public void invokeMonoWhenEmptyDecisionThenInvokeDefaultPostProcessor() throws T ReactiveAuthorizationManager mockReactiveAuthorizationManager = mock( ReactiveAuthorizationManager.class); given(mockReactiveAuthorizationManager.check(any(), eq(mockMethodInvocation))).willReturn(Mono.empty()); + given(mockReactiveAuthorizationManager.authorize(any(), any())).willCallRealMethod(); AuthorizationManagerBeforeReactiveMethodInterceptor interceptor = new AuthorizationManagerBeforeReactiveMethodInterceptor( Pointcut.TRUE, mockReactiveAuthorizationManager); Object result = interceptor.invoke(mockMethodInvocation); @@ -203,6 +210,7 @@ public void invokeFluxWhenEmptyDecisionThenInvokeDefaultPostProcessor() throws T ReactiveAuthorizationManager mockReactiveAuthorizationManager = mock( ReactiveAuthorizationManager.class); given(mockReactiveAuthorizationManager.check(any(), eq(mockMethodInvocation))).willReturn(Mono.empty()); + given(mockReactiveAuthorizationManager.authorize(any(), any())).willCallRealMethod(); AuthorizationManagerBeforeReactiveMethodInterceptor interceptor = new AuthorizationManagerBeforeReactiveMethodInterceptor( Pointcut.TRUE, mockReactiveAuthorizationManager); Object result = interceptor.invoke(mockMethodInvocation); @@ -220,6 +228,7 @@ public void invokeWhenCustomAuthorizationDeniedExceptionThenThrows() throws Thro ReactiveAuthorizationManager manager = mock(ReactiveAuthorizationManager.class); given(manager.check(any(), any())) .willThrow(new MyAuthzDeniedException("denied", new AuthorizationDecision(false))); + given(manager.authorize(any(), any())).willCallRealMethod(); AuthorizationManagerBeforeReactiveMethodInterceptor advice = new AuthorizationManagerBeforeReactiveMethodInterceptor( Pointcut.TRUE, manager); assertThatExceptionOfType(MyAuthzDeniedException.class) diff --git a/core/src/test/java/org/springframework/security/authorization/method/Jsr250AuthorizationManagerTests.java b/core/src/test/java/org/springframework/security/authorization/method/Jsr250AuthorizationManagerTests.java index ba7324d6a51..1b7d6e7a228 100644 --- a/core/src/test/java/org/springframework/security/authorization/method/Jsr250AuthorizationManagerTests.java +++ b/core/src/test/java/org/springframework/security/authorization/method/Jsr250AuthorizationManagerTests.java @@ -38,6 +38,8 @@ import static org.assertj.core.api.Assertions.assertThat; 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.BDDMockito.given; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -78,6 +80,7 @@ public void setAuthoritiesAuthorizationManagerWhenNullThenException() { @Test public void setAuthoritiesAuthorizationManagerWhenNotNullThenVerifyUsage() throws Exception { AuthorizationManager> authoritiesAuthorizationManager = mock(AuthorizationManager.class); + given(authoritiesAuthorizationManager.authorize(any(), any())).willCallRealMethod(); Jsr250AuthorizationManager manager = new Jsr250AuthorizationManager(); manager.setAuthoritiesAuthorizationManager(authoritiesAuthorizationManager); MockMethodInvocation methodInvocation = new MockMethodInvocation(new ClassLevelAnnotations(), diff --git a/gradle.properties b/gradle.properties index b56058f98aa..30c658319db 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,7 +13,6 @@ # See the License for the specific language governing permissions and # limitations under the License. # - springBootVersion=3.3.3 version=6.4.0-SNAPSHOT samplesBranch=main diff --git a/messaging/src/main/java/org/springframework/security/messaging/access/intercept/AuthorizationChannelInterceptor.java b/messaging/src/main/java/org/springframework/security/messaging/access/intercept/AuthorizationChannelInterceptor.java index c61e3b660e8..c17e2b8de94 100644 --- a/messaging/src/main/java/org/springframework/security/messaging/access/intercept/AuthorizationChannelInterceptor.java +++ b/messaging/src/main/java/org/springframework/security/messaging/access/intercept/AuthorizationChannelInterceptor.java @@ -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; import org.springframework.security.core.context.SecurityContextHolderStrategy; @@ -66,11 +67,11 @@ public AuthorizationChannelInterceptor(AuthorizationManager> preSendA @Override public Message preSend(Message message, MessageChannel channel) { this.logger.debug(LogMessage.of(() -> "Authorizing message send")); - AuthorizationDecision decision = this.preSendAuthorizationManager.check(this.authentication, message); - this.eventPublisher.publishAuthorizationEvent(this.authentication, message, decision); - if (decision == null || !decision.isGranted()) { // default deny + AuthorizationResult result = this.preSendAuthorizationManager.authorize(this.authentication, message); + this.eventPublisher.publishAuthorizationEvent(this.authentication, message, result); + if (result == null || !result.isGranted()) { // default deny this.logger.debug(LogMessage.of(() -> "Failed to authorize message with authorization manager " - + this.preSendAuthorizationManager + " and decision " + decision)); + + this.preSendAuthorizationManager + " and result " + result)); throw new AccessDeniedException("Access Denied"); } this.logger.debug(LogMessage.of(() -> "Authorized message send")); @@ -114,6 +115,12 @@ public void publishAuthorizationEvent(Supplier authenticatio } + @Override + public void publishAuthorizationEvent(Supplier authentication, T object, + AuthorizationResult result) { + + } + } } diff --git a/messaging/src/main/java/org/springframework/security/messaging/access/intercept/MessageMatcherDelegatingAuthorizationManager.java b/messaging/src/main/java/org/springframework/security/messaging/access/intercept/MessageMatcherDelegatingAuthorizationManager.java index d442e7532cf..d735fc5a422 100644 --- a/messaging/src/main/java/org/springframework/security/messaging/access/intercept/MessageMatcherDelegatingAuthorizationManager.java +++ b/messaging/src/main/java/org/springframework/security/messaging/access/intercept/MessageMatcherDelegatingAuthorizationManager.java @@ -60,7 +60,9 @@ private MessageMatcherDelegatingAuthorizationManager( * @return an {@link AuthorizationDecision}. If there is no {@link MessageMatcher} * matching the message, or the {@link AuthorizationManager} could not decide, then * null is returned + * @deprecated please use {@link #authorize(Supplier, Object)} instead */ + @Deprecated @Override public AuthorizationDecision check(Supplier authentication, Message message) { if (this.logger.isTraceEnabled()) { diff --git a/messaging/src/test/java/org/springframework/security/messaging/access/intercept/AuthorizationChannelInterceptorTests.java b/messaging/src/test/java/org/springframework/security/messaging/access/intercept/AuthorizationChannelInterceptorTests.java index fc354ed212c..debb21b7622 100644 --- a/messaging/src/test/java/org/springframework/security/messaging/access/intercept/AuthorizationChannelInterceptorTests.java +++ b/messaging/src/test/java/org/springframework/security/messaging/access/intercept/AuthorizationChannelInterceptorTests.java @@ -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. @@ -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; @@ -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; /** @@ -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)); } @@ -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()); } diff --git a/rsocket/src/main/java/org/springframework/security/rsocket/authorization/PayloadExchangeMatcherReactiveAuthorizationManager.java b/rsocket/src/main/java/org/springframework/security/rsocket/authorization/PayloadExchangeMatcherReactiveAuthorizationManager.java index 4f13b9fbfaa..fc7862f51e2 100644 --- a/rsocket/src/main/java/org/springframework/security/rsocket/authorization/PayloadExchangeMatcherReactiveAuthorizationManager.java +++ b/rsocket/src/main/java/org/springframework/security/rsocket/authorization/PayloadExchangeMatcherReactiveAuthorizationManager.java @@ -50,6 +50,10 @@ private PayloadExchangeMatcherReactiveAuthorizationManager( this.mappings = mappings; } + /** + * @deprecated please use {@link #authorize(Mono, Object)} instead + */ + @Deprecated @Override public Mono check(Mono authentication, PayloadExchange exchange) { return Flux.fromIterable(this.mappings) diff --git a/web/src/main/java/org/springframework/security/web/access/AuthorizationManagerWebInvocationPrivilegeEvaluator.java b/web/src/main/java/org/springframework/security/web/access/AuthorizationManagerWebInvocationPrivilegeEvaluator.java index e36a54fda6e..a181db2cc0d 100644 --- a/web/src/main/java/org/springframework/security/web/access/AuthorizationManagerWebInvocationPrivilegeEvaluator.java +++ b/web/src/main/java/org/springframework/security/web/access/AuthorizationManagerWebInvocationPrivilegeEvaluator.java @@ -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. @@ -19,8 +19,8 @@ import jakarta.servlet.ServletContext; import jakarta.servlet.http.HttpServletRequest; -import org.springframework.security.authorization.AuthorizationDecision; import org.springframework.security.authorization.AuthorizationManager; +import org.springframework.security.authorization.AuthorizationResult; import org.springframework.security.core.Authentication; import org.springframework.security.web.FilterInvocation; import org.springframework.util.Assert; @@ -57,8 +57,8 @@ public boolean isAllowed(String uri, Authentication authentication) { public boolean isAllowed(String contextPath, String uri, String method, Authentication authentication) { FilterInvocation filterInvocation = new FilterInvocation(contextPath, uri, method, this.servletContext); HttpServletRequest httpRequest = this.requestTransformer.transform(filterInvocation.getHttpRequest()); - AuthorizationDecision decision = this.authorizationManager.check(() -> authentication, httpRequest); - return decision == null || decision.isGranted(); + AuthorizationResult result = this.authorizationManager.authorize(() -> authentication, httpRequest); + return result == null || result.isGranted(); } @Override diff --git a/web/src/main/java/org/springframework/security/web/access/intercept/AuthorizationFilter.java b/web/src/main/java/org/springframework/security/web/access/intercept/AuthorizationFilter.java index bc09dc05732..80659363f21 100644 --- a/web/src/main/java/org/springframework/security/web/access/intercept/AuthorizationFilter.java +++ b/web/src/main/java/org/springframework/security/web/access/intercept/AuthorizationFilter.java @@ -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. @@ -33,6 +33,7 @@ import org.springframework.security.authorization.AuthorizationDeniedException; import org.springframework.security.authorization.AuthorizationEventPublisher; import org.springframework.security.authorization.AuthorizationManager; +import org.springframework.security.authorization.AuthorizationResult; import org.springframework.security.authorization.event.AuthorizationDeniedEvent; import org.springframework.security.authorization.event.AuthorizationGrantedEvent; import org.springframework.security.core.Authentication; @@ -55,7 +56,7 @@ public class AuthorizationFilter extends GenericFilterBean { private final AuthorizationManager authorizationManager; - private AuthorizationEventPublisher eventPublisher = AuthorizationFilter::noPublish; + private AuthorizationEventPublisher eventPublisher = new NoopAuthorizationEventPublisher(); private boolean observeOncePerRequest = false; @@ -92,10 +93,10 @@ public void doFilter(ServletRequest servletRequest, ServletResponse servletRespo String alreadyFilteredAttributeName = getAlreadyFilteredAttributeName(); request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE); try { - AuthorizationDecision decision = this.authorizationManager.check(this::getAuthentication, request); - this.eventPublisher.publishAuthorizationEvent(this::getAuthentication, request, decision); - if (decision != null && !decision.isGranted()) { - throw new AuthorizationDeniedException("Access Denied", decision); + AuthorizationResult result = this.authorizationManager.authorize(this::getAuthentication, request); + this.eventPublisher.publishAuthorizationEvent(this::getAuthentication, request, result); + if (result != null && !result.isGranted()) { + throw new AuthorizationDeniedException("Access Denied", result); } chain.doFilter(request, response); } @@ -195,11 +196,6 @@ public void setShouldFilterAllDispatcherTypes(boolean shouldFilterAllDispatcherT this.filterAsyncDispatch = shouldFilterAllDispatcherTypes; } - private static void noPublish(Supplier authentication, T object, - AuthorizationDecision decision) { - - } - public boolean isObserveOncePerRequest() { return this.observeOncePerRequest; } @@ -235,4 +231,19 @@ public void setFilterAsyncDispatch(boolean filterAsyncDispatch) { this.filterAsyncDispatch = filterAsyncDispatch; } + private static class NoopAuthorizationEventPublisher implements AuthorizationEventPublisher { + + @Override + public void publishAuthorizationEvent(Supplier authentication, T object, + AuthorizationDecision decision) { + + } + + @Override + public void publishAuthorizationEvent(Supplier authentication, T object, + AuthorizationResult result) { + } + + } + } diff --git a/web/src/main/java/org/springframework/security/web/access/intercept/RequestMatcherDelegatingAuthorizationManager.java b/web/src/main/java/org/springframework/security/web/access/intercept/RequestMatcherDelegatingAuthorizationManager.java index da5b5ec1ec6..1c26f9b0a4d 100644 --- a/web/src/main/java/org/springframework/security/web/access/intercept/RequestMatcherDelegatingAuthorizationManager.java +++ b/web/src/main/java/org/springframework/security/web/access/intercept/RequestMatcherDelegatingAuthorizationManager.java @@ -68,7 +68,9 @@ private RequestMatcherDelegatingAuthorizationManager( * @return an {@link AuthorizationDecision}. If there is no {@link RequestMatcher} * matching the request, or the {@link AuthorizationManager} could not decide, then * null is returned + * @deprecated please use {@link #authorize(Supplier, Object)} instead */ + @Deprecated @Override public AuthorizationDecision check(Supplier authentication, HttpServletRequest request) { if (this.logger.isTraceEnabled()) { diff --git a/web/src/main/java/org/springframework/security/web/server/authorization/DelegatingReactiveAuthorizationManager.java b/web/src/main/java/org/springframework/security/web/server/authorization/DelegatingReactiveAuthorizationManager.java index 7e61d9911e8..ebdb2b8ccba 100644 --- a/web/src/main/java/org/springframework/security/web/server/authorization/DelegatingReactiveAuthorizationManager.java +++ b/web/src/main/java/org/springframework/security/web/server/authorization/DelegatingReactiveAuthorizationManager.java @@ -48,6 +48,10 @@ private DelegatingReactiveAuthorizationManager( this.mappings = mappings; } + /** + * @deprecated please use {@link #authorize(Mono, Object)} instead + */ + @Deprecated @Override public Mono check(Mono authentication, ServerWebExchange exchange) { return Flux.fromIterable(this.mappings) diff --git a/web/src/test/java/org/springframework/security/web/access/AuthorizationManagerWebInvocationPrivilegeEvaluatorTests.java b/web/src/test/java/org/springframework/security/web/access/AuthorizationManagerWebInvocationPrivilegeEvaluatorTests.java index c5cb7669c1b..afb5ad9e975 100644 --- a/web/src/test/java/org/springframework/security/web/access/AuthorizationManagerWebInvocationPrivilegeEvaluatorTests.java +++ b/web/src/test/java/org/springframework/security/web/access/AuthorizationManagerWebInvocationPrivilegeEvaluatorTests.java @@ -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. @@ -61,6 +61,7 @@ void constructorWhenAuthorizationManagerNullThenIllegalArgument() { @Test void isAllowedWhenAuthorizationManagerAllowsThenAllowedTrue() { given(this.authorizationManager.check(any(), any())).willReturn(new AuthorizationDecision(true)); + given(this.authorizationManager.authorize(any(), any())).willCallRealMethod(); boolean allowed = this.privilegeEvaluator.isAllowed("/test", TestAuthentication.authenticatedUser()); assertThat(allowed).isTrue(); verify(this.authorizationManager).check(any(), any()); @@ -69,6 +70,7 @@ void isAllowedWhenAuthorizationManagerAllowsThenAllowedTrue() { @Test void isAllowedWhenAuthorizationManagerDeniesAllowedFalse() { given(this.authorizationManager.check(any(), any())).willReturn(new AuthorizationDecision(false)); + given(this.authorizationManager.authorize(any(), any())).willCallRealMethod(); boolean allowed = this.privilegeEvaluator.isAllowed("/test", TestAuthentication.authenticatedUser()); assertThat(allowed).isFalse(); } @@ -76,6 +78,7 @@ void isAllowedWhenAuthorizationManagerDeniesAllowedFalse() { @Test void isAllowedWhenAuthorizationManagerAbstainsThenAllowedTrue() { given(this.authorizationManager.check(any(), any())).willReturn(null); + given(this.authorizationManager.authorize(any(), any())).willCallRealMethod(); boolean allowed = this.privilegeEvaluator.isAllowed("/test", TestAuthentication.authenticatedUser()); assertThat(allowed).isTrue(); } @@ -83,6 +86,7 @@ void isAllowedWhenAuthorizationManagerAbstainsThenAllowedTrue() { @Test void isAllowedWhenServletContextExistsThenFilterInvocationHasServletContext() { ServletContext servletContext = new MockServletContext(); + given(this.authorizationManager.authorize(any(), any())).willCallRealMethod(); this.privilegeEvaluator.setServletContext(servletContext); this.privilegeEvaluator.isAllowed("/test", TestAuthentication.authenticatedUser()); ArgumentCaptor captor = ArgumentCaptor.forClass(HttpServletRequest.class); @@ -99,6 +103,7 @@ void setRequestTransformerWhenNullThenIllegalArgumentException() { void isAllowedWhenRequestTransformerThenUsesRequestTransformerResult() { HttpServletRequest request = new MockHttpServletRequest(); given(this.requestTransformer.transform(any())).willReturn(request); + given(this.authorizationManager.authorize(any(), any())).willCallRealMethod(); this.privilegeEvaluator.setRequestTransformer(this.requestTransformer); this.privilegeEvaluator.isAllowed("/test", TestAuthentication.authenticatedUser()); diff --git a/web/src/test/java/org/springframework/security/web/access/intercept/AuthorizationFilterTests.java b/web/src/test/java/org/springframework/security/web/access/intercept/AuthorizationFilterTests.java index e24fb6fd034..ba4825d7842 100644 --- a/web/src/test/java/org/springframework/security/web/access/intercept/AuthorizationFilterTests.java +++ b/web/src/test/java/org/springframework/security/web/access/intercept/AuthorizationFilterTests.java @@ -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. @@ -38,6 +38,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.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; @@ -53,6 +54,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.willThrow; +import static org.mockito.Mockito.doCallRealMethod; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; @@ -91,6 +93,7 @@ public void tearDown() { @Test public void filterWhenAuthorizationManagerVerifyPassesThenNextFilter() throws Exception { AuthorizationManager mockAuthorizationManager = mock(AuthorizationManager.class); + given(mockAuthorizationManager.authorize(any(), any())).willCallRealMethod(); given(mockAuthorizationManager.check(any(Supplier.class), any(HttpServletRequest.class))) .willReturn(new AuthorizationDecision(true)); AuthorizationFilter filter = new AuthorizationFilter(mockAuthorizationManager); @@ -118,6 +121,7 @@ public void filterWhenAuthorizationManagerVerifyPassesThenNextFilter() throws Ex @Test public void filterWhenAuthorizationManagerVerifyThrowsAccessDeniedExceptionThenStopFilterChain() { AuthorizationManager mockAuthorizationManager = mock(AuthorizationManager.class); + given(mockAuthorizationManager.authorize(any(), any())).willCallRealMethod(); AuthorizationFilter filter = new AuthorizationFilter(mockAuthorizationManager); TestingAuthenticationToken authenticationToken = new TestingAuthenticationToken("user", "password"); @@ -186,6 +190,7 @@ public void doFilterWhenAuthorizationEventPublisherThenUses() throws Exception { SecurityContextHolder.setContext(securityContext); AuthorizationEventPublisher eventPublisher = mock(AuthorizationEventPublisher.class); + doCallRealMethod().when(eventPublisher).publishAuthorizationEvent(any(), any(), any(AuthorizationResult.class)); authorizationFilter.setAuthorizationEventPublisher(eventPublisher); authorizationFilter.doFilter(mockRequest, mockResponse, mockFilterChain); verify(eventPublisher).publishAuthorizationEvent(any(Supplier.class), any(HttpServletRequest.class), @@ -195,6 +200,7 @@ public void doFilterWhenAuthorizationEventPublisherThenUses() throws Exception { @Test public void doFilterWhenErrorThenDoFilter() throws Exception { AuthorizationManager authorizationManager = mock(AuthorizationManager.class); + given(authorizationManager.authorize(any(), any())).willCallRealMethod(); AuthorizationFilter authorizationFilter = new AuthorizationFilter(authorizationManager); MockHttpServletRequest mockRequest = new MockHttpServletRequest(null, "/path"); mockRequest.setDispatcherType(DispatcherType.ERROR); @@ -231,6 +237,7 @@ public void doFilterWhenObserveOncePerRequestTrueAndIsAppliedThenNotInvoked() th @Test public void doFilterWhenObserveOncePerRequestTrueAndNotAppliedThenInvoked() throws ServletException, IOException { + given(this.authorizationManager.authorize(any(), any())).willCallRealMethod(); this.filter.setObserveOncePerRequest(true); this.filter.doFilter(this.request, this.response, this.chain); verify(this.authorizationManager).check(any(), any()); @@ -239,6 +246,7 @@ public void doFilterWhenObserveOncePerRequestTrueAndNotAppliedThenInvoked() thro @Test public void doFilterWhenObserveOncePerRequestFalseAndIsAppliedThenInvoked() throws ServletException, IOException { setIsAppliedTrue(); + given(this.authorizationManager.authorize(any(), any())).willCallRealMethod(); this.filter.setObserveOncePerRequest(false); this.filter.doFilter(this.request, this.response, this.chain); verify(this.authorizationManager).check(any(), any()); @@ -246,6 +254,7 @@ public void doFilterWhenObserveOncePerRequestFalseAndIsAppliedThenInvoked() thro @Test public void doFilterWhenObserveOncePerRequestFalseAndNotAppliedThenInvoked() throws ServletException, IOException { + given(this.authorizationManager.authorize(any(), any())).willCallRealMethod(); this.filter.setObserveOncePerRequest(false); this.filter.doFilter(this.request, this.response, this.chain); verify(this.authorizationManager).check(any(), any()); @@ -261,6 +270,7 @@ public void doFilterWhenFilterErrorDispatchFalseAndIsErrorThenNotInvoked() throw @Test public void doFilterWhenFilterErrorDispatchTrueAndIsErrorThenInvoked() throws ServletException, IOException { + given(this.authorizationManager.authorize(any(), any())).willCallRealMethod(); this.request.setDispatcherType(DispatcherType.ERROR); this.filter.setFilterErrorDispatch(true); this.filter.doFilter(this.request, this.response, this.chain); @@ -284,6 +294,7 @@ public void doFilterWhenFilterThenRemoveAlreadyFilteredAttribute() throws Servle @Test public void doFilterWhenFilterAsyncDispatchTrueAndIsAsyncThenInvoked() throws ServletException, IOException { + given(this.authorizationManager.authorize(any(), any())).willCallRealMethod(); this.request.setDispatcherType(DispatcherType.ASYNC); this.filter.setFilterAsyncDispatch(true); this.filter.doFilter(this.request, this.response, this.chain);