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
Apply additional JAX-RS security only for endpoints and not all resource methods #28820
Apply additional JAX-RS security only for endpoints and not all resource methods #28820
Conversation
Thanks for this. I'll have a look tomorrow |
...ns/security/spi/src/main/java/io/quarkus/security/spi/AdditionalSecuredClassesBuildItem.java
Show resolved
Hide resolved
Hi @michalvavrik Thanks for another quality PR, but I'm concerned that such changes are needed to address this rather edge case. Let me also comment inline... |
...eployment/src/test/java/io/quarkus/resteasy/reactive/server/test/security/HelloResource.java
Show resolved
Hide resolved
@sberyozkin I don't mind to drop the PR (you can close it yourselves as far as I'm concerned), however the issue is quite serious when JAX-RS config are used - all resource methods (public non static etc.) are secured with interceptor, it can lead to quite unexpected behavior. I can only provide my user opinion - that's not what I expect when I use If you have better suggestion, bring it on, please. |
22853dc
to
ba533f2
Compare
Hey @michalvavrik
Just because I'm not immediately getting your changes does not mean I'd like the current PR be dropped :-)
Can you please check what happens in such a case with and without your PR ? And what happens if we add I think it would be useful to see a bigger picture of how having Cheers |
@sberyozkin thank you for taking your time, but I think you got this wrong - it's not connected to exception mappers or The reason for this is that for each path we apply path matching check, there are no interceptors in action. Here, the issue comes from applying interceptors where they shouldn't be applied.
If you add yourself
As I tried to point out, the very same happen when without @Path("/hello")
public static class HelloResource {
@PermitAll
@GET
public String hello() {
return getHello();
}
public String getHello() {
return "hello";
}
} would you expect |
@michalvavrik, I was thinking about it on the way home, and I believe I came to the conclusion that opening up non-endpoint methods which are invoked as part of the failed authentication request processing is a risky idea, can cause a surprise and introduces inconsistency. Please take more feedback/don't close the PR, but IMHO we need a different solution. Let me comment more on it below,
I think I don't agree with this justification. Don't I do it myself as well with Re your updated example
Yes, because it is also secured with I'd like to propose to take a step back and ask why Thanks |
This comment has been minimized.
This comment has been minimized.
As you wish; |
I think the issue being reported here is real and makes sense to try to be addressed. However I need to take a good look at the changes themselves as they seem to be pretty far reaching. |
...on/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/EndpointIndexer.java
Outdated
Show resolved
Hide resolved
existingClassNameBindings, considerApplication, httpAnnotationToMethod, index, applicationScanningResult, | ||
getAnnotationStore()); | ||
Map<String, BasicResourceClassInfo> basicResourceClassInfoMap = new HashMap<>(); | ||
for (FoundEndpoint endpoint : endpoints) { |
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 can't really understand the changes here. Can you elaborate?
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.
createEndpoints
does 2 things:
- determine if resource method is an endpoint
- create endpoint
I had a PR back in the days where I needed to collect all endpoints and you pointed out I should keep the logic in one place (one version of #26567). The changes here just try to divide the two things without adding extra action. So for performance reasons, with each endpoint method it remembers its class, classNameBindings and httpMethod. If you look couple lines below, the logic is same -> lines are just moved to collectEndpoints
.
|
||
public class AdditionalRolesAllowedTransformer implements AnnotationsTransformer { | ||
|
||
private final Set<String> classNames; | ||
private final Set<MethodDescriptor> methods; |
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.
Again, I would much rather prefer us to use something other than a Gizmo type here
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.
It's replaced by MethodDescription
, that should be ok as it belongs to security and is very much related (used for security checks).
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'll have look on Monday
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, well appreciated.
Hey @michalvavrik
It is not a wish :-), I just I feel we should address it differently. What if we have some custom non-sec exception mapper declared on a method expecting a SecurityIdentiy injection, etc... Hey @geoand
+1 to it, my concern though is that it should not be addressed by disabling the implicit security checks for the methods declaring the mapper since, among other things, it would be inconsistent with how it works when the same restriction is enabled via the direct annotation or HTTP security policy. |
@sberyozkin you are probably right. I admit I have not looked into the originally reported issue. I'll do that and post back |
My conclusion is that what @michalvavrik is correct. The originally reported problem is caused because the security interceptor is getting called when it clearly should not (for reasons explained in this PR). |
@geoand But I don't agree we can have a case where setting these properties opens up such mapper methods while having |
Sorry, I don't understand what you mean. Can you explain with an example perhaps? Thanks |
@geoand sure, I commented about it in #28820 (comment), and then in #28820 (comment) as well, we have 3 mechanisms to request that the method access is denied. This PR is dealing with one of such mechanisms. The mechanics are different for each of them but the intention is the same, and it has to remain consistent IMHO how non-endpoint methods are treated - for the 2 cases the security is enforced. The other point I made to Michal is that fixing the Blocking exception by making non-endpoint methods open to the anonymous access seems risky. I have no specific attack scenario in mind right now, but I'm finding it somehow wrong that a secure method invocation switches to the anonymous access method to complete the call, it feels the whole call should remain under the same constraints, it is the same thread which tries both the method invocation and then attempts to map the exception. |
The other scenario I mentioned, we have a method-level mapper for some custom exception which happens as part of the successfully authenticated request - the mapper would like to have the security identity injected. If it works with exception mappers in separate classes but not with the method level mappers then it is a problem IMHO |
I think HTTP security policy has no way of securing the exception mappers as it uses path matching so mappers are not secured. If you gave concrete example (or point me to docs) it would be great. Thank you
One test I added shows add if you have endpoint secured with |
I've asked you to check earlier
I thought you did, sorry if you did not, please check.
Nonetheless it how it also works with @Denyall. There is nothing conflicting there if having @Denyall behavior is accepted. Also, I've expressed two other concerns:
What is your take on the last two points ? |
I added them to the issue reproducer and its tests succeeded. Now I did it again. They succeeded as the mapper is not secured by the policy. I only tried to point out that it's not possible to secure non-endpoints with ^^^ as the check (that's how I read the code, I can be simply wrong) is applied directly to the route in Vert.X handler.
Sure, agree.
Agree.
From my point of view, I think I understand Sergeys arguments and I don't have anything else to contribute to the discussion. Thanks for the time you spend on the review. |
@michalvavrik Thanks, one thing I believe is important is that we have a consistent approach, so we have
It feels like we'll need another issue to get it all in sync :-), I'd suggest tightening HTTP security policy in this case but may be it is only me... |
Here are my thoughts:
|
@geoand Hi, yes, PR is a quality one which I agreed to, but I can't shake the feeling we are on the wrong path here, we are essentially working around the technical decision made at the RestReasy Reactive level, which is to treat the exception mapping provided at the JAX-RS method level as a class method invocation which picks up all the security interceptors, and to make it work we have to let the same thread which failed the security check re-enter the application endpoint without any restriction. It just feels wrong - I can't offer anything but theoretical risk analysis here right now - but nonetheless it feels wrong. In all other cases this is simply not a problem because the same thread is not re-entering the same invocation chain, so the problem of non-endpoint methods being secured is non-existent in practice in all but this case where a mapper method is called like a regular resource method. Please have a look at #28820 (comment), what is your take on the 2 concerns there (at the end of that comment) ? IMHO it is worth examining other alternatives:
I'm not going to block this PR if Stuart approves Cheers |
Class level exceptions mappers is a feature plenty of people wanted and have known from Spring as well. On the implementation side, having it invoked via the same JAX-RS Resource class and thread that handles the actual Resource Method invocation is the only thing that makes sense IMHO. |
When you say injected, you mean with |
Yeah, this could be interesting to check out. @mkouba is there a way to get a bean without its interceptors? |
How do you propose this would be done? If it means, switching threads, then no, that should not be done |
The feature is cool, no doubt, it is the fact the the thread which is associated with the failed security check re-enters the application code which is of concern. If we have a usual |
I'm not sure what it will mean it the thread which entered the mapper has not gone through a sec check ? That said, yeah, looks like this particular concern is unfounded, as the same situation would take place with a regular |
In that case, which thread is used? |
Yeah, that's my point |
I was thinking more about the actual cause, what is really blocking in this deny interceptor Lets see also if Martin has some suggestion re skipping CDI interceptors in this case, Resteasy Reactive does not apply them to the regular I'll sign off in 40 mins or so for a long weekend break, thanks Michal, Georgios for the discussion :-) |
If it is the regular mapper then it is also an IO thread I believe, but the resource class is not invoked, so no CDI interceptors, no Blocking problems, this is my understanding... |
Thank you! |
Nope, there's no way to do this. For an intercepted bean |
I was afraid that would be the answer :) Thanks for the insights |
ba533f2
to
5e670e4
Compare
Unfortunately I am being pulled in too many directions to be able to properly understand and assess the implicattions of changes being introduced here. |
@geoand please take your time / request changes / whatever you like. This is not urgent, just get back to it one day, please. |
I definitely will! Thanks again for all your great work and for your patience :) |
@michalvavrik Can you clarify please what is causing IO thread to block in this case ? This is the last question I have before totally realizing this PR fix is the only possible fix, thanks |
@sberyozkin sure, in this case |
I'm fine with RESTEasy Reactive changes. |
Thanks @michalvavrik Now all the options have been explored (IMHO it was worth it), your original implementation idea does is indeed best :-), let me merge as Georgios also agrees, thanks |
fixes: #28791
When using JAX-RS security configuration properties
quarkus.security.jaxrs.deny-unannotated-endpoints
andquarkus.security.jaxrs.default-roles-allowed
all resource methods that can be intercepted (endpoints or not) are secured by security interceptors (e.g.DenyAllInterceptor
andRolesAllowedInterceptor
). This leads to unexpected behavior when non-endpoints are invoked in a fashion that can be intercepted, one example - add@ServerExceptionMapper
to a resource (methods annotated with this annotation also expects to be in resource, see its Javadoc), this mapper method will be secured (and if it handles Unauthorized, it should lead to circular reference).This PR secures endpoints rather than classes. To do that in RR, I had to extract logic behind endpoint selection (to keep the logic in one place).