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
}
}
|