rbac using tonic
This commit is contained in:
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
pub mod jwt;
|
pub mod jwt;
|
||||||
pub mod middleware;
|
pub mod middleware;
|
||||||
|
pub mod rbac;
|
||||||
|
|
||||||
pub use jwt::*;
|
pub use jwt::*;
|
||||||
pub use middleware::*;
|
pub use middleware::*;
|
||||||
|
pub use rbac::*;
|
||||||
|
|||||||
102
server/src/auth/logic/rbac.rs
Normal file
102
server/src/auth/logic/rbac.rs
Normal 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)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user