Expand description
A flexible authorization library that combines role‐based (RBAC),
attribute‐based (ABAC), and relationship‐based (ReBAC) policies.
The library provides a generic Policy
trait for defining custom policies,
a builder pattern for creating custom policies as well as several built-in
policies for common use cases, and combinators for composing complex
authorization logic.
§Overview
A Policy is an asynchronous decision unit that checks if a given subject may
perform an action on a resource within a given context. Policies implement the
Policy
trait. A PermissionChecker
aggregates multiple policies and uses OR
logic by default (i.e. if any policy grants access, then access is allowed).
The PolicyBuilder
offers a builder pattern for creating custom policies.
§Built in Policies
The library provides a few built-in policies:
RbacPolicy
: A role-based access control policy.AbacPolicy
: An attribute-based access control policy.RebacPolicy
: A relationship-based access control policy.
§Custom Policies
Below we define a simple system where a user may read a document if they are an admin (via a simple role-based policy) or if they are the owner of the document (via an attribute-based policy).
// Define our core types.
#[derive(Debug, Clone)]
pub struct User {
pub id: Uuid,
pub roles: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct Document {
pub id: Uuid,
pub owner_id: Uuid,
}
#[derive(Debug, Clone)]
pub struct ReadAction;
#[derive(Debug, Clone)]
pub struct EmptyContext;
// A simple RBAC policy: grant access if the user has the "admin" role.
struct AdminPolicy;
#[async_trait]
impl Policy<User, Document, ReadAction, EmptyContext> for AdminPolicy {
async fn evaluate_access(
&self,
user: &User,
_action: &ReadAction,
_resource: &Document,
_context: &EmptyContext,
) -> PolicyEvalResult {
if user.roles.contains(&"admin".to_string()) {
PolicyEvalResult::Granted {
policy_type: self.policy_type(),
reason: Some("User is admin".to_string()),
}
} else {
PolicyEvalResult::Denied {
policy_type: self.policy_type(),
reason: "User is not admin".to_string(),
}
}
}
fn policy_type(&self) -> String { "AdminPolicy".to_string() }
}
// An ABAC policy: grant access if the user is the owner of the document.
struct OwnerPolicy;
#[async_trait]
impl Policy<User, Document, ReadAction, EmptyContext> for OwnerPolicy {
async fn evaluate_access(
&self,
user: &User,
_action: &ReadAction,
document: &Document,
_context: &EmptyContext,
) -> PolicyEvalResult {
if user.id == document.owner_id {
PolicyEvalResult::Granted {
policy_type: self.policy_type(),
reason: Some("User is the owner".to_string()),
}
} else {
PolicyEvalResult::Denied {
policy_type: self.policy_type(),
reason: "User is not the owner".to_string(),
}
}
}
fn policy_type(&self) -> String {
"OwnerPolicy".to_string()
}
}
// Create a PermissionChecker (which uses OR semantics by default) and add both policies.
fn create_document_checker() -> PermissionChecker<User, Document, ReadAction, EmptyContext> {
let mut checker = PermissionChecker::new();
checker.add_policy(AdminPolicy);
checker.add_policy(OwnerPolicy);
checker
}
let admin_user = User {
id: Uuid::new_v4(),
roles: vec!["admin".into()],
};
let owner_user = User {
id: Uuid::new_v4(),
roles: vec!["user".into()],
};
let document = Document {
id: Uuid::new_v4(),
owner_id: owner_user.id,
};
let checker = create_document_checker();
// An admin should have access.
assert!(checker.evaluate_access(&admin_user, &ReadAction, &document, &EmptyContext).await.is_granted());
// The owner should have access.
assert!(checker.evaluate_access(&owner_user, &ReadAction, &document, &EmptyContext).await.is_granted());
// A random user should be denied access.
let random_user = User {
id: Uuid::new_v4(),
roles: vec!["user".into()],
};
assert!(!checker.evaluate_access(&random_user, &ReadAction, &document, &EmptyContext).await.is_granted());
§Evaluation Tracing
The permission system provides detailed tracing of policy decisions, see AccessEvaluation
for an example.
§Combinators
Sometimes you may want to require that several policies pass (AND), require that
at least one passes (OR), or even invert a policy (NOT). gatehouse
provides
combinators for this purpose:
Structs§
- Abac
Policy - An attribute-based access control policy.
Define a
condition
closure that determines whether a subject is allowed to perform an action on a resource, given the additional context. If it returns true, access is granted. Otherwise, access is denied. - AndPolicy
- Empty
Policies Error - Error returned when no policies are provided to a combinator policy.
- Eval
Trace - Container for the evaluation tree Detailed trace of all policy evaluations
- NotPolicy
- NotPolicy
- OrPolicy
- OrPolicy
- Permission
Checker - A container for multiple policies, applied in an “OR” fashion. (If any policy returns Ok, access is granted) Important: If no policies are added, access is always denied.
- Policy
Builder - A builder API for creating custom policies.
- Rbac
Policy - A role-based access control policy.
- Rebac
Policy - ReBAC Policy
Enums§
- Access
Evaluation - The complete result of a permission evaluation. Contains both the final decision and a detailed trace for debugging.
- Combine
Op - The type of boolean combining operation a policy might represent.
- Effect
- Represents the intended effect of a policy.
- Policy
Eval Result - The result of evaluating a single policy (or a combination).
Traits§
- Policy
- A generic async trait representing a single authorization policy. A policy determines if a subject is allowed to perform an action on a resource within a given context.
- Relationship
Resolver - A trait that abstracts a relationship resolver. Given a subject and a resource, the resolver answers whether the specified relationship e.g. “creator”, “manager” exists between them.