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

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:
Evgeniy Cheban
2022-05-07 21:28:54 +03:00
committed by Josh Cummings
parent 7d97839235
commit 362f15534e
18 changed files with 236 additions and 43 deletions
@@ -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();
}
}