Skip to content
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

RESTEasy Reactive - prevent repeating of standard security checks #26567

Conversation

michalvavrik
Copy link
Contributor

@michalvavrik michalvavrik commented Jul 5, 2022

fix: #26536

EagerSecurityHandler performs standard security checks for endpoints annotated with an RBAC annotation and the same checks are later done by SecurityHandler. We still need interceptors to run for CDI beans and gRPC. This PR prevents repeating of security checks by propagating the information that check is done to SecurityHandler via invocation context.

@michalvavrik
Copy link
Contributor Author

cc @sberyozkin @geoand

@quarkus-bot

This comment has been minimized.

@geoand
Copy link
Contributor

geoand commented Jul 6, 2022

Thanks for this!

CI seems unhappy however :)

@michalvavrik michalvavrik force-pushed the feature/resteasy-reactive-drop-double-sec-checks branch 2 times, most recently from c09d7f7 to 870c78e Compare July 6, 2022 08:59
@sberyozkin
Copy link
Member

@michalvavrik Thanks, it looks technically totally fine, thanks for working out how to handle it.
But it feels like we are doing a workaround, which in the RestEasy Reactive security case will involve running an extra interceptor with a few extra checks involved.
Would it be possible instead to remove RolesAllowedInterceptor/etc at the build level in the case where they are known to duplicate the checks ?

@michalvavrik
Copy link
Contributor Author

michalvavrik commented Jul 6, 2022

@sberyozkin yes, it would be possible and I actually already had it in progress... I stopped as the binding is between annotation (and its values) and the interceptor, not between method and the interceptor. Please remember we still need other annotated methods not checked by EagerSecurityHandler to be intercepted.

I had in progress InterceptedMethodBindingFilter that would be registered in the build time and break the binding between the method and interceptor during the build time somewhere around io.quarkus.arc.processor.BeanInfo#initInterceptedMethods. I just didn't think you would be happy if I messed with Arc Processor :-).

Is that how you meant it?

@michalvavrik
Copy link
Contributor Author

My point is that the check can only be removed on "method invocation" scope, not request scope as you need nested CDI beans to be able to perform its own (different checks) within same request.

@sberyozkin
Copy link
Member

sberyozkin commented Jul 6, 2022

Hey @michalvavrik the last thing that should be of concern is if some code in PR can make me less excited :-), you keep creating good quality PRs I may not be even getting from the first round of review :-).
But IMHO, if tweaking around io.quarkus.arc.processor.BeanInfo#initInterceptedMethods can disable those CDI interceptors then it would be preferred; have a look please when you get a chance, I guess we should not rush with merging it asap, so please take your time
thanks

@quarkus-bot

This comment has been minimized.

@michalvavrik michalvavrik marked this pull request as draft July 6, 2022 16:48
@michalvavrik
Copy link
Contributor Author

Sure, I'll find a build time solution as discussed. I changed PR to draft then.

@michalvavrik michalvavrik force-pushed the feature/resteasy-reactive-drop-double-sec-checks branch from 870c78e to 0665fa1 Compare July 11, 2022 22:53
@quarkus-bot quarkus-bot bot added the area/arc Issue related to ARC (dependency injection) label Jul 11, 2022
@michalvavrik michalvavrik marked this pull request as ready for review July 11, 2022 22:53
@michalvavrik michalvavrik force-pushed the feature/resteasy-reactive-drop-double-sec-checks branch from 0665fa1 to 68d5fb0 Compare July 11, 2022 23:11
@quarkus-bot

This comment has been minimized.

@michalvavrik
Copy link
Contributor Author

@sberyozkin ready for review

@sberyozkin
Copy link
Member

@michalvavrik Great, sorry for a delay, can you please resolve the conflict ?

@michalvavrik michalvavrik force-pushed the feature/resteasy-reactive-drop-double-sec-checks branch from 68d5fb0 to 0b92d3b Compare July 12, 2022 18:06
@michalvavrik
Copy link
Contributor Author

Sure, conflict resolved.

Copy link
Member

@sberyozkin sberyozkin left a comment

Choose a reason for hiding this comment

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

@michalvavrik I think this is a very nicely researched and done PR.

However, I think we need to get at least one more review as there might be some details that I've missed.

Thanks

