-
Notifications
You must be signed in to change notification settings - Fork 6k
Support Mono<Boolean> for Method Security SpEL expressions #4841
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
Comments
Based on your comment on this issue the fact that this ticket remains open does that mean it is not possible to do the following? Do the methods referenced within the expression in the @Component
public class SomeBean {
public Mono<Boolean> someMethod() {
return Mono.fromRunnable(() -> Mono.just(true));
}
}
@RestController
public class SomeController {
@GetMapping("/somePath")
@PreAuthorize("@someBean.someMethod()")
public SomePojo getSomething() {
return new SomePojo();
}
} |
I believe I've answered my own question and the answer is that I can not do what I posted above. Seems what is missing in Spring Security are reactive versions of Am I correct in that assessment? |
That and we need a reactive equivalent of |
Isn't Would it be better to create a new version of |
Would you be open to a PR for this or is it something you are already actively working on? |
You are right but it would need to accept the reactive advice.
Modify the existing one, deprecate the existing constructor (modify it to just adapt to use the reactive APIs to simplify the code), add a new constructor w/ reactive equivalents
I'd love a PR for this. |
After looking at this and getting most of the way through it - its not as simple as originally planned. It stems from the fact that public final class ReactiveExpressionUtils {
/**
* Evaluates an {@link Expression} that can either return a {@code boolean} or a {@code Mono<Boolean>}.
* @param expr The {@link Expression}
* @param ctx The {@link EvaluationContext}
* @return A {@link Mono} that can be subscribed to containing the result of the expression
*/
public static Mono<Boolean> evaluateAsBoolean(Expression expr, EvaluationContext ctx) {
return Mono.defer(() -> {
try {
Object value = expr.getValue(ctx);
if (value instanceof Boolean) {
return Mono.just((Boolean) value);
}
else if (value instanceof Mono) {
return ((Mono<?>) value)
.filter(Boolean.class::isInstance)
.cast(Boolean.class)
.switchIfEmpty(createInvalidTypeMono(expr));
}
else {
return createInvalidTypeMono(expr);
}
}
catch (EvaluationException ex) {
return Mono.error(new IllegalArgumentException(String.format("Failed to evaluate expression '%s': %s", expr.getExpressionString(), ex.getMessage()), ex));
}
});
}
private static Mono<Boolean> createInvalidTypeMono(Expression expr) {
return Mono.error(new IllegalArgumentException(String.format("Expression '%s' needs to either return boolean or Mono<Boolean> but it does not", expr.getExpressionString())));
}
} From there we also need a reactive version of I should have something in the next few days to share. Just need to finish a couple things & apply some polish. |
Hi @rwinch I'm just about done but the last piece I'm stuck on is how to handle In the reactive version I am building, the argument wouldn't be a I've been scratching my head on the best way to handle that but I'm just not sure how to do it. I thought I would ask for help. Do we punt on supporting Essentially what we need is a |
Little more investigating - I can do something I consider somewhat dirty by doing something like if (ProxyMethodInvocation.class.isAssignableFrom(mi.getClass())) {
args[0] = this.expressionHandler.filter(filterTarget, preFilter, ctx);
((ProxyMethodInvocation) mi).setArguments(args);
} This would work in the case where the I'm still not sure how to handle the other case where the user used the |
Can you point me to your work in progress with a test that I can run that illustrates the problem you are having? |
Hi @rwinch I've committed everything to the If you take a look at There are an entire suite of unit tests within |
Thanks for the look at the code. I may be easier to submit a PR so that feedback can be provided. However, I will provide feedback here this time:
I'd like to see the above resolved and a PR submitted so we can more easily discuss. |
Thanks @rwinch for the feedback. I'll work in some of the changes and submit as a PR.
The tests that were removed don't really make sense for reactive types. Things like @PostAuthorize("returnObject?.contains(authentication?.name)") when the return type is a We can certainly discuss further once I get a PR going. |
It should support the non-reactive return type as it did before though.
👍 |
The linked PR seems to have stalled. Are there any plans to continue with this in the near future or is it very unlikely to make it into the 5.2 release? In its current state, reactive method security is close to unusable for securing REST endpoints, as certain blocking operations like database calls are pretty much unavoidable for all but the most basic access control scenarios. |
I just have not had a chance to get back to the PR unfortunately. |
Also waiting for this PR |
Sorry I just have not had time to get back to this. I don't work for Spring - I just do this in my spare time. I'll see if I can allocate some time to get back to this. |
Rob Winch, any idea of when we can see this feature in Spring Security? |
Or is anybody working on this (community or from the spring team) ? |
There is no update on it. If people are interested in it, please react with 👍 to the original report. Better yet, if you are interested in contributing, please let us know |
Hi Rob, I would be interested in. However, I could not start before December. |
@rwinch just so I can stop looking, is that what the thymeleaf guys are waiting for before something like |
I'm not sure. The issue is that we need |
Thanks for the response @rwinch. I'm trying to trace what might be causing this error from a webflux / spring boot / spring security / okta / thymeleaf app using all the starters and autoconfig etc |
@RobMaskell No that is a separate issue. Since WebFlux uses Java Configuration it doesn't really do much with SpEL (typically a lambda can be used instead). Please create a ticket for SpEL authorization support in Reactive security and link to your comment. |
You are right this is the same issue. I had totally forgotten about it. Would you be interested in submitting a PR for the issue? |
Three years, no one to support this feature? |
If there isn't planned support for this, could we perhaps get public access to the various helper classes so we can roll home-grown implementations? 🙃 It seems that some of them are public while others are not. |
I've stumbled upon this issue in my pet project, so I've created custom hacky workaround which basically just evaluates SpEL expression which is placed in my custom annotation and has I really hope that there will be built-in support for it in the future. |
@PaperPlane01 nice thanks! Here is the kotlin implementation also supporting Flow: https://gist.github.com/RobertHeim/fc2a87c453c15da8d56e2ae141c24d1d But it does not support coroutines for now. Do you have an idea how to get the
I thought of something along the lines of (blocking just for testing now)
But it complains that it cannot find Overall it seems that we will need to stop working with Our "workaround" probably will be to just have another layer between controllers and service that implement the permissions. in plain code. Note that in this case it might be nice to implement the service interface twice to ensure that the interface is always up to date:
Controllers Autowire the service normally and get the primary, secured implementation. Each part is easily testable and only focuses on one layer. |
Isn't this supposed to be |
yes, your right, since it is a normal variable in this implementation. |
I'm closing this in favor of gh-9401 which will address this issue as well |
is there any new state for this ticket in spring boot 3.1? does it support customizing @PreAuthorize? |
Summary
Reactive method security requires the SpEL expression to return Boolean which does not work of the logic to determine access is blocking. We should allow the result to be
Mono<Boolean>
The text was updated successfully, but these errors were encountered: