Jelajahi Sumber

SecuredAuthorizationManager should allow customizing underlying authorization manager

Closes gh-12233
Evgeniy Cheban 2 tahun lalu
induk
melakukan
782b792e7b

+ 17 - 2
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<MethodInvocation> {
 
-	private final AuthoritiesAuthorizationManager delegate = new AuthoritiesAuthorizationManager();
+	private AuthorizationManager<Collection<String>> authoritiesAuthorizationManager = new AuthoritiesAuthorizationManager();
 
 	private final Map<MethodClassKey, Set<String>> 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<Collection<String>> 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<M
 	@Override
 	public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation mi) {
 		Set<String> authorities = getAuthorities(mi);
-		return authorities.isEmpty() ? null : this.delegate.check(authentication, authorities);
+		return authorities.isEmpty() ? null : this.authoritiesAuthorizationManager.check(authentication, authorities);
 	}
 
 	private Set<String> getAuthorities(MethodInvocation methodInvocation) {

+ 27 - 1
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<Collection<String>> authoritiesAuthorizationManager = mock(AuthorizationManager.class);
+		SecuredAuthorizationManager manager = new SecuredAuthorizationManager();
+		manager.setAuthoritiesAuthorizationManager(authoritiesAuthorizationManager);
+		MockMethodInvocation methodInvocation = new MockMethodInvocation(new TestClass(), TestClass.class,
+				"securedUserOrAdmin");
+		Supplier<Authentication> 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,