@michalvavrik michalvavrik force-pushed the feature/resteasy-reactive-drop-double-sec-checks branch from 0b92d3b to a5c9818 Compare July 12, 2022 22:33
@sberyozkin
Copy link
Member

@mkouba Hi Martin, can you please have a look at the Arc related code in this PR when you are get a chance ?

@Ladicek
Copy link
Contributor

Ladicek commented Jul 29, 2022

I personally don't like the concept of "interceptor binding filtering", as it seems too easy to misuse, but I didn't see the code, so my hesitation is really quite a bit uninformed.

That said, let me draw a parallel with a very similar problem I'm facing with SmallRye Fault Tolerance (#21431). If a JAX-RS resource method is annotated @Blocking and e.g. @CircuitBreaker, then both RESTEasy Reactive and SmallRye Fault Tolerance will notice the @Blocking annotation and offload the method call to another thread. Essentially the same situation -- 1 annotation used by 2 frameworks that don't know about each other and can't easily communicate out of band. There's one more thing these two situations have in common -- the double behavior (be it double security check, or double thread offload) isn't really problematic in essence (as in, it doesn't cause bugs), it's just useless work (which should obviously be avoided).

It makes me wonder: is there an easy way to figure out if an intercepted method is a JAX-RS resource method? An interceptor (be it the security interceptor, or the fault tolerance interceptor) could adjust its behavior based on that. I think resource methods always have to have the HTTP verb annotation (@GET etc.), no? There's a fixed set of those annotations, and all other HTTP verb annotations must have one meta-annotation (whose name I can't remember). Right? (Not sure how subresource locators fit into this...) Would that make sense?

@geoand
Copy link
Contributor

geoand commented Jul 29, 2022

I like the idea @Ladicek, but I am wondering if we can make it a little more general.
My thinking is that each framework (in this case JAX-RS) knows about itself, so maybe it could push information into the CDI execution context in order to identify itself?

@Ladicek
Copy link
Contributor

Ladicek commented Jul 29, 2022

That would be great, I also thought about that a little -- problem is that the invocation context starts to exist only when the method in question is actually invoked. So at the point when RESTEasy Reactive does the security check, there's no shared storage for that information. A request-scoped bean might work, but it would bring a hard dependency on context propagation.

What I can imagine is ArC exposing an API for invoking a method and adding some contextual information to the invocation context.

I guess RESTEasy Reactive currently has to have some code that invokes the resource method. I don't know how it looks, but if the invocation is reflective, there must be something like this: method.invoke(instance, params). It could probably look like this instead:

Map<String, Object> data = Map.of("resteasy.reactive.resource.method", Boolean.TRUE);
Arc.container().runWithInterceptionContext(() -> method.invoke(instance, params), data);

(It would probably be a bit more complex, but you get the idea...)

@geoand
Copy link
Contributor

geoand commented Jul 29, 2022

The invocation in RR is not reflective, it's generated code, so we can do whatever we like in there :)

@Ladicek
Copy link
Contributor

Ladicek commented Jul 29, 2022

Right, gotcha.

The API I suggest above still requires some form of out-of-band communication, but that would now have a very short lifespan, so a thread local variable should be fine. It would still be quite a bit unsafe. Maybe @mkouba can think of something better/safer, but I think the approach should work.

@geoand
Copy link
Contributor

geoand commented Jul 29, 2022

👍🏼

@Ladicek
Copy link
Contributor

Ladicek commented Jul 29, 2022

