1
0
mirror of synced 2026-05-22 21:33:16 +00:00

Add AuthorizationManagerFactory

Signed-off-by: Steve Riesenberg <5248162+sjohnr@users.noreply.github.com>
This commit is contained in:
Steve Riesenberg
2025-09-02 12:47:53 -05:00
committed by Rob Winch
parent a4f813ab29
commit eeb4574bb3
37 changed files with 2719 additions and 178 deletions
@@ -26,6 +26,8 @@ import org.springframework.security.access.hierarchicalroles.NullRoleHierarchy;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
import org.springframework.security.authorization.AuthorizationManagerFactory;
import org.springframework.security.authorization.DefaultAuthorizationManagerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
@@ -92,18 +94,18 @@ import org.springframework.util.Assert;
*/
public class SecurityEvaluationContextExtension implements EvaluationContextExtension {
private static final String DEFAULT_ROLE_PREFIX = "ROLE_";
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
.getContextHolderStrategy();
private @Nullable Authentication authentication;
private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
private RoleHierarchy roleHierarchy = new NullRoleHierarchy();
private AuthorizationManagerFactory<Object> authorizationManagerFactory = new DefaultAuthorizationManagerFactory<>();
private PermissionEvaluator permissionEvaluator = new DenyAllPermissionEvaluator();
private String defaultRolePrefix = "ROLE_";
private String defaultRolePrefix = DEFAULT_ROLE_PREFIX;
/**
* Creates a new instance that uses the current {@link Authentication} found on the
@@ -126,14 +128,16 @@ public class SecurityEvaluationContextExtension implements EvaluationContextExte
}
@Override
public SecurityExpressionRoot getRootObject() {
public SecurityExpressionRoot<Object> getRootObject() {
Authentication authentication = getAuthentication();
SecurityExpressionRoot root = new SecurityExpressionRoot(authentication) {
SecurityExpressionRoot<Object> root = new SecurityExpressionRoot<>(() -> authentication, new Object()) {
};
root.setTrustResolver(this.trustResolver);
root.setRoleHierarchy(this.roleHierarchy);
root.setAuthorizationManagerFactory(this.authorizationManagerFactory);
root.setPermissionEvaluator(this.permissionEvaluator);
root.setDefaultRolePrefix(this.defaultRolePrefix);
if (!DEFAULT_ROLE_PREFIX.equals(this.defaultRolePrefix)) {
// Ensure SecurityExpressionRoot can strip the custom role prefix
root.setDefaultRolePrefix(this.defaultRolePrefix);
}
return root;
}
@@ -156,15 +160,46 @@ public class SecurityEvaluationContextExtension implements EvaluationContextExte
return context.getAuthentication();
}
/**
* Sets the {@link AuthorizationManagerFactory} to be used. The default is
* {@link DefaultAuthorizationManagerFactory}.
* @param authorizationManagerFactory the {@link AuthorizationManagerFactory} to use.
* Cannot be null.
* @since 7.0
*/
public void setAuthorizationManagerFactory(AuthorizationManagerFactory<Object> authorizationManagerFactory) {
Assert.notNull(authorizationManagerFactory, "authorizationManagerFactory cannot be null");
this.authorizationManagerFactory = authorizationManagerFactory;
}
/**
* Allows accessing the {@link DefaultAuthorizationManagerFactory} for getting and
* setting defaults. This method will be removed in Spring Security 8.
* @return the {@link DefaultAuthorizationManagerFactory}
* @throws IllegalStateException if a different {@link AuthorizationManagerFactory}
* was already set
*/
private DefaultAuthorizationManagerFactory<Object> getDefaultAuthorizationManagerFactory() {
if (!(this.authorizationManagerFactory instanceof DefaultAuthorizationManagerFactory<Object> defaultAuthorizationManagerFactory)) {
throw new IllegalStateException(
"authorizationManagerFactory must be an instance of DefaultAuthorizationManagerFactory");
}
return defaultAuthorizationManagerFactory;
}
/**
* Sets the {@link AuthenticationTrustResolver} to be used. Default is
* {@link AuthenticationTrustResolverImpl}. Cannot be null.
* @param trustResolver the {@link AuthenticationTrustResolver} to use
* @since 5.8
* @deprecated Use
* {@link #setAuthorizationManagerFactory(AuthorizationManagerFactory)} instead
*/
@Deprecated(since = "7.0")
public void setTrustResolver(AuthenticationTrustResolver trustResolver) {
Assert.notNull(trustResolver, "trustResolver cannot be null");
this.trustResolver = trustResolver;
getDefaultAuthorizationManagerFactory().setTrustResolver(trustResolver);
}
/**
@@ -172,10 +207,13 @@ public class SecurityEvaluationContextExtension implements EvaluationContextExte
* Cannot be null.
* @param roleHierarchy the {@link RoleHierarchy} to use
* @since 5.8
* @deprecated Use
* {@link #setAuthorizationManagerFactory(AuthorizationManagerFactory)} instead
*/
@Deprecated(since = "7.0")
public void setRoleHierarchy(RoleHierarchy roleHierarchy) {
Assert.notNull(roleHierarchy, "roleHierarchy cannot be null");
this.roleHierarchy = roleHierarchy;
getDefaultAuthorizationManagerFactory().setRoleHierarchy(roleHierarchy);
}
/**
@@ -199,8 +237,12 @@ public class SecurityEvaluationContextExtension implements EvaluationContextExte
* @param defaultRolePrefix the default prefix to add to roles. The default is
* "ROLE_".
* @since 5.8
* @deprecated Use
* {@link #setAuthorizationManagerFactory(AuthorizationManagerFactory)} instead
*/
@Deprecated(since = "7.0")
public void setDefaultRolePrefix(String defaultRolePrefix) {
getDefaultAuthorizationManagerFactory().setRolePrefix(defaultRolePrefix);
this.defaultRolePrefix = defaultRolePrefix;
}
@@ -23,10 +23,9 @@ import org.junit.jupiter.api.Test;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.access.expression.DenyAllPermissionEvaluator;
import org.springframework.security.access.expression.SecurityExpressionRoot;
import org.springframework.security.access.hierarchicalroles.NullRoleHierarchy;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
@@ -102,9 +101,11 @@ public class SecurityEvaluationContextExtensionTests {
public void setTrustResolverWhenNotNullThenVerifyRootObject() {
TestingAuthenticationToken explicit = new TestingAuthenticationToken("explicit", "password", "ROLE_EXPLICIT");
this.securityExtension = new SecurityEvaluationContextExtension(explicit);
AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
AuthenticationTrustResolver trustResolver = mock(AuthenticationTrustResolver.class);
given(trustResolver.isAuthenticated(explicit)).willReturn(true);
this.securityExtension.setTrustResolver(trustResolver);
assertThat(getRoot()).extracting("trustResolver").isEqualTo(trustResolver);
assertThat(getRoot().isAuthenticated()).isTrue();
verify(trustResolver).isAuthenticated(explicit);
}
@Test
@@ -117,11 +118,11 @@ public class SecurityEvaluationContextExtensionTests {
@Test
public void setRoleHierarchyWhenNotNullThenVerifyRootObject() {
TestingAuthenticationToken explicit = new TestingAuthenticationToken("explicit", "password", "ROLE_EXPLICIT");
TestingAuthenticationToken explicit = new TestingAuthenticationToken("explicit", "password", "ROLE_PARENT");
this.securityExtension = new SecurityEvaluationContextExtension(explicit);
RoleHierarchy roleHierarchy = new NullRoleHierarchy();
RoleHierarchy roleHierarchy = RoleHierarchyImpl.fromHierarchy("ROLE_PARENT > ROLE_EXPLICIT");
this.securityExtension.setRoleHierarchy(roleHierarchy);
assertThat(getRoot()).extracting("roleHierarchy").isEqualTo(roleHierarchy);
assertThat(getRoot().hasRole("EXPLICIT")).isTrue();
}
@Test
@@ -143,25 +144,25 @@ public class SecurityEvaluationContextExtensionTests {
@Test
public void setDefaultRolePrefixWhenCustomThenVerifyRootObject() {
TestingAuthenticationToken explicit = new TestingAuthenticationToken("explicit", "password", "ROLE_EXPLICIT");
TestingAuthenticationToken explicit = new TestingAuthenticationToken("explicit", "password", "CUSTOM_EXPLICIT");
this.securityExtension = new SecurityEvaluationContextExtension(explicit);
String defaultRolePrefix = "CUSTOM_";
this.securityExtension.setDefaultRolePrefix(defaultRolePrefix);
assertThat(getRoot()).extracting("defaultRolePrefix").isEqualTo(defaultRolePrefix);
assertThat(getRoot().hasRole("EXPLICIT")).isTrue();
}
@Test
public void getRootObjectWhenAdditionalFieldsNotSetThenVerifyDefaults() {
TestingAuthenticationToken explicit = new TestingAuthenticationToken("explicit", "password", "ROLE_EXPLICIT");
this.securityExtension = new SecurityEvaluationContextExtension(explicit);
SecurityExpressionRoot root = getRoot();
assertThat(root).extracting("trustResolver").isInstanceOf(AuthenticationTrustResolverImpl.class);
assertThat(root).extracting("roleHierarchy").isInstanceOf(NullRoleHierarchy.class);
assertThat(root).extracting("permissionEvaluator").isInstanceOf(DenyAllPermissionEvaluator.class);
assertThat(root).extracting("defaultRolePrefix").isEqualTo("ROLE_");
SecurityExpressionRoot<?> securityExpressionRoot = getRoot();
assertThat(securityExpressionRoot.isAuthenticated()).isTrue();
assertThat(securityExpressionRoot.hasRole("PARENT")).isFalse();
assertThat(securityExpressionRoot.hasRole("EXPLICIT")).isTrue();
assertThat(securityExpressionRoot.hasPermission(new Object(), "read")).isFalse();
}
private SecurityExpressionRoot getRoot() {
private SecurityExpressionRoot<?> getRoot() {
return this.securityExtension.getRootObject();
}