Support Automatically Checking for Required Authorities in Authorization Rules

Closes: gh-17900

Signed-off-by: Andrey Litvitski <andrey1010102008@gmail.com>
This commit is contained in:
Andrey Litvitski 2025-09-22 00:15:13 +03:00
parent a63e87d8fb
commit 68742e170c

View File

@ -29,6 +29,7 @@ import org.springframework.util.Assert;
* *
* @param <T> the type of object that the authorization check is being done on * @param <T> the type of object that the authorization check is being done on
* @author Steve Riesenberg * @author Steve Riesenberg
* @author Andrey Litvitski
* @since 7.0 * @since 7.0
*/ */
public final class DefaultAuthorizationManagerFactory<T extends @Nullable Object> 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 rolePrefix = "ROLE_";
private String[] requiredAuthorities = new String[0];
/** /**
* Sets the {@link AuthenticationTrustResolver} used to check the user's * Sets the {@link AuthenticationTrustResolver} used to check the user's
* authentication. * authentication.
@ -69,6 +72,35 @@ public final class DefaultAuthorizationManagerFactory<T extends @Nullable Object
this.rolePrefix = rolePrefix; 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 @Override
public AuthorizationManager<T> hasRole(String role) { public AuthorizationManager<T> hasRole(String role) {
return hasAnyRole(role); return hasAnyRole(role);
@ -76,42 +108,45 @@ public final class DefaultAuthorizationManagerFactory<T extends @Nullable Object
@Override @Override
public AuthorizationManager<T> hasAnyRole(String... roles) { public AuthorizationManager<T> hasAnyRole(String... roles) {
return withRoleHierarchy(AuthorityAuthorizationManager.hasAnyRole(this.rolePrefix, roles)); return withRequiredAuthorities(
withRoleHierarchy(AuthorityAuthorizationManager.hasAnyRole(this.rolePrefix, roles)));
} }
@Override @Override
public AuthorizationManager<T> hasAllRoles(String... roles) { public AuthorizationManager<T> hasAllRoles(String... roles) {
return withRoleHierarchy(AllAuthoritiesAuthorizationManager.hasAllPrefixedAuthorities(this.rolePrefix, roles)); return withRequiredAuthorities(withRoleHierarchy(
AllAuthoritiesAuthorizationManager.hasAllPrefixedAuthorities(this.rolePrefix, roles)));
} }
@Override @Override
public AuthorizationManager<T> hasAuthority(String authority) { public AuthorizationManager<T> hasAuthority(String authority) {
return withRoleHierarchy(AuthorityAuthorizationManager.hasAuthority(authority)); return withRequiredAuthorities(withRoleHierarchy(AuthorityAuthorizationManager.hasAuthority(authority)));
} }
@Override @Override
public AuthorizationManager<T> hasAnyAuthority(String... authorities) { public AuthorizationManager<T> hasAnyAuthority(String... authorities) {
return withRoleHierarchy(AuthorityAuthorizationManager.hasAnyAuthority(authorities)); return withRequiredAuthorities(withRoleHierarchy(AuthorityAuthorizationManager.hasAnyAuthority(authorities)));
} }
@Override @Override
public AuthorizationManager<T> hasAllAuthorities(String... authorities) { public AuthorizationManager<T> hasAllAuthorities(String... authorities) {
return withRoleHierarchy(AllAuthoritiesAuthorizationManager.hasAllAuthorities(authorities)); return withRequiredAuthorities(
withRoleHierarchy(AllAuthoritiesAuthorizationManager.hasAllAuthorities(authorities)));
} }
@Override @Override
public AuthorizationManager<T> authenticated() { public AuthorizationManager<T> authenticated() {
return withTrustResolver(AuthenticatedAuthorizationManager.authenticated()); return withRequiredAuthorities(withTrustResolver(AuthenticatedAuthorizationManager.authenticated()));
} }
@Override @Override
public AuthorizationManager<T> fullyAuthenticated() { public AuthorizationManager<T> fullyAuthenticated() {
return withTrustResolver(AuthenticatedAuthorizationManager.fullyAuthenticated()); return withRequiredAuthorities(withTrustResolver(AuthenticatedAuthorizationManager.fullyAuthenticated()));
} }
@Override @Override
public AuthorizationManager<T> rememberMe() { public AuthorizationManager<T> rememberMe() {
return withTrustResolver(AuthenticatedAuthorizationManager.rememberMe()); return withRequiredAuthorities(withTrustResolver(AuthenticatedAuthorizationManager.rememberMe()));
} }
@Override @Override
@ -136,4 +171,13 @@ public final class DefaultAuthorizationManagerFactory<T extends @Nullable Object
return authorizationManager; 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);
}
} }