#macro #rbac #authorization #library

rbacrab

Rust πŸ¦€RBACπŸ¦€ library with some crabbyπŸ¦€πŸ§™ macro magic! Blazingly πŸš€πŸš€πŸš€ fast

3 releases

Uses new Rust 2024

0.0.4 Apr 1, 2026
0.0.3 Mar 1, 2026
0.0.2 Jan 14, 2026

#273 in Security

MIT license

3MB
764 lines

RBACrab

Rust πŸ¦€RBACπŸ¦€ micro library with some crabbyπŸ¦€πŸ§™ macro magic! Blazingly πŸš€πŸš€πŸš€ fast!

RBACrab

Library intended to be lightweight and simple as possible.

Type-safe, zero-allocation RBAC for Rust. 2 dependencies. ~800 lines.

Why RBACrab

  • Compile-time permission safety. The define_permissions! macro generates typed enums β€” Orders::Invoice::Read, not "orders.invoice.read". Typos are caught by the compiler, not by a 3am production alert.
  • Zero-allocation permission checks. has_permission() does pure &str hash lookups. No String construction on the hot path. tens of nanoseconds per check.
  • Roles are data, not code. Roles are serializable (serde) β€” store them in a database, config file, or external service. Permissions are code. This separation means you can change who can do what without redeploying.
  • Lock-free runtime updates. Swap the entire role set atomically via arc-swap. Readers never block. Zero downtime role reloads.
  • Composition over inheritance. Users hold multiple roles β€” the permission check is a flat union. No inheritance chains, no diamond problem, no cascading side effects. The Rust way.
  • Thread-safe by design. RbacService is Send + Sync. Works in any async runtime.

Permission Hierarchy

Three levels: Domain β†’ Object β†’ Action

Users::User::Read       β€” single permission
Users::User::*          β€” all actions on User
Users::*                β€” all objects and actions in Users domain
*                       β€” everything

Roles specify permissions as strings with wildcards and action sets:

Role::new("OrderManager", vec![
    "Orders::Order::*".to_string(),                    // all Order actions
    "Orders::Invoice::{Read,Generate}".to_string(),    // specific Invoice actions
])

Basic usage example:

use rbacrab::*;

define_permissions! {
    // Orders domain - manages orders, items, and invoices
    pub domain Orders {
        // Order operations
        Order {
            Read => "View orders",
            Create => "Create orders",
            Update => "Update orders",
            Cancel => "Cancel orders",
        },
        // Order item operations
        OrderItem {
            Read => "View order items",
            Add => "Add items to order",
            Remove => "Remove items from order",
        },
        // Invoice operations
        Invoice {
            Read => "View invoices",
            Generate => "Generate invoices",
            Send => "Send invoices to customers",
        },
    }
}

struct User {
    name: String,
    roles: Vec<String>,
}

impl RbacSubject for User {
    fn get_roles(&self) -> &Vec<String> {
        &self.roles
    }
    fn name(&self) -> &str {
        &self.name
    }
}

fn test_rbac() {
   let rbac_service = RbacService::builder()
   .add_role(Role::new(
       "OrderManager",
       vec![
           "Orders::Order::*".to_string(),
           "Orders::OrderItem::*".to_string(),
           "Orders::Invoice::{Read,Generate}".to_string(),
       ],
   ))
   .add_role(Role::new(
       "Admin",
       vec!["*".to_string()],
   ))
    .build();

   let user = User {
        name: "user".to_string(),
        roles: vec!["OrderManager".to_string()]
    };

   let admin = User {
        name: "admin".to_string(),
        roles: vec!["Admin".to_string()]
    };

   assert!(rbac_service.has_permission(&user, Orders::Order::Update).is_ok());
   assert!(rbac_service.has_permission(&user, Orders::Invoice::Send).is_err());
   assert!(rbac_service.has_permission(&admin, Orders::Invoice::Send).is_ok());

    // Runtime update RBAC service roles with new set of roles:
    
    // Get clean updater (in case if old roles needed, use .updater_copy())
    let mut updater = rbac_service.updater_clean();
 
    updater.add_role(Role::new(
        "OrderManager",
        vec![
            "Orders::Order::*".to_string(),
            "Orders::OrderItem::*".to_string(),
            "Orders::Invoice::{Read,Generate,Send}".to_string(),
        ],
    ));
 
    // Swap roles inside service (atomicly)
    updater.update(&rbac_service);
 
    assert!(rbac_service.has_permission(&user, Orders::Invoice::Send).is_ok());

}

test_rbac();

Dependencies