From 782b792e7b558132eba01a9c38a59a09cc2f95d5 Mon Sep 17 00:00:00 2001 From: Evgeniy Cheban Date: Fri, 6 Jan 2023 13:29:58 +0100 Subject: [PATCH] SecuredAuthorizationManager should allow customizing underlying authorization manager Closes gh-12233 --- .../method/SecuredAuthorizationManager.java | 19 +++++++++++-- .../SecuredAuthorizationManagerTests.java | 28 ++++++++++++++++++- 2 files changed, 44 insertions(+), 3 deletions(-) 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 04938e8822..dcfc8a8511 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 @@ -17,6 +17,7 @@ package org.springframework.security.authorization.method; import java.lang.reflect.Method; +import java.util.Collection; import java.util.Collections; import java.util.Map; import java.util.Set; @@ -32,6 +33,7 @@ import org.springframework.security.authorization.AuthoritiesAuthorizationManage import org.springframework.security.authorization.AuthorizationDecision; import org.springframework.security.authorization.AuthorizationManager; import org.springframework.security.core.Authentication; +import org.springframework.util.Assert; /** * An {@link AuthorizationManager} which can determine if an {@link Authentication} may @@ -43,10 +45,23 @@ import org.springframework.security.core.Authentication; */ public final class SecuredAuthorizationManager implements AuthorizationManager { - private final AuthoritiesAuthorizationManager delegate = new AuthoritiesAuthorizationManager(); + private AuthorizationManager> authoritiesAuthorizationManager = new AuthoritiesAuthorizationManager(); private final Map> cachedAuthorities = new ConcurrentHashMap<>(); + /** + * Sets an {@link AuthorizationManager} that accepts a collection of authority + * strings. + * @param authoritiesAuthorizationManager the {@link AuthorizationManager} that + * accepts a collection of authority strings to use + * @since 6.1 + */ + public void setAuthoritiesAuthorizationManager( + AuthorizationManager> authoritiesAuthorizationManager) { + Assert.notNull(authoritiesAuthorizationManager, "authoritiesAuthorizationManager cannot be null"); + this.authoritiesAuthorizationManager = authoritiesAuthorizationManager; + } + /** * Determine if an {@link Authentication} has access to a method by evaluating the * {@link Secured} annotation that {@link MethodInvocation} specifies. @@ -58,7 +73,7 @@ public final class SecuredAuthorizationManager implements AuthorizationManager authentication, MethodInvocation mi) { Set authorities = getAuthorities(mi); - return authorities.isEmpty() ? null : this.delegate.check(authentication, authorities); + return authorities.isEmpty() ? null : this.authoritiesAuthorizationManager.check(authentication, authorities); } private Set getAuthorities(MethodInvocation methodInvocation) { diff --git a/core/src/test/java/org/springframework/security/authorization/method/SecuredAuthorizationManagerTests.java b/core/src/test/java/org/springframework/security/authorization/method/SecuredAuthorizationManagerTests.java index f546d8cb03..18fe59f02c 100644 --- a/core/src/test/java/org/springframework/security/authorization/method/SecuredAuthorizationManagerTests.java +++ b/core/src/test/java/org/springframework/security/authorization/method/SecuredAuthorizationManagerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2021 the original author or authors. + * Copyright 2002-2023 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. @@ -18,6 +18,8 @@ package org.springframework.security.authorization.method; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Collection; +import java.util.Set; import java.util.function.Supplier; import org.junit.jupiter.api.Test; @@ -29,10 +31,14 @@ import org.springframework.security.access.intercept.method.MockMethodInvocation import org.springframework.security.authentication.TestAuthentication; import org.springframework.security.authentication.TestingAuthenticationToken; import org.springframework.security.authorization.AuthorizationDecision; +import org.springframework.security.authorization.AuthorizationManager; import org.springframework.security.core.Authentication; 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.Mockito.mock; +import static org.mockito.Mockito.verify; /** * Tests for {@link SecuredAuthorizationManager}. @@ -41,6 +47,26 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; */ public class SecuredAuthorizationManagerTests { + @Test + public void setAuthoritiesAuthorizationManagerWhenNullThenException() { + SecuredAuthorizationManager manager = new SecuredAuthorizationManager(); + assertThatIllegalArgumentException().isThrownBy(() -> manager.setAuthoritiesAuthorizationManager(null)) + .withMessage("authoritiesAuthorizationManager cannot be null"); + } + + @Test + public void setAuthoritiesAuthorizationManagerWhenNotNullThenVerifyUsage() throws Exception { + AuthorizationManager> authoritiesAuthorizationManager = mock(AuthorizationManager.class); + SecuredAuthorizationManager manager = new SecuredAuthorizationManager(); + manager.setAuthoritiesAuthorizationManager(authoritiesAuthorizationManager); + MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class, + "securedUserOrAdmin"); + Supplier authentication = TestAuthentication::authenticatedUser; + AuthorizationDecision decision = manager.check(authentication, methodInvocation); + assertThat(decision).isNull(); + verify(authoritiesAuthorizationManager).check(authentication, Set.of("ROLE_USER", "ROLE_ADMIN")); + } + @Test public void checkDoSomethingWhenNoSecuredAnnotationThenNullDecision() throws Exception { MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,