-
Notifications
You must be signed in to change notification settings - Fork 6.1k
createEvaluationContext should defer lookup of Authentication #11187
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
2209536
to
e0a8c98
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, @evgeniycheban! I've left some feedback inline. Also, will you please rebase this against 5.8.x
as I imagine that's were this change will land?
...c/main/java/org/springframework/security/authentication/AuthenticationTrustResolverImpl.java
Outdated
Show resolved
Hide resolved
* Cannot be null. | ||
*/ | ||
public SecurityExpressionRoot(Supplier<Authentication> authentication) { | ||
this.authentication = new SupplierAuthentication(authentication); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The protected
reference to Authentication
obviously causes a bit of a problem. Given that there is getAuthentication()
I wonder if it could be changed to private
(possibly needs to wait until 6.0).
@rwinch, what do you think? I don't think it's still necessary for this property to be protected -- it only remains that way for backward compatibility. Can we introduce a breaking change in 5.8 that makes this field private
so that it can be changed to a Supplier<Authentication>
? Applications subclassing SecurityExpressionRoot
would need to change this.authentication
to getAuthentication()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not certain of this, but I think that changing the member variable to private will break SpEL expressions that use #authentication
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, okay. I wondered if that was the case since I can access principal
in a SpEL expression without there being a principal
member variable. I'll do some experimenting and see.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I ran this test against SecurityExpressionRoot
with protected Authentication authentication
, private Authentication authentication
, and private Supplier<Authentication> authentication
:
@Test
public void authenticationIsCorrectlyResolved() {
Authentication authentication = new TestingAuthenticationToken("user", "password");
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = new StandardEvaluationContext(new SecurityExpressionRoot(authentication) {});
Expression root = parser.parseExpression("authentication.name == 'user'");
assertThat(root.getValue(context)).isEqualTo(true);
Expression variable = parser.parseExpression("#authentication.name == 'user'");
assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> variable.getValue(context));
}
And it passed for all three setups because the Expression
uses the method resolver to resolve authentication
. In no case could authentication
be accessed as #authentication
. Hypothetically, if we do need this, I think that we can do something like context.setVariable("authentication", authentication)
during setup.
To remove noise, I ran a test against two custom objects unrelated to Security, other than to emulate the change from a protected
field of Type A to a private
field of Type B. In both classes, the public getter method remains the same:
@Test
public void valueFieldIsNotDirectlyReferenced() {
ExpressionParser parser = new SpelExpressionParser();
Expression method = parser.parseExpression("value.length() == 2");
EvaluationContext privateField = new StandardEvaluationContext(new WithPrivateField());
EvaluationContext protectedField = new StandardEvaluationContext(new WithProtectedField());
assertThat(method.getValue(privateField)).isEqualTo(true);
assertThat(method.getValue(protectedField)).isEqualTo(true);
Expression field = parser.parseExpression("#value.length() == 2");
assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> field.getValue(privateField));
assertThatExceptionOfType(SpelEvaluationException.class).isThrownBy(() -> field.getValue(protectedField));
}
private static class WithPrivateField {
private Integer value = 13;
public String getValue() {
return String.valueOf(this.value);
}
}
private static class WithProtectedField {
protected String value = "13";
public String getValue() {
return this.value;
}
}
The test again passes.
Also note that if I remove the method getValue()
from WithProtectedField
, the test fails with "Property or field 'value' cannot be found on object of type 'org.springframework.security.access.expression.AbstractSecurityExpressionHandlerTests$WithProtectedField' - maybe not public or not valid?". Only if I also make the field public
does it pass.
Since the property in SecurityExpressionRoot
is not public
, I don't think it has any bearing on how expressions are evaluated.
Let me know if you see other rocks we should try and uncover.
...n/java/org/springframework/security/access/expression/AbstractSecurityExpressionHandler.java
Outdated
Show resolved
Hide resolved
core/src/main/java/org/springframework/security/core/SupplierAuthentication.java
Outdated
Show resolved
Hide resolved
...a/org/springframework/security/access/expression/method/MethodSecurityEvaluationContext.java
Outdated
Show resolved
Hide resolved
@jzheaux I've updated the PR and rebased this branch on top of |
13f21e1
to
5fbe8b7
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good, @evgeniycheban. I added one cleanup mention.
Additionally, are you able to add an implementation for DefaultMessageSecurityExpressionHandler
?
...pringframework/security/access/expression/method/DefaultMethodSecurityExpressionHandler.java
Show resolved
Hide resolved
- Added createEvaluationContext method that accepts Supplier<Authentication> - Refactored classes that use EvaluationContext to use lazy initialization of Authentication Closes spring-projectsgh-9667
Thanks, @evgeniycheban! This is now merged into |
createEvaluationContext
should defer lookup ofAuthentication
createEvaluationContext
method that acceptsSupplier<Authentication>
EvaluationContext
to use lazy initialization ofAuthentication
Closes gh-9667