Redis Watcher is a Redis watcher for Casbin-RS.
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.
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(()) }
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(()) }
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 specifiedBest Practices:
ignore_self to true in production to avoid processing your own updateslocal_id for easier debugging in multi-instance deploymentsThe 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.
This project is under Apache 2.0 License. See the LICENSE file for the full license text.