inferadb 0.1.5

Official Rust SDK for InferaDB
Documentation
# 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

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

## Permission Checks

### Basic Check

```rust
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:

```rust
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):

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

### Batch Checks

Check multiple permissions efficiently in a single request:

```rust
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:

```rust
// 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

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

### Write Multiple Relationships

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

### List Relationships

```rust
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:

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

### Delete a Relationship

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

### Delete Multiple Relationships

Delete all relationships matching a filter:

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

Filter by relation or subject:

```rust
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:

```rust
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:

```rust
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:

```rust
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:

```rust
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:

```rust
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:

```rust
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:

```rust
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

```rust
// 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:

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

## Error Handling

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

```rust
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:

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

## See Also

- [Error Handling]errors.md - Detailed error handling patterns
- [Testing]testing.md - MockClient and InMemoryClient for testing
- [Consistency]consistency.md - Consistency tokens and guarantees
- [Integration Patterns]integration-patterns.md - Framework middleware examples