aboutsummaryrefslogtreecommitdiff
path: root/src/permissions
diff options
context:
space:
mode:
authorUMTS at Teleco <crt@teleco.ch>2025-12-13 02:48:13 +0100
committerUMTS at Teleco <crt@teleco.ch>2025-12-13 02:48:13 +0100
commite52b8e1c2e110d0feb74feb7905c2ff064b51d55 (patch)
tree3090814e422250e07e72cf1c83241ffd95cf20f7 /src/permissions
committing to insanityHEADmaster
Diffstat (limited to 'src/permissions')
-rw-r--r--src/permissions/mod.rs3
-rw-r--r--src/permissions/rbac.rs119
2 files changed, 122 insertions, 0 deletions
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<i32, HashMap<String, Permission>>,
+}
+
+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::<i32>() {
+ 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<String, String> {
+ 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
+ }
+}