rbac using tonic

This commit is contained in:
filipriec
2025-03-25 11:33:14 +01:00
parent 507f86fcf1
commit 34dafcc23e
2 changed files with 104 additions and 0 deletions

View File

@@ -2,6 +2,8 @@
pub mod jwt;
pub mod middleware;
pub mod rbac;
pub use jwt::*;
pub use middleware::*;
pub use rbac::*;

View File

@@ -0,0 +1,102 @@
// src/auth/logic/rbac.rs
use tonic::{Request, Status};
use crate::auth::logic::jwt;
// Define roles constants to match database constraints
pub const ROLE_VIEWER: &str = "viewer";
pub const ROLE_ACCOUNTANT: &str = "accountant";
pub const ROLE_MODERATOR: &str = "moderator";
pub const ROLE_ADMIN: &str = "admin";
// Role-check interceptor functions
pub fn require_roles<T>(roles: Vec<String>, req: Request<T>) -> Result<Request<T>, Status> {
let token = match req.metadata().get("authorization") {
Some(token) => match token.to_str() {
Ok(t) => t,
Err(_) => return Err(Status::unauthenticated("Invalid authorization header")),
},
None => return Err(Status::unauthenticated("Missing authorization header")),
};
// Strip 'Bearer ' prefix if present
let token = token.strip_prefix("Bearer ")
.unwrap_or(token);
// Validate the token and get claims
let claims = match jwt::validate_token(token) {
Ok(claims) => claims,
Err(_) => return Err(Status::unauthenticated("Invalid token")),
};
// Check if user role is allowed
if !roles.contains(&claims.role) {
tracing::warn!(
user_id = %claims.sub,
user_role = %claims.role,
required_roles = ?roles,
"Access denied: insufficient permissions"
);
return Err(Status::permission_denied("Insufficient permissions"));
}
// Add claims to request extensions for handler access
let mut req = req;
req.extensions_mut().insert(claims);
Ok(req)
}
// Convenience function for admin-only endpoints
pub fn admin_only<T>(req: Request<T>) -> Result<Request<T>, Status> {
require_roles(vec![ROLE_ADMIN.to_string()], req)
}
// Convenience function for admin and moderator endpoints
pub fn admin_moderator<T>(req: Request<T>) -> Result<Request<T>, Status> {
require_roles(
vec![
ROLE_MODERATOR.to_string(),
ROLE_ADMIN.to_string()
],
req
)
}
// Convenience function for accountants and higher (more privileges)
pub fn accountant_and_higher<T>(req: Request<T>) -> Result<Request<T>, Status> {
require_roles(
vec![
ROLE_ACCOUNTANT.to_string(),
ROLE_MODERATOR.to_string(),
ROLE_ADMIN.to_string()
],
req
)
}
// Convenience function for any authenticated user
pub fn any_authenticated_user<T>(req: Request<T>) -> Result<Request<T>, Status> {
require_roles(
vec![
ROLE_VIEWER.to_string(),
ROLE_ACCOUNTANT.to_string(),
ROLE_MODERATOR.to_string(),
ROLE_ADMIN.to_string()
],
req
)
}
// Helper to get a user's role from the request
pub fn get_user_role<T>(req: &Request<T>) -> Option<&str> {
req.extensions()
.get::<jwt::Claims>()
.map(|claims| claims.role.as_str())
}
// Helper to get a user's ID from the request
pub fn get_user_id<T>(req: &Request<T>) -> Option<uuid::Uuid> {
req.extensions()
.get::<jwt::Claims>()
.map(|claims| claims.sub)
}