From e52b8e1c2e110d0feb74feb7905c2ff064b51d55 Mon Sep 17 00:00:00 2001 From: UMTS at Teleco Date: Sat, 13 Dec 2025 02:48:13 +0100 Subject: committing to insanity --- src/permissions/mod.rs | 3 ++ src/permissions/rbac.rs | 119 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 src/permissions/mod.rs create mode 100644 src/permissions/rbac.rs (limited to 'src/permissions') diff --git a/src/permissions/mod.rs b/src/permissions/mod.rs new file mode 100644 index 0000000..4c43932 --- /dev/null +++ b/src/permissions/mod.rs @@ -0,0 +1,3 @@ +pub mod rbac; + +pub use rbac::RBACManager; diff --git a/src/permissions/rbac.rs b/src/permissions/rbac.rs new file mode 100644 index 0000000..c1835e5 --- /dev/null +++ b/src/permissions/rbac.rs @@ -0,0 +1,119 @@ +// Role-based access control module +use crate::config::Config; +use crate::models::{Permission, QueryAction}; +use std::collections::HashMap; + +#[derive(Clone)] +pub struct RBACManager { + permissions: HashMap>, +} + +impl RBACManager { + pub fn new(config: &Config) -> Self { + let mut permissions = HashMap::new(); + + // Parse permissions from new config format + for (power_str, power_perms) in &config.permissions.power_levels { + if let Ok(power) = power_str.parse::() { + let mut table_permissions = HashMap::new(); + + for perm in &power_perms.basic_rules { + let parts: Vec<&str> = perm.split(':').collect(); + if parts.len() == 2 { + let table = parts[0]; + let permission = Permission::from_str(parts[1]); + + if table == "*" { + // Grant permission to all known tables from config + let all_tables = config.get_known_tables(); + + for table_name in all_tables { + // Don't overwrite existing specific permissions + if !table_permissions.contains_key(&table_name) { + table_permissions.insert(table_name, permission.clone()); + } + } + } else { + table_permissions.insert(table.to_string(), permission); + } + } + } + + permissions.insert(power, table_permissions); + } + } + + Self { permissions } + } + + fn base_table_name(table: &str) -> String { + // Accept formats: "table", "table alias", "table AS alias" (AS case-insensitive) + let parts: Vec<&str> = table.trim().split_whitespace().collect(); + if parts.len() >= 3 && parts[1].eq_ignore_ascii_case("AS") { + parts[0].to_string() + } else { + // For simple "table alias" or just "table", take the first token + parts.get(0).cloned().unwrap_or("").to_string() + } + } + + pub fn check_permission( + &self, + config: &Config, + power: i32, + table: &str, + action: &QueryAction, + ) -> bool { + // Normalize potential alias usage to the base table name for permission lookup + let base_table = Self::base_table_name(table); + + if let Some(table_permissions) = self.permissions.get(&power) { + if let Some(permission) = table_permissions.get(&base_table) { + // Check if table is read-only through config and enforce read-only constraint + if config.is_read_only_table(&base_table) && !matches!(action, QueryAction::Select) + { + return false; // Write operations not allowed on read-only tables + } + + match action { + QueryAction::Select | QueryAction::Count => permission.can_read(), + QueryAction::Insert | QueryAction::Update | QueryAction::Delete => { + permission.can_write() + } + } + } else { + false // No permission for this table + } + } else { + false // No permissions for this power level + } + } + + pub fn get_table_permissions(&self, config: &Config, power: i32) -> HashMap { + let mut result = HashMap::new(); + + if let Some(table_permissions) = self.permissions.get(&power) { + for (table, permission) in table_permissions { + let perm_str = match permission { + Permission::Read => "r", + Permission::Write => "w", + Permission::ReadWrite => "rw", + Permission::None => "", + }; + + if !perm_str.is_empty() { + result.insert(table.clone(), perm_str.to_string()); + } + } + } + + // Ensure read-only tables from config are marked as read-only + for table in &config.get_known_tables() { + if config.is_read_only_table(table) && result.contains_key(table) { + result.insert(table.clone(), "r".to_string()); + } + } + + result + } +} -- cgit v1.2.3-70-g09d2