aboutsummaryrefslogtreecommitdiff
path: root/src/permissions/rbac.rs
blob: c1835e5de0348ed1409e70189768a2548ba201c4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
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
    }
}