Skip to content

Latest commit

 

History

History
338 lines (254 loc) · 7.08 KB

File metadata and controls

338 lines (254 loc) · 7.08 KB

Authorization API

The Authorization API is the core of InferaDB's SDK, providing permission checks, relationship management, lookups, and real-time change streaming.

Getting Started

let org = client.organization("org_...");
let vault = org.vault("vlt_...");

Permission Checks

Basic Check

let allowed = vault.check("user:alice", "view", "doc:1").await?;
if allowed {
    // Grant access
}

Check with ABAC Context

Add attribute-based context to permission checks for fine-grained access control:

let allowed = vault.check("user:alice", "view", "doc:confidential")
    .with_context(Context::new()
        .with("ip_address", "10.0.0.50")
        .with("mfa_verified", true)
        .with("department", "engineering"))
    .await?;

Context values are evaluated against conditions defined in your authorization schema.

Require Permission (Guard Clause)

Use require() when denial should be an error (e.g., in middleware):

vault.check("user:alice", "edit", "doc:1").require().await?;
// If denied, returns Err(AccessDenied)

Batch Checks

Check multiple permissions efficiently in a single request:

let results = vault.check_batch([
    ("user:alice", "view", "doc:1"),
    ("user:alice", "edit", "doc:1"),
    ("user:alice", "delete", "doc:1"),
]).await?;

// Results maintain input order
for (i, allowed) in results.iter().enumerate() {
    println!("Check {}: {}", i, allowed);
}

// Convenience methods
if results.all_allowed() {
    // All permissions granted
}

let denied = results.denied_indices();

Consistency Tokens

For read-after-write consistency, use consistency tokens:

// Write returns a token
let token = vault.relationships()
    .write(Relationship::new("doc:1", "viewer", "user:alice"))
    .await?;

// Use token for consistent read
let allowed = vault.check("user:alice", "view", "doc:1")
    .at_least_as_fresh(token)
    .await?;

Relationships

Write a Relationship

vault.relationships()
    .write(Relationship::new("document:readme", "viewer", "user:alice"))
    .await?;

Write Multiple Relationships

vault.relationships().write_batch([
    Relationship::new("folder:docs", "viewer", "group:engineering#member"),
    Relationship::new("document:readme", "parent", "folder:docs"),
]).await?;

List Relationships

let rels = vault.relationships()
    .list()
    .resource("document:readme")
    .collect()
    .await?;

for rel in rels {
    println!("{} -> {} -> {}", rel.resource(), rel.relation(), rel.subject());
}

Filter by relation or subject:

let viewers = vault.relationships()
    .list()
    .resource("document:readme")
    .relation("viewer")
    .collect()
    .await?;

Delete a Relationship

vault.relationships()
    .delete(Relationship::new("document:readme", "viewer", "user:alice"))
    .await?;

Delete Multiple Relationships

Delete all relationships matching a filter:

vault.relationships()
    .delete_where()
    .resource("document:readme")
    .execute()
    .await?;

Filter by relation or subject:

vault.relationships()
    .delete_where()
    .resource("document:readme")
    .relation("viewer")
    .subject("user:alice")
    .execute()
    .await?;

Lookups

List Accessible Resources

Find all resources a subject can access:

let docs = vault.resources()
    .accessible_by("user:alice")
    .with_permission("view")
    .resource_type("document")
    .collect()
    .await?;

for doc in docs {
    println!("Alice can view: {}", doc);
}

List Subjects with Access

Find all subjects that have access to a resource:

let users = vault.subjects()
    .with_permission("view")
    .on_resource("document:readme")
    .collect()
    .await?;

for user in users {
    println!("{} can view document:readme", user);
}

Filter by subject type:

let groups = vault.subjects()
    .with_permission("view")
    .on_resource("document:readme")
    .subject_type("group")
    .collect()
    .await?;

Explain & Simulate

Explain a Permission Decision

Understand why access was allowed or denied:

let explanation = vault.explain_permission()
    .subject("user:alice")
    .permission("edit")
    .resource("document:readme")
    .execute()
    .await?;

println!("{}", explanation.summary());

if explanation.allowed {
    // Show the path that granted access
    for node in &explanation.path {
        println!("  via {} on {}", node.relation, node.resource);
    }
} else {
    // Show why access was denied
    println!("Denied: {:?}", explanation.reason);
}

Simulate What-If Scenarios

Test hypothetical changes without modifying data:

let result = vault.simulate()
    .add_relationship(Relationship::new("doc:1", "editor", "user:bob"))
    .check("user:bob", "edit", "doc:1")
    .await?;

if result.allowed {
    println!("Adding this relationship would grant access");
}

Simulate multiple changes:

let result = vault.simulate()
    .add_relationship(Relationship::new("folder:docs", "viewer", "user:bob"))
    .add_relationship(Relationship::new("doc:1", "parent", "folder:docs"))
    .remove_relationship(Relationship::new("doc:1", "viewer", "user:alice"))
    .check("user:bob", "view", "doc:1")
    .await?;

Watch for Changes

Stream real-time relationship changes:

use futures::StreamExt;

let mut stream = vault.watch()
    .filter(WatchFilter::resource_type("document"))
    .run()
    .await?;

while let Some(event) = stream.next().await {
    let event = event?;
    println!("{}: {} {} {}",
        event.operation, event.resource, event.relation, event.subject);
}

Filter Options

// Filter by resource type
.filter(WatchFilter::resource_type("document"))

// Filter by specific resource
.filter(WatchFilter::resource("document:readme"))

// Filter by relation
.filter(WatchFilter::relation("viewer"))

// Filter by operation type
.filter(WatchFilter::operations([Operation::Create, Operation::Delete]))

// Combine multiple filters
.filter(WatchFilter::resource_type("document"))
.filter(WatchFilter::relation("editor"))

Resumable Streams

For reliable processing, use resumable streams with revision tracking:

let mut stream = vault.watch()
    .from_revision(last_processed_revision)
    .resumable()
    .run()
    .await?;

Error Handling

Permission checks return Ok(false) for denial, not errors:

match vault.check("user:alice", "view", "doc:1").await {
    Ok(true) => println!("Access granted"),
    Ok(false) => println!("Access denied"),
    Err(e) => println!("Error: {}", e),  // Network, auth, etc.
}

Use require() when you want denial to be an error:

vault.check("user:alice", "view", "doc:1")
    .require()
    .await?;  // Returns Err(AccessDenied) if denied

See Also