Browse Source

Support Automatically Checking for Required Authorities in Authorization Rules

Closes: gh-17900

Signed-off-by: Andrey Litvitski <andrey1010102008@gmail.com>
Andrey Litvitski 1 week ago
parent
commit
68742e170c

+ 52 - 8
core/src/main/java/org/springframework/security/authorization/DefaultAuthorizationManagerFactory.java

@@ -29,6 +29,7 @@ import org.springframework.util.Assert;
  *
  * @param <T> the type of object that the authorization check is being done on
  * @author Steve Riesenberg
+ * @author Andrey Litvitski
  * @since 7.0
  */
 public final class DefaultAuthorizationManagerFactory<T extends @Nullable Object>
@@ -40,6 +41,8 @@ public final class DefaultAuthorizationManagerFactory<T extends @Nullable Object
 
 	private String rolePrefix = "ROLE_";
 
+	private String[] requiredAuthorities = new String[0];
+
 	/**
 	 * Sets the {@link AuthenticationTrustResolver} used to check the user's
 	 * authentication.
@@ -69,6 +72,35 @@ public final class DefaultAuthorizationManagerFactory<T extends @Nullable Object
 		this.rolePrefix = rolePrefix;
 	}
 
+	/**
+	 * Sets authorities required for authorization managers that apply to authenticated
+	 * users.
+	 * <p>
+	 * Does not affect {@code anonymous}, {@code permitAll}, or {@code denyAll}.
+	 * <p>
+	 * Evaluated with the configured {@link RoleHierarchy}.
+	 * @param requiredAuthorities the required authorities (must not be {@code null})
+	 */
+	public void setRequiredAuthorities(String[] requiredAuthorities) {
+		Assert.notNull(requiredAuthorities, "requiredAuthorities cannot be null");
+		this.requiredAuthorities = requiredAuthorities;
+	}
+
+	/**
+	 * Creates a factory that requires the given authorities for authorization managers
+	 * that apply to authenticated users.
+	 * <p>
+	 * Does not affect {@code anonymous}, {@code permitAll}, or {@code denyAll}.
+	 * @param authorities the required authorities
+	 * @param <T> the secured object type
+	 * @return a factory configured with the required authorities
+	 */
+	public static <T> AuthorizationManagerFactory<T> withAuthorities(String... authorities) {
+		DefaultAuthorizationManagerFactory<T> factory = new DefaultAuthorizationManagerFactory<>();
+		factory.setRequiredAuthorities(authorities);
+		return factory;
+	}
+
 	@Override
 	public AuthorizationManager<T> hasRole(String role) {
 		return hasAnyRole(role);
@@ -76,42 +108,45 @@ public final class DefaultAuthorizationManagerFactory<T extends @Nullable Object
 
 	@Override
 	public AuthorizationManager<T> hasAnyRole(String... roles) {
-		return withRoleHierarchy(AuthorityAuthorizationManager.hasAnyRole(this.rolePrefix, roles));
+		return withRequiredAuthorities(
+				withRoleHierarchy(AuthorityAuthorizationManager.hasAnyRole(this.rolePrefix, roles)));
 	}
 
 	@Override
 	public AuthorizationManager<T> hasAllRoles(String... roles) {
-		return withRoleHierarchy(AllAuthoritiesAuthorizationManager.hasAllPrefixedAuthorities(this.rolePrefix, roles));
+		return withRequiredAuthorities(withRoleHierarchy(
+				AllAuthoritiesAuthorizationManager.hasAllPrefixedAuthorities(this.rolePrefix, roles)));
 	}
 
 	@Override
 	public AuthorizationManager<T> hasAuthority(String authority) {
-		return withRoleHierarchy(AuthorityAuthorizationManager.hasAuthority(authority));
+		return withRequiredAuthorities(withRoleHierarchy(AuthorityAuthorizationManager.hasAuthority(authority)));
 	}
 
 	@Override
 	public AuthorizationManager<T> hasAnyAuthority(String... authorities) {
-		return withRoleHierarchy(AuthorityAuthorizationManager.hasAnyAuthority(authorities));
+		return withRequiredAuthorities(withRoleHierarchy(AuthorityAuthorizationManager.hasAnyAuthority(authorities)));
 	}
 
 	@Override
 	public AuthorizationManager<T> hasAllAuthorities(String... authorities) {
-		return withRoleHierarchy(AllAuthoritiesAuthorizationManager.hasAllAuthorities(authorities));
+		return withRequiredAuthorities(
+				withRoleHierarchy(AllAuthoritiesAuthorizationManager.hasAllAuthorities(authorities)));
 	}
 
 	@Override
 	public AuthorizationManager<T> authenticated() {
-		return withTrustResolver(AuthenticatedAuthorizationManager.authenticated());
+		return withRequiredAuthorities(withTrustResolver(AuthenticatedAuthorizationManager.authenticated()));
 	}
 
 	@Override
 	public AuthorizationManager<T> fullyAuthenticated() {
-		return withTrustResolver(AuthenticatedAuthorizationManager.fullyAuthenticated());
+		return withRequiredAuthorities(withTrustResolver(AuthenticatedAuthorizationManager.fullyAuthenticated()));
 	}
 
 	@Override
 	public AuthorizationManager<T> rememberMe() {
-		return withTrustResolver(AuthenticatedAuthorizationManager.rememberMe());
+		return withRequiredAuthorities(withTrustResolver(AuthenticatedAuthorizationManager.rememberMe()));
 	}
 
 	@Override
@@ -136,4 +171,13 @@ public final class DefaultAuthorizationManagerFactory<T extends @Nullable Object
 		return authorizationManager;
 	}
 
+	private AuthorizationManager<T> withRequiredAuthorities(AuthorizationManager<T> manager) {
+		if (this.requiredAuthorities == null || this.requiredAuthorities.length == 0) {
+			return manager;
+		}
+		AuthorizationManager<T> required = withRoleHierarchy(
+				AllAuthoritiesAuthorizationManager.hasAllAuthorities(this.requiredAuthorities));
+		return AuthorizationManagers.allOf(new AuthorizationDecision(false), manager, required);
+	}
+
 }