Improve authorization docs and tracing guidance#16
Conversation
There was a problem hiding this comment.
Pull request overview
Updates Gatehouse’s public-facing documentation to better match actual evaluation behavior and emitted tracing/telemetry, and clarifies a few semantics around policy composition and ReBAC resolution.
Changes:
- Documented decision semantics (short-circuit behavior, summary denial reasons, and
PolicyBuilder::effect(Effect::Deny)semantics) in crate docs and README. - Added explicit tracing/telemetry field reference (including fallback behavior when
security_rule()isn’t overridden). - Clarified
RelationshipResolvererror semantics (resolver must flatten failures intofalse) and updated examples/docs accordingly.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
src/lib.rs |
Expanded crate-level docs for decision/trace semantics, tracing fields, and clarified to_result() + PolicyBuilder::effect + RelationshipResolver behavior. |
README.md |
Added Quick Start + decision semantics + tracing field reference; aligned README API descriptions with the crate. |
examples/rebac_policy.rs |
Updated example docs to reflect that resolver failures must map to denial due to bool return type. |
examples/groups_policy.rs |
Fixed trace output comment to match the actual policy name shown (StaffPolicy). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| let admin_policy = PolicyBuilder::<User, Document, Action, Context>::new("AdminOnly") | ||
| .subjects(|user| user.roles.iter().any(|role| role == "admin")) | ||
| .build(); | ||
|
|
||
| let owner_policy = PolicyBuilder::<User, Document, Action, Context>::new("OwnerOnly") | ||
| .when(|user, _action, resource, _ctx| resource.owner_id == user.id) | ||
| .build(); | ||
|
|
||
| let mut checker = PermissionChecker::new(); | ||
| checker.add_policy(admin_policy); | ||
| checker.add_policy(owner_policy); | ||
|
|
||
| # tokio_test::block_on(async { | ||
| let user = User { | ||
| id: 1, | ||
| roles: vec!["admin".to_string()], | ||
| }; | ||
| let document = Document { owner_id: 1 }; | ||
|
|
||
| let evaluation = checker | ||
| .evaluate_access(&user, &Action, &document, &Context) | ||
| .await; | ||
|
|
||
| assert!(evaluation.is_granted()); | ||
| println!("{}", evaluation.display_trace()); | ||
|
|
||
| let outcome: Result<(), String> = evaluation.to_result(|reason| reason.to_string()); | ||
| assert!(outcome.is_ok()); | ||
| # }); |
There was a problem hiding this comment.
The Quick Start snippet uses rustdoc-style hidden lines (# tokio_test::block_on(async { ... # });). In a README code fence these # prefixes are not hidden, so the example is harder to copy/paste and also depends on tokio_test. Consider rewriting the snippet as a normal #[tokio::main] async fn main() (or similar) without #-prefixed lines so it works as a standalone example.
| let admin_policy = PolicyBuilder::<User, Document, Action, Context>::new("AdminOnly") | |
| .subjects(|user| user.roles.iter().any(|role| role == "admin")) | |
| .build(); | |
| let owner_policy = PolicyBuilder::<User, Document, Action, Context>::new("OwnerOnly") | |
| .when(|user, _action, resource, _ctx| resource.owner_id == user.id) | |
| .build(); | |
| let mut checker = PermissionChecker::new(); | |
| checker.add_policy(admin_policy); | |
| checker.add_policy(owner_policy); | |
| # tokio_test::block_on(async { | |
| let user = User { | |
| id: 1, | |
| roles: vec!["admin".to_string()], | |
| }; | |
| let document = Document { owner_id: 1 }; | |
| let evaluation = checker | |
| .evaluate_access(&user, &Action, &document, &Context) | |
| .await; | |
| assert!(evaluation.is_granted()); | |
| println!("{}", evaluation.display_trace()); | |
| let outcome: Result<(), String> = evaluation.to_result(|reason| reason.to_string()); | |
| assert!(outcome.is_ok()); | |
| # }); | |
| #[tokio::main] | |
| async fn main() { | |
| let admin_policy = PolicyBuilder::<User, Document, Action, Context>::new("AdminOnly") | |
| .subjects(|user| user.roles.iter().any(|role| role == "admin")) | |
| .build(); | |
| let owner_policy = PolicyBuilder::<User, Document, Action, Context>::new("OwnerOnly") | |
| .when(|user, _action, resource, _ctx| resource.owner_id == user.id) | |
| .build(); | |
| let mut checker = PermissionChecker::new(); | |
| checker.add_policy(admin_policy); | |
| checker.add_policy(owner_policy); | |
| let user = User { | |
| id: 1, | |
| roles: vec!["admin".to_string()], | |
| }; | |
| let document = Document { owner_id: 1 }; | |
| let evaluation = checker | |
| .evaluate_access(&user, &Action, &document, &Context) | |
| .await; | |
| assert!(evaluation.is_granted()); | |
| println!("{}", evaluation.display_trace()); | |
| let outcome: Result<(), String> = evaluation.to_result(|reason| reason.to_string()); | |
| assert!(outcome.is_ok()); | |
| } |
| ```rust | ||
| #[async_trait] | ||
| trait Policy<Subject, Resource, Action, Context> { | ||
| trait Policy<Subject, Resource, Action, Context>: Send + Sync { | ||
| async fn evaluate_access( | ||
| &self, | ||
| subject: &Subject, | ||
| action: &Action, | ||
| resource: &Resource, | ||
| context: &Context, | ||
| ) -> PolicyEvalResult; | ||
|
|
||
| fn policy_type(&self) -> String; | ||
|
|
||
| fn security_rule(&self) -> SecurityRuleMetadata { | ||
| SecurityRuleMetadata::default() | ||
| } | ||
| } |
There was a problem hiding this comment.
The Policy trait snippet is missing the imports needed to compile (async_trait::async_trait, PolicyEvalResult, and SecurityRuleMetadata / gatehouse::*). Since the surrounding text emphasizes aligning docs with the public API, it would be helpful if this example were copy/paste compilable by adding the minimal use lines or qualifying the types.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 6 changed files in this pull request and generated no new comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Summary
PolicyBuilder::effect(Effect::Deny)behaviorVerification