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

refactor(jans-cedarling): handle recoverable errors gracefully in authorization #10721

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from

Conversation

rmarinn
Copy link
Contributor

@rmarinn rmarinn commented Jan 23, 2025

Prepare


Description

This PR implements gracefully handling recoverable errors -- specifically, "bad input" errors where the issue is either a malformed or invalid input. In the case of a bad input, the call to Cedarling::authorize will not no longer return an error but will return an Ok with a DENY decision together with a reason.

Target issue

closes #10589

Implementation Details

  1. All the errors from the AuthorizeError enum that are just bad inputs and recoverable has been moved to a BadInputErrorEnum.

  2. AuthorizeResult has been updated to this:

#[derive(Debug, Serialize)]
pub struct AuthorizeResult {
    #[serde(serialize_with = "serialize_opt_response")]
    pub workload: Option<cedar_policy::Response>,
    #[serde(serialize_with = "serialize_opt_response")]
    pub user: Option<cedar_policy::Response>,
    #[serde(
        serialize_with = "serialize_reason_input",
        skip_serializing_if = "Option::is_none"
    )]
    pub reason_input: Option<BadInputError>,
    pub decision: bool,
}

which looks like this when serialized to json:

{
  "workload": null,
  "user": null,
  "reason_input": "Invalid Access token: Failed to decode JWT from Base64: Invalid last symbol 48, offset 749.",
  "decision": false
}

Test and Document the changes

  • Static code analysis has been run locally and issues have been fixed
  • Relevant unit and integration tests have been added/updated
  • Relevant documentation has been updated if any (i.e. user guides, installation and configuration guides, technical design docs etc)

Please check the below before submitting your PR. The PR will not be merged if there are no commits that start with docs: to indicate documentation changes or if the below checklist is not selected.

  • I confirm that there is no impact on the docs due to the code changes in this PR.

- gracefully handle authorization errors where the problem is just input
  errors or failing to build entities.
- update AuthorizeResult to return deny with some information if the
  authz was denied because of malformed inputs

Signed-off-by: rmarinn <[email protected]>
- move input errors from AuthorizeError to the new BadInputErrors enum

Signed-off-by: rmarinn <[email protected]>
@rmarinn
Copy link
Contributor Author

rmarinn commented Jan 23, 2025

try to merge #10591 before this one

olehbozhok
olehbozhok previously approved these changes Jan 23, 2025
})
.set_message("Result of authorize.".to_string()),
);
self.log_authz(LogAuthzArgs {
Copy link
Contributor

Choose a reason for hiding this comment

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

Please, do not touch logging part, to avoid conflicts in the #10705

Copy link
Contributor

Choose a reason for hiding this comment

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

If you wish to make this update, how about extract this changes to another branch? And I will merge it to my branch

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The changes does not only involve moving the logging to another file, some of the inputs change as well. Thus, if you want to move the changes, you might as well merge this whole branch onto your branch.

There will be conflicts no matter what, and we'll just have to deal with it.

Another option is to just merge main into your branch if this merges first.

Or I could also just wait for your PR then fix the conflicts if you want.


/// Error returned because of invalid or malformed inputs to [`Authz::authorize`]
#[derive(thiserror::Error, Debug)]
pub enum BadInputError {
Copy link
Contributor

Choose a reason for hiding this comment

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

Does all this error can happen only because of bad input only?
If we can't check jwt because of problems on issuer page (can't load keys), it is also bad input?

Copy link
Contributor Author

@rmarinn rmarinn Jan 24, 2025

Choose a reason for hiding this comment

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

If we can't check jwt because of problems on issuer page (can't load keys), it is also bad input?

Getting the keys happens on startup and in the background/SSE (not implemented yet) so JWT validation wont fail because of an HTTP request. However, if a decoding key cannot be found to decode the JWT or if the JWT is expired, etc., it's under the BadInputError.

jans-cedarling/cedarling/src/log/log_entry.rs Show resolved Hide resolved
serialize_with = "serialize_reason_input",
skip_serializing_if = "Option::is_none"
)]
pub reason_input: Option<BadInputError>,
Copy link
Contributor

Choose a reason for hiding this comment

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

I think It should be Vec because it can be more that one recoverable errors.

And in the issue is said that for "decision": "DENY" will be key with "errors" and for "decision": "DENY" will be key with "warnings".

It is planning?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think It should be Vec because it can be more that one recoverable errors.

We return immediately instead of going through the request the in the first recoverable error since there's no point continuing if for example the access token is malformed. In that context, is does Vec still make sense since i think it will always only have 1 item.

And in the issue is said that for "decision": "DENY" will be key with "errors" and for "decision": "DENY" will be key with "warnings".

Yeah, i just changed it from "warnings" to "reason_input" after looking at authzen. It looks like they prefer using "reason_*" for adding additional context.

Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm, the task handle recoverable errors gracefully in authorization. As I understand it means that if we have error on build role (for example) or other entity, and we can continue to execute authorize request we should continue. And this kind of errors should be collected in the vector (and be logged).

Copy link
Contributor

Choose a reason for hiding this comment

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

@nynymike what do you think?

Copy link
Contributor

Choose a reason for hiding this comment

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

I agree with your statement, but I think the scope of the issue was beyond authz. I don't want the Cedarling to crash ever... it should keep running. Your web server never crashes... A crash should only happen if something really catastrophic happens. Right now it seems like there are too many reasons the PDP might just crash.
That was my goal here...

Copy link
Contributor

Choose a reason for hiding this comment

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

@SafinWasi does flask-sidecar crash on bad input? Do you use try ... expect ... statement like here?

Copy link
Contributor

Choose a reason for hiding this comment

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

The sidecar does not crash on invalid input. It can catch cedarling exceptions and puts them in the response.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

refactor(jans-cedarling): handle recoverable errors gracefully in authorization
4 participants