1use crate::multi_tenancy::types::{MultiTenancyError, MultiTenancyResult};
4use serde::{Deserialize, Serialize};
5use std::collections::{HashMap, HashSet};
6use std::sync::{Arc, RwLock};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
10pub enum Permission {
11 Read,
13 Write,
15 Delete,
17 BuildIndex,
19 Admin,
21 ViewMetrics,
23 ManageBilling,
25 Custom(u32),
27}
28
29impl Permission {
30 pub fn includes(&self, other: &Permission) -> bool {
32 match self {
33 Self::Admin => true, _ => self == other,
35 }
36 }
37
38 pub fn name(&self) -> &'static str {
40 match self {
41 Self::Read => "read",
42 Self::Write => "write",
43 Self::Delete => "delete",
44 Self::BuildIndex => "build_index",
45 Self::Admin => "admin",
46 Self::ViewMetrics => "view_metrics",
47 Self::ManageBilling => "manage_billing",
48 Self::Custom(_) => "custom",
49 }
50 }
51}
52
53#[derive(Debug, Clone, Serialize, Deserialize)]
55pub struct Role {
56 pub name: String,
58 pub permissions: HashSet<Permission>,
60 pub description: Option<String>,
62}
63
64impl Role {
65 pub fn new(name: impl Into<String>) -> Self {
67 Self {
68 name: name.into(),
69 permissions: HashSet::new(),
70 description: None,
71 }
72 }
73
74 pub fn readonly() -> Self {
76 let mut role = Self::new("readonly");
77 role.permissions.insert(Permission::Read);
78 role.permissions.insert(Permission::ViewMetrics);
79 role.description = Some("Read-only access to vectors and metrics".to_string());
80 role
81 }
82
83 pub fn readwrite() -> Self {
85 let mut role = Self::new("readwrite");
86 role.permissions.insert(Permission::Read);
87 role.permissions.insert(Permission::Write);
88 role.permissions.insert(Permission::ViewMetrics);
89 role.description = Some("Read and write access to vectors".to_string());
90 role
91 }
92
93 pub fn admin() -> Self {
95 let mut role = Self::new("admin");
96 role.permissions.insert(Permission::Admin);
97 role.description = Some("Full administrative access".to_string());
98 role
99 }
100
101 pub fn add_permission(&mut self, permission: Permission) {
103 self.permissions.insert(permission);
104 }
105
106 pub fn has_permission(&self, permission: Permission) -> bool {
108 self.permissions.iter().any(|p| p.includes(&permission))
109 }
110}
111
112#[derive(Debug, Clone, Serialize, Deserialize)]
114pub struct AccessPolicy {
115 pub tenant_id: String,
117 pub user_roles: HashMap<String, Vec<String>>,
119 pub roles: HashMap<String, Role>,
121 pub ip_whitelist: Vec<String>,
123 pub ip_blacklist: Vec<String>,
125}
126
127impl AccessPolicy {
128 pub fn new(tenant_id: impl Into<String>) -> Self {
130 let mut policy = Self {
131 tenant_id: tenant_id.into(),
132 user_roles: HashMap::new(),
133 roles: HashMap::new(),
134 ip_whitelist: Vec::new(),
135 ip_blacklist: Vec::new(),
136 };
137
138 policy.add_role(Role::readonly());
140 policy.add_role(Role::readwrite());
141 policy.add_role(Role::admin());
142
143 policy
144 }
145
146 pub fn add_role(&mut self, role: Role) {
148 self.roles.insert(role.name.clone(), role);
149 }
150
151 pub fn assign_role(&mut self, user_id: impl Into<String>, role_name: impl Into<String>) {
153 self.user_roles
154 .entry(user_id.into())
155 .or_default()
156 .push(role_name.into());
157 }
158
159 pub fn has_permission(&self, user_id: &str, permission: Permission) -> bool {
161 if let Some(role_names) = self.user_roles.get(user_id) {
162 for role_name in role_names {
163 if let Some(role) = self.roles.get(role_name) {
164 if role.has_permission(permission) {
165 return true;
166 }
167 }
168 }
169 }
170 false
171 }
172
173 pub fn is_ip_allowed(&self, ip: &str) -> bool {
175 if self.ip_blacklist.contains(&ip.to_string()) {
177 return false;
178 }
179
180 if self.ip_whitelist.is_empty() {
182 return true;
183 }
184
185 self.ip_whitelist.contains(&ip.to_string())
187 }
188}
189
190pub struct AccessControl {
192 policies: Arc<RwLock<HashMap<String, AccessPolicy>>>,
194}
195
196impl AccessControl {
197 pub fn new() -> Self {
199 Self {
200 policies: Arc::new(RwLock::new(HashMap::new())),
201 }
202 }
203
204 pub fn set_policy(&self, policy: AccessPolicy) -> MultiTenancyResult<()> {
206 self.policies
207 .write()
208 .map_err(|e| MultiTenancyError::InternalError {
209 message: format!("Lock error: {}", e),
210 })?
211 .insert(policy.tenant_id.clone(), policy);
212 Ok(())
213 }
214
215 pub fn get_policy(&self, tenant_id: &str) -> MultiTenancyResult<AccessPolicy> {
217 self.policies
218 .read()
219 .map_err(|e| MultiTenancyError::InternalError {
220 message: format!("Lock error: {}", e),
221 })?
222 .get(tenant_id)
223 .cloned()
224 .ok_or_else(|| MultiTenancyError::TenantNotFound {
225 tenant_id: tenant_id.to_string(),
226 })
227 }
228
229 pub fn check_permission(
231 &self,
232 tenant_id: &str,
233 user_id: &str,
234 permission: Permission,
235 ) -> MultiTenancyResult<bool> {
236 let policy = self.get_policy(tenant_id)?;
237 Ok(policy.has_permission(user_id, permission))
238 }
239
240 pub fn authorize(
242 &self,
243 tenant_id: &str,
244 user_id: &str,
245 permission: Permission,
246 client_ip: Option<&str>,
247 ) -> MultiTenancyResult<()> {
248 let policy = self.get_policy(tenant_id)?;
249
250 if let Some(ip) = client_ip {
252 if !policy.is_ip_allowed(ip) {
253 return Err(MultiTenancyError::AccessDenied {
254 tenant_id: tenant_id.to_string(),
255 reason: format!("IP {} not allowed", ip),
256 });
257 }
258 }
259
260 if !policy.has_permission(user_id, permission) {
262 return Err(MultiTenancyError::AccessDenied {
263 tenant_id: tenant_id.to_string(),
264 reason: format!("User {} lacks permission {:?}", user_id, permission),
265 });
266 }
267
268 Ok(())
269 }
270
271 pub fn create_default_policy(&self, tenant_id: impl Into<String>) -> MultiTenancyResult<()> {
273 let policy = AccessPolicy::new(tenant_id);
274 self.set_policy(policy)
275 }
276}
277
278impl Default for AccessControl {
279 fn default() -> Self {
280 Self::new()
281 }
282}
283
284#[cfg(test)]
285mod tests {
286 type Result<T> = std::result::Result<T, Box<dyn std::error::Error>>;
287 use super::*;
288
289 #[test]
290 fn test_permissions() {
291 assert!(Permission::Admin.includes(&Permission::Read));
292 assert!(Permission::Admin.includes(&Permission::Write));
293 assert!(!Permission::Read.includes(&Permission::Write));
294 assert!(Permission::Read.includes(&Permission::Read));
295 }
296
297 #[test]
298 fn test_role_creation() {
299 let role = Role::readonly();
300 assert!(role.has_permission(Permission::Read));
301 assert!(!role.has_permission(Permission::Write));
302 assert!(!role.has_permission(Permission::Delete));
303
304 let role = Role::readwrite();
305 assert!(role.has_permission(Permission::Read));
306 assert!(role.has_permission(Permission::Write));
307 assert!(!role.has_permission(Permission::Delete));
308
309 let role = Role::admin();
310 assert!(role.has_permission(Permission::Read));
311 assert!(role.has_permission(Permission::Write));
312 assert!(role.has_permission(Permission::Delete));
313 assert!(role.has_permission(Permission::Admin));
314 }
315
316 #[test]
317 fn test_access_policy() {
318 let mut policy = AccessPolicy::new("tenant1");
319
320 policy.assign_role("user1", "readonly");
322 policy.assign_role("user2", "readwrite");
323 policy.assign_role("user3", "admin");
324
325 assert!(policy.has_permission("user1", Permission::Read));
327 assert!(!policy.has_permission("user1", Permission::Write));
328
329 assert!(policy.has_permission("user2", Permission::Read));
330 assert!(policy.has_permission("user2", Permission::Write));
331 assert!(!policy.has_permission("user2", Permission::Delete));
332
333 assert!(policy.has_permission("user3", Permission::Read));
334 assert!(policy.has_permission("user3", Permission::Write));
335 assert!(policy.has_permission("user3", Permission::Delete));
336 assert!(policy.has_permission("user3", Permission::Admin));
337 }
338
339 #[test]
340 fn test_ip_restrictions() {
341 let mut policy = AccessPolicy::new("tenant1");
342
343 assert!(policy.is_ip_allowed("192.168.1.1"));
345 assert!(policy.is_ip_allowed("10.0.0.1"));
346
347 policy.ip_blacklist.push("192.168.1.100".to_string());
349 assert!(!policy.is_ip_allowed("192.168.1.100"));
350 assert!(policy.is_ip_allowed("192.168.1.1"));
351
352 policy.ip_whitelist.push("192.168.1.1".to_string());
354 policy.ip_whitelist.push("192.168.1.2".to_string());
355 assert!(policy.is_ip_allowed("192.168.1.1"));
356 assert!(policy.is_ip_allowed("192.168.1.2"));
357 assert!(!policy.is_ip_allowed("10.0.0.1"));
358 assert!(!policy.is_ip_allowed("192.168.1.100")); }
360
361 #[test]
362 fn test_access_control_manager() -> Result<()> {
363 let ac = AccessControl::new();
364
365 ac.create_default_policy("tenant1")?;
367
368 let mut policy = ac.get_policy("tenant1")?;
370 policy.assign_role("user1", "readonly");
371 policy.assign_role("user2", "admin");
372 ac.set_policy(policy)?;
373
374 assert!(ac.check_permission("tenant1", "user1", Permission::Read)?);
376 assert!(!ac.check_permission("tenant1", "user1", Permission::Write)?);
377
378 assert!(ac.check_permission("tenant1", "user2", Permission::Admin)?);
379
380 assert!(ac
382 .authorize("tenant1", "user1", Permission::Read, None)
383 .is_ok());
384 assert!(ac
385 .authorize("tenant1", "user1", Permission::Write, None)
386 .is_err());
387 assert!(ac
388 .authorize("tenant1", "user2", Permission::Write, None)
389 .is_ok());
390 Ok(())
391 }
392
393 #[test]
394 fn test_authorize_with_ip() -> Result<()> {
395 let ac = AccessControl::new();
396 let mut policy = AccessPolicy::new("tenant1");
397 policy.assign_role("user1", "readonly");
398 policy.ip_whitelist.push("192.168.1.1".to_string());
399 ac.set_policy(policy)?;
400
401 assert!(ac
403 .authorize("tenant1", "user1", Permission::Read, Some("192.168.1.1"))
404 .is_ok());
405
406 assert!(ac
408 .authorize("tenant1", "user1", Permission::Read, Some("10.0.0.1"))
409 .is_err());
410 Ok(())
411 }
412
413 #[test]
414 fn test_custom_roles() {
415 let mut role = Role::new("custom");
416 role.add_permission(Permission::Read);
417 role.add_permission(Permission::ViewMetrics);
418 role.add_permission(Permission::Custom(100));
419
420 assert!(role.has_permission(Permission::Read));
421 assert!(role.has_permission(Permission::ViewMetrics));
422 assert!(role.has_permission(Permission::Custom(100)));
423 assert!(!role.has_permission(Permission::Write));
424 }
425}