-
Notifications
You must be signed in to change notification settings - Fork 3k
Make response available on error in rest client #45282
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
b234baa to
4bb672d
Compare
This comment has been minimized.
This comment has been minimized.
|
Thanks for this. It would be nice to have some tests covering the functionality |
4bb672d to
8c7a5c3
Compare
This comment has been minimized.
This comment has been minimized.
|
@FroMage WDYT? |
|
I think it makes sense, but I want @FroMage to have a look |
| * Subclass of {@link WebApplicationException} for use by clients, which forbids setting a response that | ||
| * would be used by the server. | ||
| * FIXME: I'd rather this be disjoint from WebApplicationException, so we could store the response info | ||
| * for client usage. Perhaps we can store it in an alternate field? |
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.
This explains that we did not want the client response to end up in the WebApplicationException.response field, and you're doing exactly that.
Doing this, we run the risk of user exception mappers that turn that Response into a server response, which is a huge security issue.
I'd much rather this response get added as a new field property getClientReponse(), would this work for you?
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 was aware of this, but I decided to put it in the response field anyway because now the exception implements ResteasyReactiveClientProblem to prevent that. Having another field to hold the response may result confusing, but I can add that property.
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 is not ideal, but the client web application exception type should be disjoint to the server web application exception type in an ideal world.
I think the (client) Response should go into another property, to make sure nobody ever treats this as a server response. The security risk is too great, in my opinion.
c719a91 to
881f277
Compare
| * would be used by the server. | ||
| * FIXME: I'd rather this be disjoint from WebApplicationException, so we could store the response info | ||
| * for client usage. Perhaps we can store it in an alternate field? | ||
| * FIXME: I'd rather this be disjoint from WebApplicationException, but changing the exception type could break existing |
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 considered not inheriting from WebApplicationException, as there's no use for it if I can't use it's response property... however, it may break code that is still catching it, right?
Let me know if this comment is accurate.
I'd rather have a separate exception type that has it's own response property to avoid confusion, though... right now people should know that the response is available in the new field instead of the standard one.
| } | ||
|
|
||
| public WebClientApplicationException(Response clientResponse) { | ||
| super("Server response is: " + clientResponse.getStatus(), new DummyResponse(clientResponse.getStatus(), |
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.
Preserved the dummy response in this case, so that it's not null and breaks existing code that expects to read the status code from the response field.
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.
This is not right, it will set a Response property from the client's response, which is a security risk. The super.response field must always be null, and the response should be stored in the clientResponse property only.
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.
🤔 That's how it was done before my change, and it looks like that's why the DummyResponse class was created for, so that clients can look at the status of the response from the server... If I set the response to null, then should I remove the DummyResponse class as well?
This comment has been minimized.
This comment has been minimized.
881f277 to
149d77b
Compare
| } | ||
|
|
||
| public WebClientApplicationException(Response clientResponse) { | ||
| super("Server response is: " + clientResponse.getStatus(), new DummyResponse(clientResponse.getStatus(), |
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.
This is not right, it will set a Response property from the client's response, which is a security risk. The super.response field must always be null, and the response should be stored in the clientResponse property only.
| + invokedMethod.getName() + "'"; | ||
| } | ||
| Response response = webApplicationException instanceof WebClientApplicationException wcae ? wcae.getClientResponse() | ||
| : webApplicationException.getResponse(); |
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.
@geoand I can't remember why we have ClientWebApplicationException and WebClientApplicationException when they implement the same types…
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 don't remember at all, but the commit that introduced the former was 6674bd5
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 wonder if this is not simply a mistake, I wonder what happens if we remove one of the two types…
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.
We can always try :)
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.
So, I can remove the lesser-used exception. Now, as it turns out, the other type has zero protection for hiding the Response like this type has.
If I add the protections to the other type, lots of tests fail, because clients expect the Response to be set when throwing, and unless we tell our users to stop using getResponse() and use getClientResponse() this won't work. And they likely will care that we break their applications this way, now.
Now, given the fact that we have the most used type of client exception actually contain a Response for years, and we've solved the security risk by marking both types of exception with a special marker interface, Quarkus REST is not vulnerable if the client exception's Response is set.
The only risk remaining is a user risk of adding their own ExceptionMapper or any type of filter and extracting client exception responses to turn them into a server response. I'm not sure we can fix that risk. Well, we could, I suppose also mark the client Response implementations and refuse to accept them as server responses. I'm not sure it's worth the effort.
WDYT @geoand ?
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've made an alternate PR at #46826 to remove the duplicate exception, and restore the client Response. Let's see what the full CI says. And what you think about the risk for users.
|
We merged #46826 which has your commits, so in theory this should be fixed. Thanks a lot for your help and patience :) |
|
@FroMage Thank you very much! 👍 |
Responses cannot be read if the HTTP status is not in the 2xx range and you request that the response is converted to a specific type, like when calling
In this case, an exception is thrown, but it contains a DummyResponse instance that doesn't give access to the Response object.
Being able to read the response from the server in case of error is extremely valuable in some cases, as it may contain useful information about the problem. Most REST APIs return this information as a JSON object that can be parsed, and the contents examined to see custom error status codes or passing the error message to the user, for example.
This PR sets the response in the exception object, without creating a new field for it, as implementing
ResteasyReactiveClientProblemshould prevent the server from trying to read the response.