#role #permissions #rbac #security #authorization

rust-rbac

A flexible Role-Based Access Control (RBAC) system for Rust applications

1 unstable release

0.1.0 Feb 27, 2025

#562 in Authentication

Download history 149/week @ 2025-02-25 22/week @ 2025-03-04 2/week @ 2025-03-11

173 downloads per month

MIT license

43KB
635 lines

Rust RBAC

A flexible Role-Based Access Control (RBAC) system for Rust applications, inspired by Spatie Laravel Permissions.

Features

  • Role-based permissions
  • Direct permissions to users
  • Multiple roles per user
  • Multiple permissions per role
  • Permission inheritance through roles
  • Flexible storage backends
  • Framework-agnostic core with middleware for popular web frameworks

Installation

Add this to your Cargo.toml:

[dependencies]
rust-rbac = "0.1.0"

Usage

Basic Example

use rust_rbac::{RbacService, MemoryStorage, Permission, Role};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Create a new RBAC service with in-memory storage
    let storage = MemoryStorage::new();
    let rbac = RbacService::new(storage);

    // Create permissions
    let create_post = Permission::new("create-post");
    let edit_post = Permission::new("edit-post");
    let delete_post = Permission::new("delete-post");

    rbac.create_permission(&create_post).await?;
    rbac.create_permission(&edit_post).await?;
    rbac.create_permission(&delete_post).await?;

    // Create roles
    let author = Role::new("author");
    let editor = Role::new("editor");
    let admin = Role::new("admin");

    rbac.create_role(&author).await?;
    rbac.create_role(&editor).await?;
    rbac.create_role(&admin).await?;

    // Assign permissions to roles
    rbac.assign_permission_to_role("create-post", "author").await?;
    rbac.assign_permission_to_role("edit-post", "editor").await?;
    rbac.assign_permission_to_role("delete-post", "admin").await?;

    // Assign roles to users
    let user_id = "user123";
    rbac.assign_role_to_subject("author", user_id).await?;

    // Check permissions
    assert!(rbac.subject_has_permission(user_id, "create-post").await?);
    assert!(!rbac.subject_has_permission(user_id, "delete-post").await?);

    Ok(())
}

Integrating with User Models

To integrate with your user models, implement the RbacSubject trait:

use async_trait::async_trait;
use rust_rbac::{RbacError, RbacService, RbacSubject};
use uuid::Uuid;

struct User {
    id: Uuid,
    name: String,
    // other fields...
}

#[async_trait]
impl RbacSubject for User {
    fn get_id(&self) -> String {
        self.id.to_string()
    }

    async fn has_permission(&self, permission: &str) -> Result<bool, RbacError> {
        // Use the RBAC service to check permissions
        let rbac = get_rbac_service(); // Get your RBAC service instance
        rbac.subject_has_permission(&self.get_id(), permission).await
    }

    async fn has_role(&self, role: &str) -> Result<bool, RbacError> {
        let rbac = get_rbac_service();
        let roles = rbac.get_roles_for_subject(&self.get_id()).await?;
        Ok(roles.iter().any(|r| r.name == role))
    }

    async fn get_permissions(&self) -> Result<Vec<String>, RbacError> {
        let rbac = get_rbac_service();
        let direct = rbac.get_direct_permissions_for_subject(&self.get_id()).await?;
        let roles = rbac.get_roles_for_subject(&self.get_id()).await?;
        
        let mut permissions = direct.iter().map(|p| p.name.clone()).collect::<Vec<_>>();
        
        for role in roles {
            let role_permissions = rbac.get_permissions_for_role(&role.name).await?;
            permissions.extend(role_permissions.iter().map(|p| p.name.clone()));
        }
        
        Ok(permissions)
    }

    async fn get_roles(&self) -> Result<Vec<String>, RbacError> {
        let rbac = get_rbac_service();
        let roles = rbac.get_roles_for_subject(&self.get_id()).await?;
        Ok(roles.iter().map(|r| r.name.clone()).collect())
    }
}

Using with Web Frameworks

Actix Web

use actix_web::{web, App, HttpServer};
use rust_rbac::middleware::actix::RequirePermission;

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    // Set up RBAC service
    let rbac_service = setup_rbac_service();
    
    HttpServer::new(move || {
        App::new()
            .app_data(web::Data::new(rbac_service.clone()))
            .service(
                web::scope("/api")
                    .service(
                        web::resource("/posts")
                            .wrap(RequirePermission::new("create-post"))
                            .route(web::post().to(create_post))
                    )
                    .service(
                        web::resource("/posts/{id}")
                            .wrap(RequirePermission::new("edit-post"))
                            .route(web::put().to(update_post))
                    )
                    .service(
                        web::resource("/admin")
                            .wrap(RequirePermission::new("view-dashboard"))
                            .route(web::get().to(admin_dashboard))
                    )
            )
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

Storage Backends

The crate supports multiple storage backends:

  • In-memory storage (default)
  • PostgreSQL (with the postgres feature)
  • MySQL (with the mysql feature)
  • SQLite (with the sqlite feature)

Using PostgreSQL

[dependencies]
rust-rbac = { version = "0.1.0", features = ["postgres"] }
use rust_rbac::{RbacService, storage::sqlx::PostgresStorage};

async fn setup() -> Result<(), Box<dyn std::error::Error>> {
    let pool = sqlx::PgPool::connect("postgres://user:password@localhost/dbname").await?;
    let storage = PostgresStorage::new(pool);
    let rbac = RbacService::new(storage);
    
    // Use RBAC service...
    
    Ok(())
}

License

This project is licensed under the MIT License - see the LICENSE file for details.

Dependencies

~2–13MB
~160K SLoC