1 unstable release
0.1.0 | Feb 27, 2025 |
---|
#562 in Authentication
173 downloads per month
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