Skip to content

Support Mono<Boolean> for Method Security #8584

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

Closed
wants to merge 2 commits into from
Closed

Support Mono<Boolean> for Method Security #8584

wants to merge 2 commits into from

Conversation

sheiy
Copy link

@sheiy sheiy commented May 22, 2020

@pivotal-issuemaster
Copy link

@sheiy Please sign the Contributor License Agreement!

Click here to manually synchronize the status of this Pull Request.

See the FAQ for frequently asked questions.

@pivotal-issuemaster
Copy link

@sheiy Thank you for signing the Contributor License Agreement!

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged label May 22, 2020
Copy link
Member

@rwinch rwinch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR @sheiy I've left some comments inline. Can you also put together some tests? Unit tests are good, but we also would want some similar to those in EnableReactiveMethodSecurityTests

@@ -30,4 +31,14 @@ public static boolean evaluateAsBoolean(Expression expr, EvaluationContext ctx)
+ expr.getExpressionString() + "'", e);
}
}

public static Mono<Boolean> evaluateAsMonoBoolean(Expression expr, EvaluationContext ctx) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adding a Reactive return type to a class used by non-reactive applications can potentially break non-reactive applications since depending on the JDK it may load all method definitions as it loads the class. In general, optional dependencies must be placed in distinct classes.

* annotations.
* @return Mono<true> if authorised, Mono<false> otherwise
*/
Mono<Boolean> before(Authentication authentication, MethodInvocation mi,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally on the reactive side we use Mono<Authentication> so that if the Authentication is not needed, it is not subscribed to (i.e. if a method had permitAll we wouldn't need to load Authentication from session).

@@ -58,7 +58,7 @@
* @param preInvocationAdvice the {@link PreInvocationAuthorizationAdvice} to use
* @param postInvocationAdvice the {@link PostInvocationAuthorizationAdvice} to use
*/
public PrePostAdviceReactiveMethodInterceptor(MethodSecurityMetadataSource attributeSource, PreInvocationAuthorizationAdvice preInvocationAdvice, PostInvocationAuthorizationAdvice postInvocationAdvice) {
public PrePostAdviceReactiveMethodInterceptor(MethodSecurityMetadataSource attributeSource, PreInvocationAuthorizationReactiveAdvice preInvocationAdvice, PostInvocationAuthorizationAdvice postInvocationAdvice) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a breaking change that we cannot make. We would need to add another constructor

@rwinch rwinch changed the title fix issues #4841 Support Mono<Boolean> for Method Security May 27, 2020
@rwinch
Copy link
Member

rwinch commented May 27, 2020

Another note is that we may want to ensure what we do for evaluating SpEL expressions works for #5867

@rwinch rwinch self-assigned this Jun 1, 2020
Copy link
Member

@rwinch rwinch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the updates. I'm wondering how this fits into #8584 (comment) Is there a way to leverage this to parse SpEL expressions independent of where they come from? I cannot find out how this would be leveraged. I'm also not convinced a direct mapping of imperative to reactive makes sense here. Spring Security's ReactiveAuthorizationManager supports a richer model (Mono<AuthorizationDecision>) than the imperative model (true/false).

I'm thinking the first step is to create an ReactiveAuthorizationManager<Expression>. It should be able to evaluate the Expression and turn it into Mono<AuthorizationDecision>. It would support reactive (see ReactiveAdapterRegistry) or imperative results of Boolean and AuthorizationDecision. This could be reused for #5867 I'd create a separate pull request for this.

Then we can create a separate pull request with an implementation of ReactiveAuthorizationManager<MethodInvocation> that obtains the Expression and delegates to ReactiveAuthorizationManager<Expression>.

private ExpressionParser expressionParser = new SpelExpressionParser();
private BeanResolver br;
private RoleHierarchy roleHierarchy;
private PermissionEvaluator permissionEvaluator = new DenyAllPermissionEvaluator();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PermissionEvaluator is not a reactive API, so I don't think we should use it here.

}

private Mono<Boolean> hasAnyAuthorityName(String prefix, String... roles) {
return getAuthoritySet().map(roleSet -> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this could be replaced with AuthorityReactiveAuthorizationManager

}

public final Mono<Boolean> isAuthenticated() {
return isAnonymous().map(aBoolean -> !aBoolean);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this could be replaced with AuthenticatedReactiveAuthorizationManager

* @author Sheiy
* @since 5.4
*/
public abstract class SecurityExpressionReactiveRoot implements SecurityExpressionReactiveOperations {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that most of the expressions should be mapped to ReactiveAuthorizationManager implementations. I gave a few examples below. If there isn't one, then we should add it.

* Allows "denyAll" expression
*/
public final boolean denyAll = false;
private PermissionEvaluator permissionEvaluator;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PermissionEvaluator is not reactive, so we should probably not be using it here.

return Mono.just(true);
}
try {
return Mono.just(ExpressionUtils.evaluateAsBoolean(preAuthorize, ctx));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should not be a try/catch. Instead I'd expect that ReactiveExpressionUtils can handle multiple return types using instanceof checks


private final MethodSecurityMetadataSource attributeSource;

private final PreInvocationAuthorizationAdvice preInvocationAdvice;
private PreInvocationAuthorizationAdvice preInvocationAdvice;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than have another member and and if/else statement, if the user passes in PreInvocationAuthorizationAdvice it could be adapted to match PreInvocationAuthorizationReactiveAdvice

import org.springframework.expression.Expression;
import reactor.core.publisher.Mono;

public final class ReactiveExpressionUtils {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only used in a single location and can probably be moved to a private method.

* @author Sheiy
* @since 5.1
*/
public interface SecurityExpressionReactiveOperations {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should use the naming ReactiveSecurityExpressionOperations

* @author Sheiy
* @since 5.4
*/
public abstract class SecurityExpressionReactiveRoot implements SecurityExpressionReactiveOperations {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the name should be ReactiveSecurityExpressionRoot

@sheiy
Copy link
Author

sheiy commented Jun 2, 2020

@rwinch Thanks for your reply. I'm sorry for that I'm not familiar with spring-security.
I will close this PR and wait for your commit of ReactiveAuthorizationManager.

@sheiy sheiy closed this Jun 2, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: waiting-for-triage An issue we've not yet triaged
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants