redis-watcher 1.0.0

Redis watcher for Casbin-RS
Documentation

Redis Watcher

GitHub Actions Codecov Docs Crates.io crates.io Discord

Redis Watcher is a Redis watcher for Casbin-RS.

Installation

Add this to your Cargo.toml:

[dependencies]
redis-watcher = "1.0.0"
casbin = { version = "2.13.0", features = ["watcher"] }
tokio = { version = "1.0", features = ["rt-multi-thread", "macros", "time"] }
redis = { version = "0.32.6", features = ["tokio-comp", "cluster-async", "aio"] }

Note: The watcher feature is required for Casbin to enable watcher functionality. For Redis cluster support, include the cluster-async feature.

Simple Example

use redis_watcher::{RedisWatcher, WatcherOptions};
use casbin::{prelude::*, Watcher, EventData};

fn main() -> redis_watcher::Result<()> {
    // Configure watcher options
    let options = WatcherOptions::default()
        .with_channel("/casbin".to_string())
        .with_ignore_self(false);  // Set to true in production to ignore self-updates
    
    // Create watcher for standalone Redis
    let mut watcher = RedisWatcher::new("redis://127.0.0.1:6379", options)?;

    // Set callback to handle policy updates
    watcher.set_update_callback(Box::new(|msg: String| {
        println!("Policy updated: {}", msg);
        // Reload your enforcer policies here
    }));

    // The watcher automatically starts subscription when callback is set
    // Now you can use it with Casbin enforcer
    // Your enforcer will be notified when policies change

    Ok(())
}

Cluster Example

use redis_watcher::{RedisWatcher, WatcherOptions};
use casbin::{prelude::*, Watcher};

fn main() -> redis_watcher::Result<()> {
    let options = WatcherOptions::default()
        .with_channel("/casbin".to_string())
        .with_ignore_self(true);

    // Initialize watcher with Redis cluster
    // Provide comma-separated list of cluster nodes
    let mut watcher = RedisWatcher::new_cluster(
        "redis://127.0.0.1:7000,redis://127.0.0.1:7001,redis://127.0.0.1:7002",
        options
    )?;

    // Set up callback to handle policy updates
    watcher.set_update_callback(Box::new(|msg: String| {
        println!("Received policy update from cluster: {}", msg);
        // Parse message and reload enforcer policies
    }));

    // Watcher is now ready to receive cluster-wide policy updates
    
    Ok(())
}

Configuration

WatcherOptions

The WatcherOptions struct provides configuration for the Redis watcher:

use redis_watcher::WatcherOptions;

let options = WatcherOptions::default()
    .with_channel("/casbin-policy-updates".to_string())  // Redis channel name
    .with_ignore_self(true)                              // Ignore self-generated updates
    .with_local_id("unique-instance-id".to_string());    // Unique identifier for this instance

Options Explained:

  • channel: Redis pub/sub channel name for policy updates (default: "/casbin")
  • ignore_self: When true, the watcher ignores messages it published itself, preventing circular updates (default: false)
  • local_id: Unique identifier for this watcher instance, automatically generated using UUID v4 if not specified

Best Practices:

  • Set ignore_self to true in production to avoid processing your own updates
  • Use a descriptive local_id for easier debugging in multi-instance deployments
  • Choose a channel name that doesn't conflict with other Redis applications

Update Types

The watcher supports various policy update types through the UpdateType enum, which corresponds to different Casbin operations:

pub enum UpdateType {
    Update,                           // Generic update notification
    UpdateForAddPolicy,               // Single policy addition
    UpdateForRemovePolicy,            // Single policy removal
    UpdateForRemoveFilteredPolicy,    // Filtered policy removal
    UpdateForSavePolicy,              // Complete policy save
    UpdateForAddPolicies,             // Batch policy addition
    UpdateForRemovePolicies,          // Batch policy removal
    UpdateForUpdatePolicy,            // Single policy update
    UpdateForUpdatePolicies,          // Batch policy update
}

Message Structure:

Each update is published as a JSON message with the following structure:

pub struct Message {
    pub method: UpdateType,       // Type of update
    pub id: String,               // Sender's local_id
    pub sec: String,              // Policy section (e.g., "p", "g")
    pub ptype: String,            // Policy type
    pub old_rule: Vec<String>,    // Old policy rule
    pub old_rules: Vec<Vec<String>>,  // Old policy rules (batch)
    pub new_rule: Vec<String>,    // New policy rule
    pub new_rules: Vec<Vec<String>>,  // New policy rules (batch)
    pub field_index: i32,         // Field index for filtered operations
    pub field_values: Vec<String>, // Field values for filtered operations
}

Integration with Casbin:

The watcher automatically converts Casbin's EventData to these message types when you call watcher.update(event_data). This ensures consistent synchronization across all instances.

Getting Help

Documentation

License

This project is under Apache 2.0 License. See the LICENSE file for the full license text.