From 863e016c1f135f704f6a883fd0e80baa04227b9a Mon Sep 17 00:00:00 2001 From: Evgeniy Cheban Date: Fri, 18 Apr 2025 13:11:17 +0300 Subject: [PATCH 1/2] Replace deprecated #check calls with #authorize Closes gh-16936 Signed-off-by: Evgeniy Cheban --- .../AuthorizeHttpRequestsConfigurerTests.java | 6 ++++- .../authorization/AuthorizationManager.java | 8 +++---- .../ObservationAuthorizationManager.java | 23 +++++++++++++++---- .../ObservationAuthorizationManagerTests.java | 5 +++- ...MatcherDelegatingAuthorizationManager.java | 18 +++++++++++++-- ...MatcherDelegatingAuthorizationManager.java | 18 +++++++++++++-- 6 files changed, 63 insertions(+), 15 deletions(-) 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 057deea40c..7d57cae201 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-2024 the original author or authors. + * Copyright 2002-2025 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. @@ -85,6 +85,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.any; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doCallRealMethod; @@ -153,6 +154,7 @@ public void configureWhenMvcMatcherAfterAnyRequestThenException() { @Test public void configureMvcMatcherAccessAuthorizationManagerWhenNotNullThenVerifyUse() throws Exception { CustomAuthorizationManagerConfig.authorizationManager = mock(AuthorizationManager.class); + given(CustomAuthorizationManagerConfig.authorizationManager.authorize(any(), any())).willCallRealMethod(); this.spring.register(CustomAuthorizationManagerConfig.class, BasicController.class).autowire(); this.mvc.perform(get("/")).andExpect(status().isOk()); verify(CustomAuthorizationManagerConfig.authorizationManager).check(any(), any()); @@ -161,6 +163,8 @@ public void configureMvcMatcherAccessAuthorizationManagerWhenNotNullThenVerifyUs @Test public void configureNoParameterMvcMatcherAccessAuthorizationManagerWhenNotNullThenVerifyUse() throws Exception { CustomAuthorizationManagerNoParameterConfig.authorizationManager = mock(AuthorizationManager.class); + given(CustomAuthorizationManagerNoParameterConfig.authorizationManager.authorize(any(), any())) + .willCallRealMethod(); this.spring.register(CustomAuthorizationManagerNoParameterConfig.class, BasicController.class).autowire(); this.mvc.perform(get("/")).andExpect(status().isOk()); verify(CustomAuthorizationManagerNoParameterConfig.authorizationManager).check(any(), any()); 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 758abce2c7..b039068b22 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-2024 the original author or authors. + * Copyright 2002-2025 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. @@ -39,9 +39,9 @@ public interface AuthorizationManager { * @throws AccessDeniedException if access is not granted */ default void verify(Supplier authentication, T object) { - AuthorizationDecision decision = check(authentication, object); - if (decision != null && !decision.isGranted()) { - throw new AuthorizationDeniedException("Access Denied", decision); + AuthorizationResult result = authorize(authentication, object); + if (result != null && !result.isGranted()) { + throw new AuthorizationDeniedException("Access Denied", result); } } 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 0494dbfecb..2c4a785f30 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-2024 the original author or authors. + * Copyright 2002-2025 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. @@ -67,6 +67,19 @@ public ObservationAuthorizationManager(ObservationRegistry registry, Authorizati @Deprecated @Override public 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 returned result is of type AuthorizationDecision"); + } + + @Override + public AuthorizationResult authorize(Supplier authentication, T object) { AuthorizationObservationContext context = new AuthorizationObservationContext<>(object); Supplier wrapped = () -> { context.setAuthentication(authentication.get()); @@ -74,13 +87,13 @@ 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.setAuthorizationResult(decision); - if (decision != null && !decision.isGranted()) { + AuthorizationResult result = this.delegate.authorize(wrapped, object); + context.setAuthorizationResult(result); + if (result != null && !result.isGranted()) { observation.error(new AccessDeniedException( this.messages.getMessage("AbstractAccessDecisionManager.accessDenied", "Access Denied"))); } - return decision; + return result; } catch (Throwable ex) { observation.error(ex); diff --git a/core/src/test/java/org/springframework/security/authorization/ObservationAuthorizationManagerTests.java b/core/src/test/java/org/springframework/security/authorization/ObservationAuthorizationManagerTests.java index 1921f7dc1a..bb2ccd8faa 100644 --- a/core/src/test/java/org/springframework/security/authorization/ObservationAuthorizationManagerTests.java +++ b/core/src/test/java/org/springframework/security/authorization/ObservationAuthorizationManagerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2025 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. @@ -74,6 +74,7 @@ void setup() { void verifyWhenDefaultsThenObserves() { given(this.handler.supportsContext(any())).willReturn(true); given(this.authorizationManager.check(any(), any())).willReturn(this.grant); + given(this.authorizationManager.authorize(any(), any())).willCallRealMethod(); this.tested.verify(this.token, this.object); ArgumentCaptor captor = ArgumentCaptor.forClass(Observation.Context.class); verify(this.handler).onStart(captor.capture()); @@ -92,6 +93,7 @@ void verifyWhenErrorsThenObserves() { this.tested.setMessageSource(source); given(this.handler.supportsContext(any())).willReturn(true); given(this.authorizationManager.check(any(), any())).willReturn(this.deny); + given(this.authorizationManager.authorize(any(), any())).willCallRealMethod(); given(source.getMessage(eq("AbstractAccessDecisionManager.accessDenied"), any(), any(), any())) .willReturn("accessDenied"); assertThatExceptionOfType(AccessDeniedException.class) @@ -116,6 +118,7 @@ void verifyWhenLooksUpAuthenticationThenObserves() { ((Supplier) invocation.getArgument(0)).get(); return this.grant; }); + given(this.authorizationManager.authorize(any(), any())).willCallRealMethod(); this.tested.verify(this.token, this.object); ArgumentCaptor captor = ArgumentCaptor.forClass(Observation.Context.class); verify(this.handler).onStart(captor.capture()); 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 4a2b3de56f..3e43d60f04 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 @@ -30,6 +30,7 @@ import org.springframework.security.authorization.AuthorityAuthorizationManager; import org.springframework.security.authorization.AuthorizationDecision; import org.springframework.security.authorization.AuthorizationManager; +import org.springframework.security.authorization.AuthorizationResult; import org.springframework.security.authorization.SingleResultAuthorizationManager; import org.springframework.security.core.Authentication; import org.springframework.security.messaging.util.matcher.MessageMatcher; @@ -63,11 +64,24 @@ 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 please use {@link #authorize(Supplier, Message)} instead */ @Deprecated @Override public AuthorizationDecision check(Supplier authentication, Message message) { + AuthorizationResult result = authorize(authentication, message); + if (result == null) { + return null; + } + if (result instanceof AuthorizationDecision decision) { + return decision; + } + throw new IllegalArgumentException( + "Please call #authorize or ensure that the returned result is of type AuthorizationDecision"); + } + + @Override + public AuthorizationResult authorize(Supplier authentication, Message message) { if (this.logger.isTraceEnabled()) { this.logger.trace(LogMessage.format("Authorizing message")); } @@ -79,7 +93,7 @@ public AuthorizationDecision check(Supplier authentication, Mess if (this.logger.isTraceEnabled()) { this.logger.trace(LogMessage.format("Checking authorization on message using %s", manager)); } - return manager.check(authentication, authorizationContext); + return manager.authorize(authentication, authorizationContext); } } this.logger.trace("Abstaining since did not find matching MessageMatcher"); 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 b01ab43bbd..de660a3f97 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 @@ -30,6 +30,7 @@ import org.springframework.security.authorization.AuthorityAuthorizationManager; import org.springframework.security.authorization.AuthorizationDecision; import org.springframework.security.authorization.AuthorizationManager; +import org.springframework.security.authorization.AuthorizationResult; import org.springframework.security.authorization.SingleResultAuthorizationManager; import org.springframework.security.core.Authentication; import org.springframework.security.web.util.UrlUtils; @@ -69,11 +70,24 @@ 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 please use {@link #authorize(Supplier, HttpServletRequest)} instead */ @Deprecated @Override public AuthorizationDecision check(Supplier authentication, HttpServletRequest request) { + AuthorizationResult result = authorize(authentication, request); + if (result == null) { + return null; + } + if (result instanceof AuthorizationDecision decision) { + return decision; + } + throw new IllegalArgumentException( + "Please call #authorize or ensure that the returned result is of type AuthorizationDecision"); + } + + @Override + public AuthorizationResult authorize(Supplier authentication, HttpServletRequest request) { if (this.logger.isTraceEnabled()) { this.logger.trace(LogMessage.format("Authorizing %s", requestLine(request))); } @@ -87,7 +101,7 @@ public AuthorizationDecision check(Supplier authentication, Http this.logger.trace( LogMessage.format("Checking authorization on %s using %s", requestLine(request), manager)); } - return manager.check(authentication, + return manager.authorize(authentication, new RequestAuthorizationContext(request, matchResult.getVariables())); } } From 1172581703acfc3bd85208d4dd73cb9a74fc49aa Mon Sep 17 00:00:00 2001 From: Evgeniy Cheban Date: Thu, 24 Apr 2025 23:54:37 +0300 Subject: [PATCH 2/2] ReactiveAuthorizationManager replace deprecated #check calls with #authorize Closes gh-16936 Signed-off-by: Evgeniy Cheban --- ...servationReactiveAuthorizationManager.java | 19 +++++++++++++---- .../ReactiveAuthorizationManager.java | 6 +++--- ...tionReactiveAuthorizationManagerTests.java | 5 ++++- ...geMatcherReactiveAuthorizationManager.java | 18 +++++++++++++--- ...cherReactiveAuthorizationManagerTests.java | 6 +++++- ...elegatingReactiveAuthorizationManager.java | 21 +++++++++++++++---- ...tingReactiveAuthorizationManagerTests.java | 4 +++- 7 files changed, 62 insertions(+), 17 deletions(-) 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 a9d77c641b..0ef9b3a4be 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-2024 the original author or authors. + * Copyright 2002-2025 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. @@ -62,6 +62,17 @@ public ObservationReactiveAuthorizationManager(ObservationRegistry registry, @Deprecated @Override public Mono check(Mono authentication, T object) { + return authorize(authentication, object).flatMap((result) -> { + if (result instanceof AuthorizationDecision decision) { + return Mono.just(decision); + } + return Mono.error(new IllegalArgumentException( + "Please call #authorize or ensure that the returned result is of type Mono")); + }); + } + + @Override + public Mono authorize(Mono authentication, T object) { AuthorizationObservationContext context = new AuthorizationObservationContext<>(object); Mono wrapped = authentication.map((auth) -> { context.setAuthentication(auth); @@ -71,9 +82,9 @@ public Mono check(Mono authentication, T Observation observation = Observation.createNotStarted(this.convention, () -> context, this.registry) .parentObservation(contextView.getOrDefault(ObservationThreadLocalAccessor.KEY, null)) .start(); - return this.delegate.check(wrapped, object).doOnSuccess((decision) -> { - context.setAuthorizationResult(decision); - if (decision == null || !decision.isGranted()) { + return this.delegate.authorize(wrapped, object).doOnSuccess((result) -> { + context.setAuthorizationResult(result); + if (result == null || !result.isGranted()) { observation.error(new AccessDeniedException("Access Denied")); } observation.stop(); 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 05662737d1..860208c6f5 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-2024 the original author or authors. + * Copyright 2002-2025 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,8 @@ public interface ReactiveAuthorizationManager { */ default Mono verify(Mono authentication, T object) { // @formatter:off - return check(authentication, object) - .filter(AuthorizationDecision::isGranted) + return authorize(authentication, object) + .filter(AuthorizationResult::isGranted) .switchIfEmpty(Mono.defer(() -> Mono.error(new AccessDeniedException("Access Denied")))) .flatMap((decision) -> Mono.empty()); // @formatter:on diff --git a/core/src/test/java/org/springframework/security/authorization/ObservationReactiveAuthorizationManagerTests.java b/core/src/test/java/org/springframework/security/authorization/ObservationReactiveAuthorizationManagerTests.java index 34b0533a26..d3eb935cc7 100644 --- a/core/src/test/java/org/springframework/security/authorization/ObservationReactiveAuthorizationManagerTests.java +++ b/core/src/test/java/org/springframework/security/authorization/ObservationReactiveAuthorizationManagerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * Copyright 2002-2025 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. @@ -70,6 +70,7 @@ void setup() { void verifyWhenDefaultsThenObserves() { given(this.handler.supportsContext(any())).willReturn(true); given(this.authorizationManager.check(any(), any())).willReturn(Mono.just(this.grant)); + given(this.authorizationManager.authorize(any(), any())).willCallRealMethod(); this.tested.verify(this.token, this.object).block(); ArgumentCaptor captor = ArgumentCaptor.forClass(Observation.Context.class); verify(this.handler).onStart(captor.capture()); @@ -86,6 +87,7 @@ void verifyWhenDefaultsThenObserves() { void verifyWhenErrorsThenObserves() { given(this.handler.supportsContext(any())).willReturn(true); given(this.authorizationManager.check(any(), any())).willReturn(Mono.just(this.deny)); + given(this.authorizationManager.authorize(any(), any())).willCallRealMethod(); assertThatExceptionOfType(AccessDeniedException.class) .isThrownBy(() -> this.tested.verify(this.token, this.object).block()); ArgumentCaptor captor = ArgumentCaptor.forClass(Observation.Context.class); @@ -106,6 +108,7 @@ void verifyWhenLooksUpAuthenticationThenObserves() { ((Mono) invocation.getArgument(0)).block(); return Mono.just(this.grant); }); + given(this.authorizationManager.authorize(any(), any())).willCallRealMethod(); this.tested.verify(this.token, this.object).block(); ArgumentCaptor captor = ArgumentCaptor.forClass(Observation.Context.class); verify(this.handler).onStart(captor.capture()); 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 fc7862f51e..9004633f9e 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 @@ -1,5 +1,5 @@ /* - * Copyright 2019 the original author or authors. + * Copyright 2002-2025 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. @@ -23,6 +23,7 @@ import reactor.core.publisher.Mono; import org.springframework.security.authorization.AuthorizationDecision; +import org.springframework.security.authorization.AuthorizationResult; import org.springframework.security.authorization.ReactiveAuthorizationManager; import org.springframework.security.core.Authentication; import org.springframework.security.rsocket.api.PayloadExchange; @@ -51,18 +52,29 @@ private PayloadExchangeMatcherReactiveAuthorizationManager( } /** - * @deprecated please use {@link #authorize(Mono, Object)} instead + * @deprecated please use {@link #authorize(Mono, PayloadExchange)} instead */ @Deprecated @Override public Mono check(Mono authentication, PayloadExchange exchange) { + return authorize(authentication, exchange).flatMap((result) -> { + if (result instanceof AuthorizationDecision decision) { + return Mono.just(decision); + } + return Mono.error(new IllegalArgumentException( + "Please call #authorize or ensure that the returned result is of type Mono")); + }); + } + + @Override + public Mono authorize(Mono authentication, PayloadExchange exchange) { return Flux.fromIterable(this.mappings) .concatMap((mapping) -> mapping.getMatcher() .matches(exchange) .filter(PayloadExchangeMatcher.MatchResult::isMatch) .map(MatchResult::getVariables) .flatMap((variables) -> mapping.getEntry() - .check(authentication, new PayloadExchangeAuthorizationContext(exchange, variables)))) + .authorize(authentication, new PayloadExchangeAuthorizationContext(exchange, variables)))) .next() .switchIfEmpty(Mono.fromCallable(() -> new AuthorizationDecision(false))); } diff --git a/rsocket/src/test/java/org/springframework/security/rsocket/authorization/PayloadExchangeMatcherReactiveAuthorizationManagerTests.java b/rsocket/src/test/java/org/springframework/security/rsocket/authorization/PayloadExchangeMatcherReactiveAuthorizationManagerTests.java index 4f8041aeb4..c0b14cb835 100644 --- a/rsocket/src/test/java/org/springframework/security/rsocket/authorization/PayloadExchangeMatcherReactiveAuthorizationManagerTests.java +++ b/rsocket/src/test/java/org/springframework/security/rsocket/authorization/PayloadExchangeMatcherReactiveAuthorizationManagerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2019 the original author or authors. + * Copyright 2002-2025 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. @@ -53,6 +53,7 @@ public class PayloadExchangeMatcherReactiveAuthorizationManagerTests { public void checkWhenGrantedThenGranted() { AuthorizationDecision expected = new AuthorizationDecision(true); given(this.authz.check(any(), any())).willReturn(Mono.just(expected)); + given(this.authz.authorize(any(), any())).willCallRealMethod(); PayloadExchangeMatcherReactiveAuthorizationManager manager = PayloadExchangeMatcherReactiveAuthorizationManager .builder() .add(new PayloadExchangeMatcherEntry<>(PayloadExchangeMatchers.anyExchange(), this.authz)) @@ -64,6 +65,7 @@ public void checkWhenGrantedThenGranted() { public void checkWhenDeniedThenDenied() { AuthorizationDecision expected = new AuthorizationDecision(false); given(this.authz.check(any(), any())).willReturn(Mono.just(expected)); + given(this.authz.authorize(any(), any())).willCallRealMethod(); PayloadExchangeMatcherReactiveAuthorizationManager manager = PayloadExchangeMatcherReactiveAuthorizationManager .builder() .add(new PayloadExchangeMatcherEntry<>(PayloadExchangeMatchers.anyExchange(), this.authz)) @@ -75,6 +77,7 @@ public void checkWhenDeniedThenDenied() { public void checkWhenFirstMatchThenSecondUsed() { AuthorizationDecision expected = new AuthorizationDecision(true); given(this.authz.check(any(), any())).willReturn(Mono.just(expected)); + given(this.authz.authorize(any(), any())).willCallRealMethod(); PayloadExchangeMatcherReactiveAuthorizationManager manager = PayloadExchangeMatcherReactiveAuthorizationManager .builder() .add(new PayloadExchangeMatcherEntry<>(PayloadExchangeMatchers.anyExchange(), this.authz)) @@ -87,6 +90,7 @@ public void checkWhenFirstMatchThenSecondUsed() { public void checkWhenSecondMatchThenSecondUsed() { AuthorizationDecision expected = new AuthorizationDecision(true); given(this.authz2.check(any(), any())).willReturn(Mono.just(expected)); + given(this.authz2.authorize(any(), any())).willCallRealMethod(); PayloadExchangeMatcherReactiveAuthorizationManager manager = PayloadExchangeMatcherReactiveAuthorizationManager .builder() .add(new PayloadExchangeMatcherEntry<>((e) -> PayloadExchangeMatcher.MatchResult.notMatch(), this.authz)) 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 ebdb2b8ccb..e245ce0466 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2025 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. @@ -26,6 +26,7 @@ import org.springframework.core.log.LogMessage; import org.springframework.security.authorization.AuthorizationDecision; +import org.springframework.security.authorization.AuthorizationResult; import org.springframework.security.authorization.ReactiveAuthorizationManager; import org.springframework.security.core.Authentication; import org.springframework.security.web.server.util.matcher.ServerWebExchangeMatcher.MatchResult; @@ -35,6 +36,7 @@ /** * @author Rob Winch * @author Mathieu Ouellet + * @author Evgeniy Cheban * @since 5.0 */ public final class DelegatingReactiveAuthorizationManager implements ReactiveAuthorizationManager { @@ -49,11 +51,22 @@ private DelegatingReactiveAuthorizationManager( } /** - * @deprecated please use {@link #authorize(Mono, Object)} instead + * @deprecated please use {@link #authorize(Mono, ServerWebExchange)} instead */ @Deprecated @Override public Mono check(Mono authentication, ServerWebExchange exchange) { + return authorize(authentication, exchange).flatMap((result) -> { + if (result instanceof AuthorizationDecision decision) { + return Mono.just(decision); + } + return Mono.error(new IllegalArgumentException( + "Please call #authorize or ensure that the returned result is of type Mono")); + }); + } + + @Override + public Mono authorize(Mono authentication, ServerWebExchange exchange) { return Flux.fromIterable(this.mappings) .concatMap((mapping) -> mapping.getMatcher() .matches(exchange) @@ -63,10 +76,10 @@ public Mono check(Mono authentication, Se logger.debug(LogMessage.of(() -> "Checking authorization on '" + exchange.getRequest().getPath().pathWithinApplication() + "' using " + mapping.getEntry())); - return mapping.getEntry().check(authentication, new AuthorizationContext(exchange, variables)); + return mapping.getEntry().authorize(authentication, new AuthorizationContext(exchange, variables)); })) .next() - .defaultIfEmpty(new AuthorizationDecision(false)); + .switchIfEmpty(Mono.fromCallable(() -> new AuthorizationDecision(false))); } public static DelegatingReactiveAuthorizationManager.Builder builder() { diff --git a/web/src/test/java/org/springframework/security/web/server/authorization/DelegatingReactiveAuthorizationManagerTests.java b/web/src/test/java/org/springframework/security/web/server/authorization/DelegatingReactiveAuthorizationManagerTests.java index 8d916142d2..adab9bb717 100644 --- a/web/src/test/java/org/springframework/security/web/server/authorization/DelegatingReactiveAuthorizationManagerTests.java +++ b/web/src/test/java/org/springframework/security/web/server/authorization/DelegatingReactiveAuthorizationManagerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2025 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. @@ -81,6 +81,7 @@ public void checkWhenFirstMatchesThenNoMoreMatchersAndNoMoreDelegatesInvoked() { given(this.match1.matches(any())).willReturn(ServerWebExchangeMatcher.MatchResult.match()); given(this.delegate1.check(eq(this.authentication), any(AuthorizationContext.class))) .willReturn(Mono.just(this.decision)); + given(this.delegate1.authorize(eq(this.authentication), any(AuthorizationContext.class))).willCallRealMethod(); assertThat(this.manager.check(this.authentication, this.exchange).block()).isEqualTo(this.decision); verifyNoMoreInteractions(this.match2, this.delegate2); } @@ -91,6 +92,7 @@ public void checkWhenSecondMatchesThenNoMoreMatchersAndNoMoreDelegatesInvoked() given(this.match2.matches(any())).willReturn(ServerWebExchangeMatcher.MatchResult.match()); given(this.delegate2.check(eq(this.authentication), any(AuthorizationContext.class))) .willReturn(Mono.just(this.decision)); + given(this.delegate2.authorize(eq(this.authentication), any(AuthorizationContext.class))).willCallRealMethod(); assertThat(this.manager.check(this.authentication, this.exchange).block()).isEqualTo(this.decision); verifyNoMoreInteractions(this.delegate1); }