I just realized that the concept of "executable methods", which we unfortunately failed to explore in the CDI 4.0 timeframe (see jakartaee/cdi#460), would be a perfect fit for this. If we had that, it would likely be straightforward to extend to support extending the context data map. Oh well.

@mkouba
Copy link
Contributor

mkouba commented Aug 2, 2022

My thinking is that each framework (in this case JAX-RS) knows about itself, so maybe it could push information into the CDI execution context in order to identify itself?

Well, there's no such thing like "CDI execution context". Yes, we have javax.interceptor.InvocationContext for intercepted methods but it's not stored in a threadlocal variable so you can't easily get a reference outside of the interceptors chain.

...but that would now have a very short lifespan, so a thread local variable should be fine. It would still be quite a bit unsafe.

It's safe until the execution is offloaded to a different thread.

I think that an idiomatic solution would be to introduce a new interceptor with priority 0 for any resource method, and this interceptor would set a flag in the context data map, i.e. something like InvocationContext.getContextData().put("resteasy.reactive.resource.method", Boolean.TRUE). Other interceptors in the chain could then read the context data map and adapt the behavior accordingly.

@michalvavrik
Copy link
Contributor Author

@mkouba that was my solution number one (it's already implemented), as long as other agrees, we could use interceptor solution for now.

@geoand
Copy link
Contributor

geoand commented Aug 2, 2022

Fine with me

@michalvavrik michalvavrik force-pushed the feature/resteasy-reactive-drop-double-sec-checks branch from 809091d to 110a083 Compare August 8, 2022 12:00
@michalvavrik
Copy link
Contributor Author

Done.

Copy link
Contributor

@geoand geoand left a comment

Choose a reason for hiding this comment

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

I added a minor comment, but otherwise this is a good with me!

@sberyozkin, @mkouba mind taking a look as well?

@michalvavrik michalvavrik force-pushed the feature/resteasy-reactive-drop-double-sec-checks branch from 110a083 to e2e94f1 Compare August 10, 2022 09:09
@mkouba
Copy link
Contributor

mkouba commented Aug 10, 2022

I added a minor comment, but otherwise this is a good with me!

@sberyozkin, @mkouba mind taking a look as well?

I've added a minor comment too and it's already addressed ;-). So let's wait for Sergey?

@gsmet
Copy link
Member

gsmet commented Aug 10, 2022

General comment but I see a lot of you here so it's in good hands: we should be extremely careful given we are changing a behavior related to security. Better check things three times to make sure we are all good.

@mkouba
Copy link
Contributor

mkouba commented Aug 10, 2022

General comment but I see a lot of you here so it's in good hands: we should be extremely careful given we are changing a behavior related to security. Better check things three times to make sure we are all good.

That's the reason why we wait for Sergey's opinion as well.

@quarkus-bot
Copy link

quarkus-bot bot commented Aug 10, 2022

Failing Jobs - Building e2e94f1

Status Name Step Failures Logs Raw logs
Gradle Tests - JDK 11 Build Failures Logs Raw logs
✔️ Gradle Tests - JDK 11 Windows

Full information is available in the Build summary check run.

Failures

⚙️ Gradle Tests - JDK 11 #

- Failing: integration-tests/gradle 

📦 integration-tests/gradle

io.quarkus.gradle.devmode.CompositeBuildWithDependenciesDevModeTest.main line 24 - More details - Source on GitHub

org.awaitility.core.ConditionTimeoutException: Condition with lambda expression in io.quarkus.test.devmode.util.DevModeTestUtils that uses java.util.function.Supplier, java.util.function.Supplierjava.util.concurrent.atomic.AtomicReference, java.util.concurrent.atomic.AtomicReferencejava.lang.String, java.lang.Stringboolean was not fulfilled within 1 minutes.
	at org.awaitility.core.ConditionAwaiter.await(ConditionAwaiter.java:167)
	at org.awaitility.core.CallableCondition.await(CallableCondition.java:78)

@michalvavrik
Copy link
Contributor Author

ping @sberyozkin

@sberyozkin
Copy link
Member

Hi All, apologies for keeping you waiting, was back on Mon but missed this one. So it is back to the original solution, which is fine, it is the easiest to understand.
@michalvavrik sorry you've had to do so many iterations :-), but as I said IMHO it was worth a try, as some very interesting comments were added, and Ladislav has identified what may prove to be the perfect solution once CDI executable methods are available, so there is a chance we can revisit this solution in the future :-)

@sberyozkin sberyozkin merged commit c48cc16 into quarkusio:main Aug 19, 2022
@quarkus-bot quarkus-bot bot added this to the 2.13 - main milestone Aug 19, 2022
@quarkus-bot quarkus-bot bot added the kind/enhancement New feature or request label Aug 19, 2022
@michalvavrik michalvavrik deleted the feature/resteasy-reactive-drop-double-sec-checks branch August 19, 2022 11:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/arc Issue related to ARC (dependency injection) area/resteasy-reactive area/security kind/enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

RestEasy Reactive performs authorization checks twice
7 participants