feat(rbac): implement complete RBAC middleware and route permissions

- Add rbac_middleware_fn for use in middleware layer chain
- Add RBAC middleware to request processing pipeline (after auth)
- Complete route permissions for ALL apps:
  - Anonymous: health, i18n, product, auth/login, chat, websocket
  - Authenticated users: drive, mail, calendar, tasks, docs, paper, sheet,
    slides, meet, research, sources, canvas, video, player, workspaces,
    projects, goals, settings, bots (read), designer, dashboards, crm,
    contacts, billing, products, tickets, learn, social, llm, autotask
  - Admin/SuperAdmin: users, groups, bot management, analytics, monitoring,
    audit, security, admin panel, attendant
  - SuperAdmin only: RBAC management
- Add all /api/ui/** HTMX routes with proper permissions
- Chat remains anonymous for customer support functionality
This commit is contained in:
Rodrigo Rodriguez (Pragmatismo) 2026-01-10 11:41:25 -03:00
parent 81b8fd8f2d
commit b4647cd8d2
3 changed files with 398 additions and 33 deletions

View file

@ -227,6 +227,8 @@ async fn run_axum_server(
let cors = create_cors_layer(); let cors = create_cors_layer();
// Create auth config for protected routes // Create auth config for protected routes
// Session-based auth from Zitadel uses session tokens (not JWTs)
// The auth middleware in auth.rs handles both JWT and session token validation
let auth_config = Arc::new(AuthConfig::from_env() let auth_config = Arc::new(AuthConfig::from_env()
.add_anonymous_path("/health") .add_anonymous_path("/health")
.add_anonymous_path("/healthz") .add_anonymous_path("/healthz")
@ -488,6 +490,9 @@ async fn run_axum_server(
base_router base_router
}; };
// Clone rbac_manager for use in middleware
let rbac_manager_for_middleware = Arc::clone(&rbac_manager);
let app = app_with_ui let app = app_with_ui
// Security middleware stack (order matters - first added is outermost) // Security middleware stack (order matters - first added is outermost)
.layer(middleware::from_fn(security_headers_middleware)) .layer(middleware::from_fn(security_headers_middleware))
@ -495,6 +500,13 @@ async fn run_axum_server(
.layer(rate_limit_extension) .layer(rate_limit_extension)
// Request ID tracking for all requests // Request ID tracking for all requests
.layer(middleware::from_fn(request_id_middleware)) .layer(middleware::from_fn(request_id_middleware))
// RBAC middleware - checks permissions AFTER authentication
.layer(middleware::from_fn(move |req: axum::http::Request<axum::body::Body>, next: axum::middleware::Next| {
let rbac = Arc::clone(&rbac_manager_for_middleware);
async move {
botserver::security::rbac_middleware_fn(req, next, rbac).await
}
}))
// Authentication middleware using provider registry // Authentication middleware using provider registry
.layer(middleware::from_fn(move |req: axum::http::Request<axum::body::Body>, next: axum::middleware::Next| { .layer(middleware::from_fn(move |req: axum::http::Request<axum::body::Body>, next: axum::middleware::Next| {
let state = auth_middleware_state.clone(); let state = auth_middleware_state.clone();

View file

@ -79,7 +79,7 @@ pub use rbac_middleware::{
AccessDecision, AccessDecisionResult, RbacConfig, RbacError, RbacManager, RbacMiddlewareState, AccessDecision, AccessDecisionResult, RbacConfig, RbacError, RbacManager, RbacMiddlewareState,
RequirePermission, RequireResourceAccess, RequireRole, ResourceAcl, ResourcePermission, RequirePermission, RequireResourceAccess, RequireRole, ResourceAcl, ResourcePermission,
RoutePermission, build_default_route_permissions, create_admin_layer, create_permission_layer, RoutePermission, build_default_route_permissions, create_admin_layer, create_permission_layer,
create_role_layer, rbac_middleware, require_admin_middleware, require_super_admin_middleware, create_role_layer, rbac_middleware, rbac_middleware_fn, require_admin_middleware, require_super_admin_middleware,
}; };
pub use session::{ pub use session::{
DeviceInfo, InMemorySessionStore, SameSite, Session, SessionConfig, SessionManager, DeviceInfo, InMemorySessionStore, SameSite, Session, SessionConfig, SessionManager,

View file

@ -619,6 +619,56 @@ impl RbacManager {
} }
} }
/// RBAC middleware function for use with middleware::from_fn
/// This version takes the RbacManager as a parameter instead of State
pub async fn rbac_middleware_fn(
request: Request<Body>,
next: Next,
rbac: Arc<RbacManager>,
) -> Response {
let path = request.uri().path().to_string();
let method = request.method().to_string();
let user = request
.extensions()
.get::<AuthenticatedUser>()
.cloned()
.unwrap_or_else(AuthenticatedUser::anonymous);
let decision = rbac.check_route_access(&path, &method, &user).await;
if rbac.config.audit_all_decisions {
debug!(
"RBAC decision for {} {} by user {}: {:?} - {}",
method, path, user.user_id, decision.decision, decision.reason
);
}
if !decision.is_allowed() {
if !user.is_authenticated() {
return (
StatusCode::UNAUTHORIZED,
Json(serde_json::json!({
"error": "unauthorized",
"message": "Authentication required"
})),
)
.into_response();
}
return (
StatusCode::FORBIDDEN,
Json(serde_json::json!({
"error": "forbidden",
"message": decision.reason
})),
)
.into_response();
}
next.run(request).await
}
pub async fn rbac_middleware( pub async fn rbac_middleware(
State(rbac): State<Arc<RbacManager>>, State(rbac): State<Arc<RbacManager>>,
request: Request<Body>, request: Request<Body>,
@ -877,57 +927,360 @@ pub fn create_admin_layer(rbac_manager: Arc<RbacManager>) -> RbacMiddlewareState
pub fn build_default_route_permissions() -> Vec<RoutePermission> { pub fn build_default_route_permissions() -> Vec<RoutePermission> {
vec![ vec![
// =====================================================================
// PUBLIC / ANONYMOUS ROUTES (no auth required)
// =====================================================================
RoutePermission::new("/health", "GET", "").with_anonymous(true),
RoutePermission::new("/healthz", "GET", "").with_anonymous(true),
RoutePermission::new("/api/health", "GET", "").with_anonymous(true), RoutePermission::new("/api/health", "GET", "").with_anonymous(true),
RoutePermission::new("/api/version", "GET", "").with_anonymous(true), RoutePermission::new("/api/version", "GET", "").with_anonymous(true),
RoutePermission::new("/api/product", "GET", "").with_anonymous(true), RoutePermission::new("/api/product", "GET", "").with_anonymous(true),
RoutePermission::new("/api/i18n/**", "GET", "").with_anonymous(true), RoutePermission::new("/api/i18n/**", "GET", "").with_anonymous(true),
// Auth routes - login must be anonymous
RoutePermission::new("/api/auth", "GET", "").with_anonymous(true), RoutePermission::new("/api/auth", "GET", "").with_anonymous(true),
RoutePermission::new("/api/auth/login", "POST", "").with_anonymous(true), RoutePermission::new("/api/auth/login", "POST", "").with_anonymous(true),
RoutePermission::new("/api/auth/bootstrap", "POST", "").with_anonymous(true),
RoutePermission::new("/api/auth/refresh", "POST", "").with_anonymous(true),
RoutePermission::new("/api/auth/logout", "POST", ""),
RoutePermission::new("/api/auth/me", "GET", ""), RoutePermission::new("/api/auth/me", "GET", ""),
RoutePermission::new("/api/users", "GET", "users.read") RoutePermission::new("/api/auth/**", "GET", ""),
.with_roles(vec!["Admin".into(), "SuperAdmin".into()]), RoutePermission::new("/api/auth/**", "POST", ""),
RoutePermission::new("/api/users", "POST", "users.create")
.with_roles(vec!["Admin".into(), "SuperAdmin".into()]), // WebSocket - anonymous for chat support
RoutePermission::new("/api/users/:id", "GET", "users.read"), RoutePermission::new("/ws", "GET", "").with_anonymous(true),
RoutePermission::new("/api/users/:id", "PUT", "users.update"), RoutePermission::new("/ws/**", "GET", "").with_anonymous(true),
RoutePermission::new("/api/users/:id", "DELETE", "users.delete")
.with_roles(vec!["Admin".into(), "SuperAdmin".into()]), // Chat - ANONYMOUS for customer support
RoutePermission::new("/api/drive/**", "GET", "drive.read"), RoutePermission::new("/api/chat/**", "GET", "").with_anonymous(true),
RoutePermission::new("/api/drive/upload", "POST", "drive.write"), RoutePermission::new("/api/chat/**", "POST", "").with_anonymous(true),
RoutePermission::new("/api/drive/:id", "DELETE", "drive.delete"),
RoutePermission::new("/api/mail/**", "GET", "mail.read"), // Sessions - anonymous can create sessions for chat
RoutePermission::new("/api/mail/send", "POST", "mail.send"), RoutePermission::new("/api/sessions", "POST", "").with_anonymous(true),
RoutePermission::new("/api/calendar/**", "GET", "calendar.read"), RoutePermission::new("/api/sessions", "GET", ""),
RoutePermission::new("/api/calendar/events", "POST", "calendar.write"), RoutePermission::new("/api/sessions/**", "GET", ""),
RoutePermission::new("/api/tasks/**", "GET", "tasks.read"),
RoutePermission::new("/api/tasks", "POST", "tasks.write"), // =====================================================================
RoutePermission::new("/api/analytics/**", "GET", "analytics.read") // AUTHENTICATED USER ROUTES (any logged-in user)
.with_roles(vec!["Admin".into(), "SuperAdmin".into(), "Moderator".into()]), // =====================================================================
RoutePermission::new("/api/audit/**", "GET", "audit.read")
.with_roles(vec!["Admin".into(), "SuperAdmin".into()]), // Drive / Files
RoutePermission::new("/api/rbac/**", "*", "roles.assign") RoutePermission::new("/api/drive/**", "GET", ""),
.with_roles(vec!["SuperAdmin".into()]), RoutePermission::new("/api/drive/**", "POST", ""),
RoutePermission::new("/api/monitoring/**", "GET", "monitoring.read") RoutePermission::new("/api/drive/**", "PUT", ""),
.with_roles(vec!["Admin".into(), "SuperAdmin".into()]), RoutePermission::new("/api/drive/**", "DELETE", ""),
RoutePermission::new("/api/bots", "POST", "bots.create"), RoutePermission::new("/api/files/**", "GET", ""),
RoutePermission::new("/api/bots/:id", "DELETE", "bots.delete"), RoutePermission::new("/api/files/**", "POST", ""),
RoutePermission::new("/api/bots/:id/**", "GET", "bots.read"), RoutePermission::new("/api/files/**", "PUT", ""),
RoutePermission::new("/api/bots/:id/**", "PUT", "bots.update"), RoutePermission::new("/api/files/**", "DELETE", ""),
// UI routes (HTMX endpoints) - allow authenticated users
// Mail
RoutePermission::new("/api/mail/**", "GET", ""),
RoutePermission::new("/api/mail/**", "POST", ""),
RoutePermission::new("/api/mail/**", "PUT", ""),
RoutePermission::new("/api/mail/**", "DELETE", ""),
// Calendar
RoutePermission::new("/api/calendar/**", "GET", ""),
RoutePermission::new("/api/calendar/**", "POST", ""),
RoutePermission::new("/api/calendar/**", "PUT", ""),
RoutePermission::new("/api/calendar/**", "DELETE", ""),
// Tasks
RoutePermission::new("/api/tasks/**", "GET", ""),
RoutePermission::new("/api/tasks/**", "POST", ""),
RoutePermission::new("/api/tasks/**", "PUT", ""),
RoutePermission::new("/api/tasks/**", "PATCH", ""),
RoutePermission::new("/api/tasks/**", "DELETE", ""),
// Docs / Paper
RoutePermission::new("/api/docs/**", "GET", ""),
RoutePermission::new("/api/docs/**", "POST", ""),
RoutePermission::new("/api/docs/**", "PUT", ""),
RoutePermission::new("/api/docs/**", "DELETE", ""),
RoutePermission::new("/api/paper/**", "GET", ""),
RoutePermission::new("/api/paper/**", "POST", ""),
RoutePermission::new("/api/paper/**", "PUT", ""),
RoutePermission::new("/api/paper/**", "DELETE", ""),
// Sheet
RoutePermission::new("/api/sheet/**", "GET", ""),
RoutePermission::new("/api/sheet/**", "POST", ""),
RoutePermission::new("/api/sheet/**", "PUT", ""),
RoutePermission::new("/api/sheet/**", "DELETE", ""),
// Slides
RoutePermission::new("/api/slides/**", "GET", ""),
RoutePermission::new("/api/slides/**", "POST", ""),
RoutePermission::new("/api/slides/**", "PUT", ""),
RoutePermission::new("/api/slides/**", "DELETE", ""),
// Meet
RoutePermission::new("/api/meet/**", "GET", ""),
RoutePermission::new("/api/meet/**", "POST", ""),
RoutePermission::new("/api/meet/**", "PUT", ""),
RoutePermission::new("/api/meet/**", "DELETE", ""),
// Research
RoutePermission::new("/api/research/**", "GET", ""),
RoutePermission::new("/api/research/**", "POST", ""),
RoutePermission::new("/api/research/**", "PUT", ""),
RoutePermission::new("/api/research/**", "DELETE", ""),
// Sources
RoutePermission::new("/api/sources/**", "GET", ""),
RoutePermission::new("/api/sources/**", "POST", ""),
RoutePermission::new("/api/sources/**", "PUT", ""),
RoutePermission::new("/api/sources/**", "DELETE", ""),
// Canvas
RoutePermission::new("/api/canvas/**", "GET", ""),
RoutePermission::new("/api/canvas/**", "POST", ""),
RoutePermission::new("/api/canvas/**", "PUT", ""),
RoutePermission::new("/api/canvas/**", "DELETE", ""),
// Video / Player
RoutePermission::new("/api/video/**", "GET", ""),
RoutePermission::new("/api/video/**", "POST", ""),
RoutePermission::new("/api/player/**", "GET", ""),
RoutePermission::new("/api/player/**", "POST", ""),
// Workspaces
RoutePermission::new("/api/workspaces/**", "GET", ""),
RoutePermission::new("/api/workspaces/**", "POST", ""),
RoutePermission::new("/api/workspaces/**", "PUT", ""),
RoutePermission::new("/api/workspaces/**", "DELETE", ""),
// Projects
RoutePermission::new("/api/projects/**", "GET", ""),
RoutePermission::new("/api/projects/**", "POST", ""),
RoutePermission::new("/api/projects/**", "PUT", ""),
RoutePermission::new("/api/projects/**", "DELETE", ""),
// Goals
RoutePermission::new("/api/goals/**", "GET", ""),
RoutePermission::new("/api/goals/**", "POST", ""),
RoutePermission::new("/api/goals/**", "PUT", ""),
RoutePermission::new("/api/goals/**", "DELETE", ""),
// Settings (user's own settings)
RoutePermission::new("/api/settings/**", "GET", ""),
RoutePermission::new("/api/settings/**", "POST", ""),
RoutePermission::new("/api/settings/**", "PUT", ""),
// Bots (read for all authenticated users)
RoutePermission::new("/api/bots", "GET", ""),
RoutePermission::new("/api/bots/:id", "GET", ""),
RoutePermission::new("/api/bots/:id/**", "GET", ""),
// Autotask
RoutePermission::new("/api/autotask/**", "GET", ""),
RoutePermission::new("/api/autotask/**", "POST", ""),
RoutePermission::new("/api/autotask/**", "PUT", ""),
RoutePermission::new("/api/autotask/**", "DELETE", ""),
// Designer
RoutePermission::new("/api/designer/**", "GET", ""),
RoutePermission::new("/api/designer/**", "POST", ""),
RoutePermission::new("/api/designer/**", "PUT", ""),
RoutePermission::new("/api/designer/**", "DELETE", ""),
// Dashboards
RoutePermission::new("/api/dashboards/**", "GET", ""),
RoutePermission::new("/api/dashboards/**", "POST", ""),
RoutePermission::new("/api/dashboards/**", "PUT", ""),
RoutePermission::new("/api/dashboards/**", "DELETE", ""),
// DB/Table access
RoutePermission::new("/api/db/**", "GET", ""),
RoutePermission::new("/api/db/**", "POST", ""),
RoutePermission::new("/api/db/**", "PUT", ""),
RoutePermission::new("/api/db/**", "DELETE", ""),
// CRM / Contacts
RoutePermission::new("/api/crm/**", "GET", ""),
RoutePermission::new("/api/crm/**", "POST", ""),
RoutePermission::new("/api/crm/**", "PUT", ""),
RoutePermission::new("/api/crm/**", "DELETE", ""),
RoutePermission::new("/api/contacts/**", "GET", ""),
RoutePermission::new("/api/contacts/**", "POST", ""),
RoutePermission::new("/api/contacts/**", "PUT", ""),
RoutePermission::new("/api/contacts/**", "DELETE", ""),
// Billing / Products
RoutePermission::new("/api/billing/**", "GET", ""),
RoutePermission::new("/api/billing/**", "POST", ""),
RoutePermission::new("/api/products/**", "GET", ""),
RoutePermission::new("/api/products/**", "POST", ""),
RoutePermission::new("/api/products/**", "PUT", ""),
RoutePermission::new("/api/products/**", "DELETE", ""),
// Tickets
RoutePermission::new("/api/tickets/**", "GET", ""),
RoutePermission::new("/api/tickets/**", "POST", ""),
RoutePermission::new("/api/tickets/**", "PUT", ""),
RoutePermission::new("/api/tickets/**", "DELETE", ""),
// Learn
RoutePermission::new("/api/learn/**", "GET", ""),
RoutePermission::new("/api/learn/**", "POST", ""),
// Social
RoutePermission::new("/api/social/**", "GET", ""),
RoutePermission::new("/api/social/**", "POST", ""),
// LLM
RoutePermission::new("/api/llm/**", "GET", ""),
RoutePermission::new("/api/llm/**", "POST", ""),
// =====================================================================
// UI ROUTES (HTMX endpoints) - authenticated users
// =====================================================================
RoutePermission::new("/api/ui/tasks/**", "GET", ""), RoutePermission::new("/api/ui/tasks/**", "GET", ""),
RoutePermission::new("/api/ui/tasks/**", "POST", ""), RoutePermission::new("/api/ui/tasks/**", "POST", ""),
RoutePermission::new("/api/ui/tasks/**", "PUT", ""), RoutePermission::new("/api/ui/tasks/**", "PUT", ""),
RoutePermission::new("/api/ui/tasks/**", "PATCH", ""), RoutePermission::new("/api/ui/tasks/**", "PATCH", ""),
RoutePermission::new("/api/ui/tasks/**", "DELETE", ""), RoutePermission::new("/api/ui/tasks/**", "DELETE", ""),
RoutePermission::new("/api/ui/calendar/**", "GET", ""), RoutePermission::new("/api/ui/calendar/**", "GET", ""),
RoutePermission::new("/api/ui/calendar/**", "POST", ""),
RoutePermission::new("/api/ui/drive/**", "GET", ""), RoutePermission::new("/api/ui/drive/**", "GET", ""),
RoutePermission::new("/api/ui/drive/**", "POST", ""),
RoutePermission::new("/api/ui/mail/**", "GET", ""), RoutePermission::new("/api/ui/mail/**", "GET", ""),
RoutePermission::new("/api/ui/monitoring/**", "GET", ""), RoutePermission::new("/api/ui/mail/**", "POST", ""),
RoutePermission::new("/api/ui/docs/**", "GET", ""),
RoutePermission::new("/api/ui/docs/**", "POST", ""),
RoutePermission::new("/api/ui/paper/**", "GET", ""),
RoutePermission::new("/api/ui/paper/**", "POST", ""),
RoutePermission::new("/api/ui/sheet/**", "GET", ""),
RoutePermission::new("/api/ui/sheet/**", "POST", ""),
RoutePermission::new("/api/ui/slides/**", "GET", ""),
RoutePermission::new("/api/ui/slides/**", "POST", ""),
RoutePermission::new("/api/ui/meet/**", "GET", ""),
RoutePermission::new("/api/ui/meet/**", "POST", ""),
RoutePermission::new("/api/ui/research/**", "GET", ""),
RoutePermission::new("/api/ui/research/**", "POST", ""),
RoutePermission::new("/api/ui/sources/**", "GET", ""),
RoutePermission::new("/api/ui/sources/**", "POST", ""),
RoutePermission::new("/api/ui/canvas/**", "GET", ""),
RoutePermission::new("/api/ui/video/**", "GET", ""),
RoutePermission::new("/api/ui/player/**", "GET", ""),
RoutePermission::new("/api/ui/workspaces/**", "GET", ""),
RoutePermission::new("/api/ui/projects/**", "GET", ""),
RoutePermission::new("/api/ui/goals/**", "GET", ""),
RoutePermission::new("/api/ui/designer/**", "GET", ""),
RoutePermission::new("/api/ui/dashboards/**", "GET", ""),
RoutePermission::new("/api/ui/crm/**", "GET", ""),
RoutePermission::new("/api/ui/billing/**", "GET", ""),
RoutePermission::new("/api/ui/products/**", "GET", ""),
RoutePermission::new("/api/ui/tickets/**", "GET", ""),
RoutePermission::new("/api/ui/learn/**", "GET", ""),
RoutePermission::new("/api/ui/social/**", "GET", ""),
RoutePermission::new("/api/ui/settings/**", "GET", ""),
RoutePermission::new("/api/ui/autotask/**", "GET", ""),
// =====================================================================
// ADMIN ROUTES (requires Admin or SuperAdmin role)
// =====================================================================
RoutePermission::new("/api/users", "GET", "")
.with_roles(vec!["Admin".into(), "SuperAdmin".into()]),
RoutePermission::new("/api/users", "POST", "")
.with_roles(vec!["Admin".into(), "SuperAdmin".into()]),
RoutePermission::new("/api/users/:id", "GET", "")
.with_roles(vec!["Admin".into(), "SuperAdmin".into()]),
RoutePermission::new("/api/users/:id", "PUT", "")
.with_roles(vec!["Admin".into(), "SuperAdmin".into()]),
RoutePermission::new("/api/users/:id", "DELETE", "")
.with_roles(vec!["Admin".into(), "SuperAdmin".into()]),
RoutePermission::new("/api/users/**", "GET", "")
.with_roles(vec!["Admin".into(), "SuperAdmin".into()]),
RoutePermission::new("/api/users/**", "POST", "")
.with_roles(vec!["Admin".into(), "SuperAdmin".into()]),
RoutePermission::new("/api/users/**", "PUT", "")
.with_roles(vec!["Admin".into(), "SuperAdmin".into()]),
RoutePermission::new("/api/users/**", "DELETE", "")
.with_roles(vec!["Admin".into(), "SuperAdmin".into()]),
// Groups management
RoutePermission::new("/api/groups/**", "GET", "")
.with_roles(vec!["Admin".into(), "SuperAdmin".into()]),
RoutePermission::new("/api/groups/**", "POST", "")
.with_roles(vec!["Admin".into(), "SuperAdmin".into()]),
RoutePermission::new("/api/groups/**", "PUT", "")
.with_roles(vec!["Admin".into(), "SuperAdmin".into()]),
RoutePermission::new("/api/groups/**", "DELETE", "")
.with_roles(vec!["Admin".into(), "SuperAdmin".into()]),
// Bot management (create/delete)
RoutePermission::new("/api/bots", "POST", "")
.with_roles(vec!["Admin".into(), "SuperAdmin".into()]),
RoutePermission::new("/api/bots/:id", "PUT", "")
.with_roles(vec!["Admin".into(), "SuperAdmin".into()]),
RoutePermission::new("/api/bots/:id", "DELETE", "")
.with_roles(vec!["Admin".into(), "SuperAdmin".into()]),
RoutePermission::new("/api/bots/:id/**", "PUT", "")
.with_roles(vec!["Admin".into(), "SuperAdmin".into()]),
RoutePermission::new("/api/bots/:id/**", "DELETE", "")
.with_roles(vec!["Admin".into(), "SuperAdmin".into()]),
// Analytics (admin view)
RoutePermission::new("/api/analytics/**", "GET", "")
.with_roles(vec!["Admin".into(), "SuperAdmin".into(), "Moderator".into()]),
RoutePermission::new("/api/ui/analytics/**", "GET", "") RoutePermission::new("/api/ui/analytics/**", "GET", "")
.with_roles(vec!["Admin".into(), "SuperAdmin".into(), "Moderator".into()]), .with_roles(vec!["Admin".into(), "SuperAdmin".into(), "Moderator".into()]),
// Monitoring
RoutePermission::new("/api/monitoring/**", "GET", "")
.with_roles(vec!["Admin".into(), "SuperAdmin".into()]),
RoutePermission::new("/api/ui/monitoring/**", "GET", "")
.with_roles(vec!["Admin".into(), "SuperAdmin".into()]),
// Audit logs
RoutePermission::new("/api/audit/**", "GET", "")
.with_roles(vec!["Admin".into(), "SuperAdmin".into()]),
RoutePermission::new("/api/ui/audit/**", "GET", "")
.with_roles(vec!["Admin".into(), "SuperAdmin".into()]),
// Security settings
RoutePermission::new("/api/security/**", "GET", "")
.with_roles(vec!["Admin".into(), "SuperAdmin".into()]),
RoutePermission::new("/api/security/**", "POST", "")
.with_roles(vec!["Admin".into(), "SuperAdmin".into()]),
RoutePermission::new("/api/security/**", "PUT", "")
.with_roles(vec!["Admin".into(), "SuperAdmin".into()]),
RoutePermission::new("/api/ui/security/**", "GET", "")
.with_roles(vec!["Admin".into(), "SuperAdmin".into()]),
// Admin panel
RoutePermission::new("/api/admin/**", "GET", "")
.with_roles(vec!["Admin".into(), "SuperAdmin".into()]),
RoutePermission::new("/api/admin/**", "POST", "")
.with_roles(vec!["Admin".into(), "SuperAdmin".into()]),
RoutePermission::new("/api/admin/**", "PUT", "")
.with_roles(vec!["Admin".into(), "SuperAdmin".into()]),
RoutePermission::new("/api/admin/**", "DELETE", "")
.with_roles(vec!["Admin".into(), "SuperAdmin".into()]),
RoutePermission::new("/api/ui/admin/**", "GET", "") RoutePermission::new("/api/ui/admin/**", "GET", "")
.with_roles(vec!["Admin".into(), "SuperAdmin".into()]), .with_roles(vec!["Admin".into(), "SuperAdmin".into()]),
RoutePermission::new("/api/ui/**", "GET", ""),
// Attendant (customer service)
RoutePermission::new("/api/attendant/**", "GET", "")
.with_roles(vec!["Admin".into(), "SuperAdmin".into(), "Moderator".into()]),
RoutePermission::new("/api/attendant/**", "POST", "")
.with_roles(vec!["Admin".into(), "SuperAdmin".into(), "Moderator".into()]),
RoutePermission::new("/api/ui/attendant/**", "GET", "")
.with_roles(vec!["Admin".into(), "SuperAdmin".into(), "Moderator".into()]),
// =====================================================================
// SUPER ADMIN ONLY ROUTES
// =====================================================================
RoutePermission::new("/api/rbac/**", "GET", "")
.with_roles(vec!["SuperAdmin".into()]),
RoutePermission::new("/api/rbac/**", "POST", "")
.with_roles(vec!["SuperAdmin".into()]),
RoutePermission::new("/api/rbac/**", "PUT", "")
.with_roles(vec!["SuperAdmin".into()]),
RoutePermission::new("/api/rbac/**", "DELETE", "")
.with_roles(vec!["SuperAdmin".into()]),
] ]
} }