jean-marie/backend/src/rbac.rs

48 lines
1.3 KiB
Rust

// src/rbac.rs
use uuid::Uuid;
#[derive (Clone)]
pub struct RbacService {
pool: sqlx::PgPool,
}
impl RbacService {
pub fn new(pool: sqlx::PgPool) -> Self {
Self { pool }
}
pub async fn has_permission(&self, user_id: Uuid, resource: &str) -> bool {
let result: Result<Vec<String>, _> = sqlx::query_scalar(
r#"
SELECT rp.item FROM roles r
INNER JOIN role_permissions rp ON r.id = rp.role_id
INNER JOIN user_roles ur ON r.id = ur.role_id
WHERE ur.user_id = $1
"#,
)
.bind(user_id)
.fetch_all(&self.pool)
.await;
match result {
Ok(patterns) => patterns.iter()
.any(|pattern| permission_matches(pattern, resource)),
Err(_) => false,
}
}
}
/// Wildcard permission matching (e.g., "article:edit:*" matches "article:edit:123")
fn permission_matches(pattern: &str, resource: &str) -> bool {
let pattern_segments: Vec<&str> = pattern.split(':').collect();
let resource_segments: Vec<&str> = resource.split(':').collect();
if pattern_segments.len() != resource_segments.len() {
return false;
}
pattern_segments.iter()
.zip(resource_segments.iter())
.all(|(p, r)| p == r || *p == "*")
}