Add functions for inspecting TPE determining policies#2401
Conversation
…peResponse::satisfied_forbids` Signed-off-by: John Kastner <jkastner@amazon.com>
There was a problem hiding this comment.
Pull request overview
This PR extends the (experimental) TPE public API to let callers inspect which policies were satisfied/determining for a partial-authorization outcome, and adjusts cedar-policy-core TPE internals to expose policy IDs by reference.
Changes:
- Add
TpeResponse::reason,TpeResponse::satisfied_permits, andTpeResponse::satisfied_forbidsto inspect determining/satisfied policies for partial decisions. - Change
cedar-policy-core::tpe::ResidualPolicy::get_policy_idto return&PolicyIDand update internal logic/tests accordingly. - Add/update tests and a changelog entry for the new API surface.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 10 comments.
| File | Description |
|---|---|
| cedar-policy/src/api/tpe.rs | Adds new TpeResponse inspection methods and corresponding tests. |
| cedar-policy/CHANGELOG.md | Documents the new TPE inspection API (needs alignment with actual changes). |
| cedar-policy-core/src/tpe/response.rs | Changes ResidualPolicy::get_policy_id to borrow the ID and updates set construction. |
| cedar-policy-core/src/tpe.rs | Updates tests to accommodate the borrowed policy ID accessor. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
This comment was marked as outdated.
This comment was marked as outdated.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This comment was marked as outdated.
This comment was marked as outdated.
luxas
left a comment
There was a problem hiding this comment.
Thanks, this is a good addition to the API surface 👍
What the Kubernetes integration in particular is interested in, is understanding that when given decision=Deny, did that originate from a matched deny policy or just no matching policies? In the latter case, one could treat that as a NoOpinion mode, and let other subsequent authorizers (if many) have their say.
| /// These are a subset of the determining policies for any subsequent | ||
| /// concrete reauthorization. |
There was a problem hiding this comment.
I'm not sure I understand the for any subsequent concrete reauthorization part of the comment, the returned policies already evaluated to true, so there is no need for reauthorization?
There was a problem hiding this comment.
The idea is that if you were to do concrete authorization, there might be more determining policies that aren't represented here. If you want to take some action based on which policies apply to a request, this might be important.
There was a problem hiding this comment.
expanded comments substantially. let me know if it makes sense
There was a problem hiding this comment.
Maybe determining policies for any subsequent concrete reauthorization could be reworded as determining policies of an concrete authorization request with a complete Entity store? I guess that's what you mean?
Signed-off-by: John Kastner <jkastner@amazon.com>
This comment was marked as outdated.
This comment was marked as outdated.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This comment was marked as outdated.
This comment was marked as outdated.
luxas
left a comment
There was a problem hiding this comment.
Thanks, I like it 👍
Should we make the error change in this or another PR? It could be done in another.
| /// These are a subset of the determining policies for any subsequent | ||
| /// concrete reauthorization. |
There was a problem hiding this comment.
Maybe determining policies for any subsequent concrete reauthorization could be reworded as determining policies of an concrete authorization request with a complete Entity store? I guess that's what you mean?
Signed-off-by: John Kastner <jkastner@amazon.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Coverage ReportHead Commit: Base Commit: Download the full coverage report. Coverage of Added or Modified Lines of Rust CodeRequired coverage: 80.00% Actual coverage: 100.00% Status: PASSED ✅ Details
Coverage of All Lines of Rust CodeRequired coverage: 80.00% Actual coverage: 87.91% Status: PASSED ✅ Details
|
luxas
left a comment
There was a problem hiding this comment.
Approving with some small renames that could be followed up upon if we like.
Thanks, this should give folks quite a lot of data to play with to deepen the understanding of the decision.
| @@ -130,28 +136,32 @@ impl<'a> Response<'a> { | |||
| match rp.get_effect() { | |||
| Effect::Forbid => { | |||
| if r.is_true() { | |||
There was a problem hiding this comment.
nit: feels like this could be a good candidate to have an enum / match statement for in the future.
There was a problem hiding this comment.
We could write this as a match on the residual, but I don't think I see how it would make the code any nicer
| .map(|id| self.residuals.get(id).unwrap()) | ||
| } | ||
|
|
||
| /// Get trivially erroring permit residual policies |
There was a problem hiding this comment.
| /// Get trivially erroring permit residual policies | |
| /// Get permit policies that errored during evaluation |
nit: not sure I understand the word trivially in this context, maybe just drop it?
| .map(|id| self.residuals.get(id).unwrap()) | ||
| } | ||
|
|
||
| /// Get trivially erroring forbid residual policies |
There was a problem hiding this comment.
| /// Get trivially erroring forbid residual policies | |
| /// Get forbid policies that errored during evaluation |
| /// Get the forbid policies that did not reach a concrete value or error for the partial request. | ||
| /// | ||
| /// This function only returns the `PolicyId`s for residual policies. | ||
| /// To access the residual policy conditions, use [`TpeResponse::nontrivial_residual_policies`]. |
There was a problem hiding this comment.
this reminds me that we should probably rename TpeResponse::nontrivial_residual_policies to just TpeResponse::residual_policies now, as we've anyways factored out the "trivial" ones to their own constructors and thus don't need that trivial/nontrivial distinction. That'd then be consistent with calling the other getters here "just" residual_forbids/permits
There was a problem hiding this comment.
I need to think more about the naming here. In some places "residual" refers to any output of partial evaluation (even if it reaches a concrete value). In other place "residual" specifically means a policy which didn't reach a concrete value.
| /// must return a concrete `Deny`. | ||
| pub fn true_forbids(&self) -> impl Iterator<Item = &PolicyId> { | ||
| self.0 | ||
| .satisfied_forbids() |
There was a problem hiding this comment.
seems like one missing getter rename
| /// policy. | ||
| pub fn true_permits(&self) -> impl Iterator<Item = &PolicyId> { | ||
| self.0 | ||
| .satisfied_permits() |
There was a problem hiding this comment.
one missing getter rename, or don't we want to update all of these at the same time?
Adds
TpeResponse::reason,TpeResponse::satisfied_permits, andTpeResponse::satisfied_forbidsto the public TPE API to enable callers to find determining policies for partial authorization responses.Description of changes
Issue #, if available
Checklist for requesting a review
The change in this PR is (choose one, and delete the other options):
cedar-policy(e.g., changes to the signature of an existing API).cedar-policy(e.g., addition of a new API).cedar-policy.cedar-policy-core,cedar-validator, etc.)I confirm that this PR (choose one, and delete the other options):
I confirm that
cedar-spec(choose one, and delete the other options):cedar-spec, and how you have tested that your updates are correct.)cedar-spec. (Post your PR anyways, and we'll discuss in the comments.)I confirm that
docs.cedarpolicy.com(choose one, and delete the other options):cedar-docs. PRs should be targeted at astaging-X.Ybranch, notmain.)