createEvaluationContext should defer lookup of Authentication
- Added createEvaluationContext method that accepts Supplier<Authentication> - Refactored classes that use EvaluationContext to use lazy initialization of Authentication Closes gh-9667
This commit is contained in:
parent
2be141f800
commit
5540bbcf0b
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2022 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.
|
||||
|
@ -35,6 +35,7 @@ import org.springframework.util.Assert;
|
|||
* objects.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @author Evgeniy Cheban
|
||||
* @since 3.1
|
||||
*/
|
||||
public abstract class AbstractSecurityExpressionHandler<T>
|
||||
|
@ -116,6 +117,10 @@ public abstract class AbstractSecurityExpressionHandler<T>
|
|||
this.permissionEvaluator = permissionEvaluator;
|
||||
}
|
||||
|
||||
protected BeanResolver getBeanResolver() {
|
||||
return this.beanResolver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setApplicationContext(ApplicationContext applicationContext) {
|
||||
this.beanResolver = new BeanFactoryResolver(applicationContext);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2022 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.
|
||||
|
@ -16,6 +16,8 @@
|
|||
|
||||
package org.springframework.security.access.expression;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.aop.framework.AopInfrastructureBean;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
|
@ -26,6 +28,7 @@ import org.springframework.security.core.Authentication;
|
|||
* expressions from the implementation of the underlying expression objects
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @author Evgeniy Cheban
|
||||
* @since 3.1
|
||||
*/
|
||||
public interface SecurityExpressionHandler<T> extends AopInfrastructureBean {
|
||||
|
@ -41,4 +44,19 @@ public interface SecurityExpressionHandler<T> extends AopInfrastructureBean {
|
|||
*/
|
||||
EvaluationContext createEvaluationContext(Authentication authentication, T invocation);
|
||||
|
||||
/**
|
||||
* Provides an evaluation context in which to evaluate security expressions for the
|
||||
* invocation type. You can override this method in order to provide a custom
|
||||
* implementation that uses lazy initialization of the {@link Authentication} object.
|
||||
* By default, this method uses eager initialization of the {@link Authentication}
|
||||
* object.
|
||||
* @param authentication the {@link Supplier} of the {@link Authentication} to use
|
||||
* @param invocation the {@link T} to use
|
||||
* @return the {@link EvaluationContext} to use
|
||||
* @since 5.8
|
||||
*/
|
||||
default EvaluationContext createEvaluationContext(Supplier<Authentication> authentication, T invocation) {
|
||||
return createEvaluationContext(authentication.get(), invocation);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2022 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.
|
||||
|
@ -19,6 +19,7 @@ package org.springframework.security.access.expression;
|
|||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.security.access.PermissionEvaluator;
|
||||
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
|
||||
|
@ -26,16 +27,18 @@ import org.springframework.security.authentication.AuthenticationTrustResolver;
|
|||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.authority.AuthorityUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Base root object for use in Spring Security expression evaluations.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @author Evgeniy Cheban
|
||||
* @since 3.0
|
||||
*/
|
||||
public abstract class SecurityExpressionRoot implements SecurityExpressionOperations {
|
||||
|
||||
protected final Authentication authentication;
|
||||
private final Supplier<Authentication> authentication;
|
||||
|
||||
private AuthenticationTrustResolver trustResolver;
|
||||
|
||||
|
@ -72,10 +75,18 @@ public abstract class SecurityExpressionRoot implements SecurityExpressionOperat
|
|||
* @param authentication the {@link Authentication} to use. Cannot be null.
|
||||
*/
|
||||
public SecurityExpressionRoot(Authentication authentication) {
|
||||
if (authentication == null) {
|
||||
throw new IllegalArgumentException("Authentication object cannot be null");
|
||||
}
|
||||
this.authentication = authentication;
|
||||
this(() -> authentication);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance that uses lazy initialization of the {@link Authentication}
|
||||
* object.
|
||||
* @param authentication the {@link Supplier} of the {@link Authentication} to use.
|
||||
* Cannot be null.
|
||||
* @since 5.8
|
||||
*/
|
||||
public SecurityExpressionRoot(Supplier<Authentication> authentication) {
|
||||
this.authentication = new AuthenticationSupplier(authentication);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -111,7 +122,7 @@ public abstract class SecurityExpressionRoot implements SecurityExpressionOperat
|
|||
|
||||
@Override
|
||||
public final Authentication getAuthentication() {
|
||||
return this.authentication;
|
||||
return this.authentication.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -126,7 +137,7 @@ public abstract class SecurityExpressionRoot implements SecurityExpressionOperat
|
|||
|
||||
@Override
|
||||
public final boolean isAnonymous() {
|
||||
return this.trustResolver.isAnonymous(this.authentication);
|
||||
return this.trustResolver.isAnonymous(getAuthentication());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -136,13 +147,13 @@ public abstract class SecurityExpressionRoot implements SecurityExpressionOperat
|
|||
|
||||
@Override
|
||||
public final boolean isRememberMe() {
|
||||
return this.trustResolver.isRememberMe(this.authentication);
|
||||
return this.trustResolver.isRememberMe(getAuthentication());
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean isFullyAuthenticated() {
|
||||
return !this.trustResolver.isAnonymous(this.authentication)
|
||||
&& !this.trustResolver.isRememberMe(this.authentication);
|
||||
Authentication authentication = getAuthentication();
|
||||
return !this.trustResolver.isAnonymous(authentication) && !this.trustResolver.isRememberMe(authentication);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -151,7 +162,7 @@ public abstract class SecurityExpressionRoot implements SecurityExpressionOperat
|
|||
* @return
|
||||
*/
|
||||
public Object getPrincipal() {
|
||||
return this.authentication.getPrincipal();
|
||||
return getAuthentication().getPrincipal();
|
||||
}
|
||||
|
||||
public void setTrustResolver(AuthenticationTrustResolver trustResolver) {
|
||||
|
@ -181,7 +192,7 @@ public abstract class SecurityExpressionRoot implements SecurityExpressionOperat
|
|||
|
||||
private Set<String> getAuthoritySet() {
|
||||
if (this.roles == null) {
|
||||
Collection<? extends GrantedAuthority> userAuthorities = this.authentication.getAuthorities();
|
||||
Collection<? extends GrantedAuthority> userAuthorities = getAuthentication().getAuthorities();
|
||||
if (this.roleHierarchy != null) {
|
||||
userAuthorities = this.roleHierarchy.getReachableGrantedAuthorities(userAuthorities);
|
||||
}
|
||||
|
@ -192,12 +203,12 @@ public abstract class SecurityExpressionRoot implements SecurityExpressionOperat
|
|||
|
||||
@Override
|
||||
public boolean hasPermission(Object target, Object permission) {
|
||||
return this.permissionEvaluator.hasPermission(this.authentication, target, permission);
|
||||
return this.permissionEvaluator.hasPermission(getAuthentication(), target, permission);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(Object targetId, String targetType, Object permission) {
|
||||
return this.permissionEvaluator.hasPermission(this.authentication, (Serializable) targetId, targetType,
|
||||
return this.permissionEvaluator.hasPermission(getAuthentication(), (Serializable) targetId, targetType,
|
||||
permission);
|
||||
}
|
||||
|
||||
|
@ -225,4 +236,27 @@ public abstract class SecurityExpressionRoot implements SecurityExpressionOperat
|
|||
return defaultRolePrefix + role;
|
||||
}
|
||||
|
||||
private static final class AuthenticationSupplier implements Supplier<Authentication> {
|
||||
|
||||
private Authentication value;
|
||||
|
||||
private final Supplier<Authentication> delegate;
|
||||
|
||||
private AuthenticationSupplier(Supplier<Authentication> delegate) {
|
||||
Assert.notNull(delegate, "delegate cannot be null");
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Authentication get() {
|
||||
if (this.value == null) {
|
||||
Authentication authentication = this.delegate.get();
|
||||
Assert.notNull(authentication, "Authentication object cannot be null");
|
||||
this.value = authentication;
|
||||
}
|
||||
return this.value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2022 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.
|
||||
|
@ -23,6 +23,7 @@ import java.util.Collection;
|
|||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
@ -50,6 +51,7 @@ import org.springframework.util.Assert;
|
|||
* support.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @author Evgeniy Cheban
|
||||
* @since 3.0
|
||||
*/
|
||||
public class DefaultMethodSecurityExpressionHandler extends AbstractSecurityExpressionHandler<MethodInvocation>
|
||||
|
@ -77,12 +79,26 @@ public class DefaultMethodSecurityExpressionHandler extends AbstractSecurityExpr
|
|||
return new MethodSecurityEvaluationContext(auth, mi, getParameterNameDiscoverer());
|
||||
}
|
||||
|
||||
@Override
|
||||
public EvaluationContext createEvaluationContext(Supplier<Authentication> authentication, MethodInvocation mi) {
|
||||
MethodSecurityExpressionOperations root = createSecurityExpressionRoot(authentication, mi);
|
||||
MethodSecurityEvaluationContext ctx = new MethodSecurityEvaluationContext(root, mi,
|
||||
getParameterNameDiscoverer());
|
||||
ctx.setBeanResolver(getBeanResolver());
|
||||
return ctx;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the root object for expression evaluation.
|
||||
*/
|
||||
@Override
|
||||
protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication,
|
||||
MethodInvocation invocation) {
|
||||
return createSecurityExpressionRoot(() -> authentication, invocation);
|
||||
}
|
||||
|
||||
private MethodSecurityExpressionOperations createSecurityExpressionRoot(Supplier<Authentication> authentication,
|
||||
MethodInvocation invocation) {
|
||||
MethodSecurityExpressionRoot root = new MethodSecurityExpressionRoot(authentication);
|
||||
root.setThis(invocation.getThis());
|
||||
root.setPermissionEvaluator(getPermissionEvaluator());
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2022 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.
|
||||
|
@ -34,6 +34,7 @@ import org.springframework.security.core.parameters.DefaultSecurityParameterName
|
|||
*
|
||||
* @author Luke Taylor
|
||||
* @author Daniel Bustamante
|
||||
* @author Evgeniy Cheban
|
||||
* @since 3.0
|
||||
*/
|
||||
class MethodSecurityEvaluationContext extends MethodBasedEvaluationContext {
|
||||
|
@ -52,6 +53,11 @@ class MethodSecurityEvaluationContext extends MethodBasedEvaluationContext {
|
|||
super(mi.getThis(), getSpecificMethod(mi), mi.getArguments(), parameterNameDiscoverer);
|
||||
}
|
||||
|
||||
MethodSecurityEvaluationContext(MethodSecurityExpressionOperations root, MethodInvocation mi,
|
||||
ParameterNameDiscoverer parameterNameDiscoverer) {
|
||||
super(root, getSpecificMethod(mi), mi.getArguments(), parameterNameDiscoverer);
|
||||
}
|
||||
|
||||
private static Method getSpecificMethod(MethodInvocation mi) {
|
||||
return AopUtils.getMostSpecificMethod(mi.getMethod(), AopProxyUtils.ultimateTargetClass(mi.getThis()));
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2022 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.
|
||||
|
@ -16,6 +16,8 @@
|
|||
|
||||
package org.springframework.security.access.expression.method;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.security.access.expression.SecurityExpressionRoot;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
|
@ -23,6 +25,7 @@ import org.springframework.security.core.Authentication;
|
|||
* Extended expression root object which contains extra method-specific functionality.
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @author Evgeniy Cheban
|
||||
* @since 3.0
|
||||
*/
|
||||
class MethodSecurityExpressionRoot extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {
|
||||
|
@ -37,6 +40,10 @@ class MethodSecurityExpressionRoot extends SecurityExpressionRoot implements Met
|
|||
super(a);
|
||||
}
|
||||
|
||||
MethodSecurityExpressionRoot(Supplier<Authentication> authentication) {
|
||||
super(authentication);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFilterObject(Object filterObject) {
|
||||
this.filterObject = filterObject;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2022 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.
|
||||
|
@ -72,7 +72,7 @@ public final class PostAuthorizeAuthorizationManager implements AuthorizationMan
|
|||
if (attribute == ExpressionAttribute.NULL_ATTRIBUTE) {
|
||||
return null;
|
||||
}
|
||||
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication.get(),
|
||||
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication,
|
||||
mi.getMethodInvocation());
|
||||
this.expressionHandler.setReturnObject(mi.getResult(), ctx);
|
||||
boolean granted = ExpressionUtils.evaluateAsBoolean(attribute.getExpression(), ctx);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2022 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.
|
||||
|
@ -128,7 +128,7 @@ public final class PostFilterAuthorizationMethodInterceptor
|
|||
if (attribute == ExpressionAttribute.NULL_ATTRIBUTE) {
|
||||
return returnedObject;
|
||||
}
|
||||
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(AUTHENTICATION_SUPPLIER.get(), mi);
|
||||
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(AUTHENTICATION_SUPPLIER, mi);
|
||||
return this.expressionHandler.filter(returnedObject, attribute.getExpression(), ctx);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2022 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.
|
||||
|
@ -72,7 +72,7 @@ public final class PreAuthorizeAuthorizationManager implements AuthorizationMana
|
|||
if (attribute == ExpressionAttribute.NULL_ATTRIBUTE) {
|
||||
return null;
|
||||
}
|
||||
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication.get(), mi);
|
||||
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication, mi);
|
||||
boolean granted = ExpressionUtils.evaluateAsBoolean(attribute.getExpression(), ctx);
|
||||
return new ExpressionAttributeAuthorizationDecision(granted, attribute);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2022 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.
|
||||
|
@ -126,7 +126,7 @@ public final class PreFilterAuthorizationMethodInterceptor
|
|||
if (attribute == PreFilterExpressionAttribute.NULL_ATTRIBUTE) {
|
||||
return mi.proceed();
|
||||
}
|
||||
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(AUTHENTICATION_SUPPLIER.get(), mi);
|
||||
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(AUTHENTICATION_SUPPLIER, mi);
|
||||
Object filterTarget = findFilterTarget(attribute.filterTarget, ctx, mi);
|
||||
this.expressionHandler.filter(filterTarget, attribute.getExpression(), ctx);
|
||||
return mi.proceed();
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2022 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.
|
||||
|
@ -19,10 +19,12 @@ package org.springframework.security.access.expression.method;
|
|||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.assertj.core.api.InstanceOfAssertFactories;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
@ -32,6 +34,8 @@ import org.mockito.junit.jupiter.MockitoExtension;
|
|||
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.security.access.expression.SecurityExpressionRoot;
|
||||
import org.springframework.security.authentication.AuthenticationTrustResolver;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
|
@ -43,6 +47,7 @@ import static org.mockito.BDDMockito.given;
|
|||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class DefaultMethodSecurityExpressionHandlerTests {
|
||||
|
@ -167,6 +172,20 @@ public class DefaultMethodSecurityExpressionHandlerTests {
|
|||
verify(upstream).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createEvaluationContextSupplierAuthentication() {
|
||||
setupMocks();
|
||||
Supplier<Authentication> mockAuthenticationSupplier = mock(Supplier.class);
|
||||
given(mockAuthenticationSupplier.get()).willReturn(this.authentication);
|
||||
EvaluationContext context = this.handler.createEvaluationContext(mockAuthenticationSupplier,
|
||||
this.methodInvocation);
|
||||
verifyNoInteractions(mockAuthenticationSupplier);
|
||||
assertThat(context.getRootObject()).extracting(TypedValue::getValue)
|
||||
.asInstanceOf(InstanceOfAssertFactories.type(MethodSecurityExpressionRoot.class))
|
||||
.extracting(SecurityExpressionRoot::getAuthentication).isEqualTo(this.authentication);
|
||||
verify(mockAuthenticationSupplier).get();
|
||||
}
|
||||
|
||||
static class Foo {
|
||||
|
||||
void bar() {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2022 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.
|
||||
|
@ -16,6 +16,10 @@
|
|||
|
||||
package org.springframework.security.messaging.access.expression;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.security.access.expression.AbstractSecurityExpressionHandler;
|
||||
import org.springframework.security.access.expression.SecurityExpressionHandler;
|
||||
|
@ -31,15 +35,29 @@ import org.springframework.util.Assert;
|
|||
*
|
||||
* @param <T> the type for the body of the Message
|
||||
* @author Rob Winch
|
||||
* @author Evgeniy Cheban
|
||||
* @since 4.0
|
||||
*/
|
||||
public class DefaultMessageSecurityExpressionHandler<T> extends AbstractSecurityExpressionHandler<Message<T>> {
|
||||
|
||||
private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
|
||||
|
||||
@Override
|
||||
public EvaluationContext createEvaluationContext(Supplier<Authentication> authentication, Message<T> message) {
|
||||
MessageSecurityExpressionRoot root = createSecurityExpressionRoot(authentication, message);
|
||||
StandardEvaluationContext ctx = new StandardEvaluationContext(root);
|
||||
ctx.setBeanResolver(getBeanResolver());
|
||||
return ctx;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication,
|
||||
Message<T> invocation) {
|
||||
return createSecurityExpressionRoot(() -> authentication, invocation);
|
||||
}
|
||||
|
||||
private MessageSecurityExpressionRoot createSecurityExpressionRoot(Supplier<Authentication> authentication,
|
||||
Message<T> invocation) {
|
||||
MessageSecurityExpressionRoot root = new MessageSecurityExpressionRoot(authentication, invocation);
|
||||
root.setPermissionEvaluator(getPermissionEvaluator());
|
||||
root.setTrustResolver(this.trustResolver);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2022 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.
|
||||
|
@ -16,6 +16,8 @@
|
|||
|
||||
package org.springframework.security.messaging.access.expression;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.security.access.expression.SecurityExpressionRoot;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
@ -24,6 +26,7 @@ import org.springframework.security.core.Authentication;
|
|||
* The {@link SecurityExpressionRoot} used for {@link Message} expressions.
|
||||
*
|
||||
* @author Rob Winch
|
||||
* @author Evgeniy Cheban
|
||||
* @since 4.0
|
||||
*/
|
||||
public class MessageSecurityExpressionRoot extends SecurityExpressionRoot {
|
||||
|
@ -31,6 +34,17 @@ public class MessageSecurityExpressionRoot extends SecurityExpressionRoot {
|
|||
public final Message<?> message;
|
||||
|
||||
public MessageSecurityExpressionRoot(Authentication authentication, Message<?> message) {
|
||||
this(() -> authentication, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance for the given {@link Supplier} of the {@link Authentication}
|
||||
* and {@link Message}.
|
||||
* @param authentication the {@link Supplier} of the {@link Authentication} to use
|
||||
* @param message the {@link Message} to use
|
||||
* @since 5.8
|
||||
*/
|
||||
public MessageSecurityExpressionRoot(Supplier<Authentication> authentication, Message<?> message) {
|
||||
super(authentication);
|
||||
this.message = message;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2022 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.
|
||||
|
@ -16,6 +16,9 @@
|
|||
|
||||
package org.springframework.security.messaging.access.expression;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.assertj.core.api.InstanceOfAssertFactories;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
@ -24,10 +27,12 @@ import org.mockito.junit.jupiter.MockitoExtension;
|
|||
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
import org.springframework.security.access.PermissionEvaluator;
|
||||
import org.springframework.security.access.expression.ExpressionUtils;
|
||||
import org.springframework.security.access.expression.SecurityExpressionRoot;
|
||||
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
|
||||
import org.springframework.security.authentication.AnonymousAuthenticationToken;
|
||||
import org.springframework.security.authentication.AuthenticationTrustResolver;
|
||||
|
@ -38,6 +43,9 @@ import org.springframework.security.core.authority.AuthorityUtils;
|
|||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class DefaultMessageSecurityExpressionHandlerTests {
|
||||
|
@ -104,4 +112,16 @@ public class DefaultMessageSecurityExpressionHandlerTests {
|
|||
assertThat(ExpressionUtils.evaluateAsBoolean(expression, context)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createEvaluationContextSupplierAuthentication() {
|
||||
Supplier<Authentication> mockAuthenticationSupplier = mock(Supplier.class);
|
||||
given(mockAuthenticationSupplier.get()).willReturn(this.authentication);
|
||||
EvaluationContext context = this.handler.createEvaluationContext(mockAuthenticationSupplier, this.message);
|
||||
verifyNoInteractions(mockAuthenticationSupplier);
|
||||
assertThat(context.getRootObject()).extracting(TypedValue::getValue)
|
||||
.asInstanceOf(InstanceOfAssertFactories.type(MessageSecurityExpressionRoot.class))
|
||||
.extracting(SecurityExpressionRoot::getAuthentication).isEqualTo(this.authentication);
|
||||
verify(mockAuthenticationSupplier).get();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,6 +16,10 @@
|
|||
|
||||
package org.springframework.security.web.access.expression;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.security.access.expression.AbstractSecurityExpressionHandler;
|
||||
import org.springframework.security.access.expression.SecurityExpressionHandler;
|
||||
import org.springframework.security.access.expression.SecurityExpressionOperations;
|
||||
|
@ -39,9 +43,24 @@ public class DefaultHttpSecurityExpressionHandler extends AbstractSecurityExpres
|
|||
|
||||
private String defaultRolePrefix = "ROLE_";
|
||||
|
||||
@Override
|
||||
public EvaluationContext createEvaluationContext(Supplier<Authentication> authentication,
|
||||
RequestAuthorizationContext context) {
|
||||
WebSecurityExpressionRoot root = createSecurityExpressionRoot(authentication, context);
|
||||
StandardEvaluationContext ctx = new StandardEvaluationContext(root);
|
||||
ctx.setBeanResolver(getBeanResolver());
|
||||
context.getVariables().forEach(ctx::setVariable);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication,
|
||||
RequestAuthorizationContext context) {
|
||||
return createSecurityExpressionRoot(() -> authentication, context);
|
||||
}
|
||||
|
||||
private WebSecurityExpressionRoot createSecurityExpressionRoot(Supplier<Authentication> authentication,
|
||||
RequestAuthorizationContext context) {
|
||||
WebSecurityExpressionRoot root = new WebSecurityExpressionRoot(authentication, context.getRequest());
|
||||
root.setRoleHierarchy(getRoleHierarchy());
|
||||
root.setPermissionEvaluator(getPermissionEvaluator());
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
package org.springframework.security.web.access.expression;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
|
@ -72,10 +71,7 @@ public final class WebExpressionAuthorizationManager implements AuthorizationMan
|
|||
*/
|
||||
@Override
|
||||
public AuthorizationDecision check(Supplier<Authentication> authentication, RequestAuthorizationContext context) {
|
||||
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication.get(), context);
|
||||
for (Map.Entry<String, String> entry : context.getVariables().entrySet()) {
|
||||
ctx.setVariable(entry.getKey(), entry.getValue());
|
||||
}
|
||||
EvaluationContext ctx = this.expressionHandler.createEvaluationContext(authentication, context);
|
||||
boolean granted = ExpressionUtils.evaluateAsBoolean(this.expression, ctx);
|
||||
return new ExpressionAuthorizationDecision(granted, this.expression);
|
||||
}
|
||||
|
|
|
@ -16,8 +16,9 @@
|
|||
|
||||
package org.springframework.security.web.access.expression;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.springframework.security.access.expression.SecurityExpressionRoot;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.web.FilterInvocation;
|
||||
|
@ -36,17 +37,17 @@ public class WebSecurityExpressionRoot extends SecurityExpressionRoot {
|
|||
public final HttpServletRequest request;
|
||||
|
||||
public WebSecurityExpressionRoot(Authentication a, FilterInvocation fi) {
|
||||
this(a, fi.getRequest());
|
||||
this(() -> a, fi.getRequest());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance for the given {@link Authentication} and
|
||||
* {@link HttpServletRequest}.
|
||||
* @param authentication the {@link Authentication} to use
|
||||
* Creates an instance for the given {@link Supplier} of the {@link Authentication}
|
||||
* and {@link HttpServletRequest}.
|
||||
* @param authentication the {@link Supplier} of the {@link Authentication} to use
|
||||
* @param request the {@link HttpServletRequest} to use
|
||||
* @since 5.8
|
||||
*/
|
||||
public WebSecurityExpressionRoot(Authentication authentication, HttpServletRequest request) {
|
||||
public WebSecurityExpressionRoot(Supplier<Authentication> authentication, HttpServletRequest request) {
|
||||
super(authentication);
|
||||
this.request = request;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2016 the original author or authors.
|
||||
* Copyright 2002-2022 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.
|
||||
|
@ -16,6 +16,9 @@
|
|||
|
||||
package org.springframework.security.web.access.expression;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.assertj.core.api.InstanceOfAssertFactories;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
@ -28,7 +31,9 @@ import org.springframework.context.support.StaticApplicationContext;
|
|||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.ExpressionParser;
|
||||
import org.springframework.expression.TypedValue;
|
||||
import org.springframework.security.access.SecurityConfig;
|
||||
import org.springframework.security.access.expression.SecurityExpressionRoot;
|
||||
import org.springframework.security.authentication.AuthenticationTrustResolver;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
|
@ -36,8 +41,10 @@ import org.springframework.security.web.access.intercept.RequestAuthorizationCon
|
|||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class DefaultHttpSecurityExpressionHandlerTests {
|
||||
|
@ -91,4 +98,16 @@ public class DefaultHttpSecurityExpressionHandlerTests {
|
|||
verify(this.trustResolver).isAnonymous(this.authentication);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createEvaluationContextSupplierAuthentication() {
|
||||
Supplier<Authentication> mockAuthenticationSupplier = mock(Supplier.class);
|
||||
given(mockAuthenticationSupplier.get()).willReturn(this.authentication);
|
||||
EvaluationContext context = this.handler.createEvaluationContext(mockAuthenticationSupplier, this.context);
|
||||
verifyNoInteractions(mockAuthenticationSupplier);
|
||||
assertThat(context.getRootObject()).extracting(TypedValue::getValue)
|
||||
.asInstanceOf(InstanceOfAssertFactories.type(WebSecurityExpressionRoot.class))
|
||||
.extracting(SecurityExpressionRoot::getAuthentication).isEqualTo(this.authentication);
|
||||
verify(mockAuthenticationSupplier).get();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue