1use std::collections::HashMap;
2use std::sync::Arc;
3
4use parking_lot::RwLock;
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct Tenant {
10 pub id: String,
12 pub name: String,
14 pub plan: TenantPlan,
16 pub max_crawls: u64,
18 pub max_pages: usize,
20 pub max_concurrent: usize,
22 pub rate_limit: u32,
24 pub features: Vec<String>,
26}
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
30pub enum TenantPlan {
31 Free,
32 Pro,
33 Enterprise,
34}
35
36pub struct TenantManager {
38 tenants: Arc<RwLock<HashMap<String, Tenant>>>,
39}
40
41impl TenantManager {
42 #[must_use]
44 pub fn new() -> Self {
45 Self {
46 tenants: Arc::new(RwLock::new(HashMap::new())),
47 }
48 }
49
50 pub fn add_tenant(&self, tenant: Tenant) {
52 let mut tenants = self.tenants.write();
53 tenants.insert(tenant.id.clone(), tenant);
54 }
55
56 #[must_use]
58 pub fn get_tenant(&self, id: &str) -> Option<Tenant> {
59 self.tenants.read().get(id).cloned()
60 }
61
62 #[must_use]
64 pub fn list_tenants(&self) -> Vec<Tenant> {
65 self.tenants.read().values().cloned().collect()
66 }
67
68 pub fn remove_tenant(&self, id: &str) -> bool {
70 self.tenants.write().remove(id).is_some()
71 }
72
73 #[must_use]
75 pub fn count(&self) -> usize {
76 self.tenants.read().len()
77 }
78}
79
80impl Default for TenantManager {
81 fn default() -> Self {
82 Self::new()
83 }
84}
85
86#[derive(Debug, Clone, Serialize, Deserialize)]
88pub struct Role {
89 pub id: String,
91 pub name: String,
93 pub permissions: Vec<String>,
95}
96
97#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
99pub enum Permission {
100 CrawlCreate,
102 CrawlRead,
104 CrawlDelete,
106 ApiKeyManage,
108 AnalyticsView,
110 UserManage,
112 TenantManage,
114 AuditView,
116 BillingManage,
118}
119
120impl Permission {
121 #[must_use]
123 pub fn as_str(&self) -> &'static str {
124 match self {
125 Permission::CrawlCreate => "crawl:create",
126 Permission::CrawlRead => "crawl:read",
127 Permission::CrawlDelete => "crawl:delete",
128 Permission::ApiKeyManage => "apikey:manage",
129 Permission::AnalyticsView => "analytics:view",
130 Permission::UserManage => "user:manage",
131 Permission::TenantManage => "tenant:manage",
132 Permission::AuditView => "audit:view",
133 Permission::BillingManage => "billing:manage",
134 }
135 }
136
137 pub fn parse(s: &str) -> Option<Self> {
141 match s {
142 "crawl:create" => Some(Permission::CrawlCreate),
143 "crawl:read" => Some(Permission::CrawlRead),
144 "crawl:delete" => Some(Permission::CrawlDelete),
145 "apikey:manage" => Some(Permission::ApiKeyManage),
146 "analytics:view" => Some(Permission::AnalyticsView),
147 "user:manage" => Some(Permission::UserManage),
148 "tenant:manage" => Some(Permission::TenantManage),
149 "audit:view" => Some(Permission::AuditView),
150 "billing:manage" => Some(Permission::BillingManage),
151 _ => None,
152 }
153 }
154}
155
156#[derive(Debug, Clone, Serialize, Deserialize)]
158pub struct User {
159 pub id: String,
161 pub email: String,
163 pub name: String,
165 pub tenant_id: String,
167 pub roles: Vec<String>,
169 pub active: bool,
171}
172
173pub struct RbacManager {
175 roles: Arc<RwLock<HashMap<String, Role>>>,
176 users: Arc<RwLock<HashMap<String, User>>>,
177}
178
179impl RbacManager {
180 #[must_use]
182 pub fn new() -> Self {
183 let mut roles = HashMap::new();
184
185 roles.insert(
187 "admin".to_string(),
188 Role {
189 id: "admin".to_string(),
190 name: "Administrator".to_string(),
191 permissions: Permission::iter().map(|p| p.as_str().to_string()).collect(),
192 },
193 );
194
195 roles.insert(
196 "user".to_string(),
197 Role {
198 id: "user".to_string(),
199 name: "User".to_string(),
200 permissions: vec![
201 Permission::CrawlCreate.as_str().to_string(),
202 Permission::CrawlRead.as_str().to_string(),
203 Permission::AnalyticsView.as_str().to_string(),
204 ],
205 },
206 );
207
208 roles.insert(
209 "viewer".to_string(),
210 Role {
211 id: "viewer".to_string(),
212 name: "Viewer".to_string(),
213 permissions: vec![
214 Permission::CrawlRead.as_str().to_string(),
215 Permission::AnalyticsView.as_str().to_string(),
216 ],
217 },
218 );
219
220 Self {
221 roles: Arc::new(RwLock::new(roles)),
222 users: Arc::new(RwLock::new(HashMap::new())),
223 }
224 }
225
226 pub fn add_role(&self, role: Role) {
228 let mut roles = self.roles.write();
229 roles.insert(role.id.clone(), role);
230 }
231
232 #[must_use]
234 pub fn get_role(&self, id: &str) -> Option<Role> {
235 self.roles.read().get(id).cloned()
236 }
237
238 pub fn add_user(&self, user: User) {
240 let mut users = self.users.write();
241 users.insert(user.id.clone(), user);
242 }
243
244 #[must_use]
246 pub fn get_user(&self, id: &str) -> Option<User> {
247 self.users.read().get(id).cloned()
248 }
249
250 #[must_use]
252 pub fn has_permission(&self, user_id: &str, permission: &Permission) -> bool {
253 let users = self.users.read();
254 let roles = self.roles.read();
255
256 if let Some(user) = users.get(user_id) {
257 if !user.active {
258 return false;
259 }
260
261 for role_id in &user.roles {
262 if let Some(role) = roles.get(role_id) {
263 if role.permissions.contains(&permission.as_str().to_string()) {
264 return true;
265 }
266 }
267 }
268 }
269
270 false
271 }
272
273 #[must_use]
275 pub fn get_permissions(&self, user_id: &str) -> Vec<String> {
276 let users = self.users.read();
277 let roles = self.roles.read();
278 let mut permissions = Vec::new();
279
280 if let Some(user) = users.get(user_id) {
281 if !user.active {
282 return permissions;
283 }
284
285 for role_id in &user.roles {
286 if let Some(role) = roles.get(role_id) {
287 permissions.extend(role.permissions.clone());
288 }
289 }
290 }
291
292 permissions.sort();
293 permissions.dedup();
294 permissions
295 }
296}
297
298impl Default for RbacManager {
299 fn default() -> Self {
300 Self::new()
301 }
302}
303
304#[derive(Debug, Clone, Serialize, Deserialize)]
306pub struct SsoConfig {
307 pub provider: SsoProvider,
309 pub provider_url: String,
311 pub client_id: String,
313 pub client_secret: String,
315 pub callback_url: String,
317 pub enabled_domains: Vec<String>,
319}
320
321#[derive(Debug, Clone, Serialize, Deserialize)]
323pub enum SsoProvider {
324 Saml,
325 Oidc,
326}
327
328pub struct SsoManager {
330 configs: Arc<RwLock<Vec<SsoConfig>>>,
331}
332
333impl SsoManager {
334 #[must_use]
336 pub fn new() -> Self {
337 Self {
338 configs: Arc::new(RwLock::new(Vec::new())),
339 }
340 }
341
342 pub fn add_config(&self, config: SsoConfig) {
344 let mut configs = self.configs.write();
345 configs.push(config);
346 }
347
348 #[must_use]
350 pub fn get_config_for_domain(&self, domain: &str) -> Option<SsoConfig> {
351 self.configs
352 .read()
353 .iter()
354 .find(|c| c.enabled_domains.contains(&domain.to_string()))
355 .cloned()
356 }
357
358 #[must_use]
360 pub fn list_configs(&self) -> Vec<SsoConfig> {
361 self.configs.read().clone()
362 }
363}
364
365impl Default for SsoManager {
366 fn default() -> Self {
367 Self::new()
368 }
369}
370
371#[derive(Debug, Clone, Serialize, Deserialize)]
373pub struct SlaConfig {
374 pub uptime_target: f64,
376 pub response_time_target: u64,
378 pub error_rate_target: f64,
380 pub alert_thresholds: SlaThresholds,
382}
383
384#[derive(Debug, Clone, Serialize, Deserialize)]
386pub struct SlaThresholds {
387 pub warning: f64,
389 pub critical: f64,
391}
392
393impl Default for SlaConfig {
394 fn default() -> Self {
395 Self {
396 uptime_target: 99.9,
397 response_time_target: 500,
398 error_rate_target: 1.0,
399 alert_thresholds: SlaThresholds {
400 warning: 0.95,
401 critical: 0.90,
402 },
403 }
404 }
405}
406
407pub struct SlaMonitor {
409 config: SlaConfig,
410 metrics: Arc<RwLock<SlaMetrics>>,
411}
412
413#[derive(Debug, Clone, Default, Serialize, Deserialize)]
415pub struct SlaMetrics {
416 pub total_requests: u64,
418 pub successful_requests: u64,
420 pub failed_requests: u64,
422 pub total_response_time: u64,
424 pub uptime: f64,
426 pub avg_response_time: f64,
428 pub error_rate: f64,
430}
431
432impl SlaMonitor {
433 #[must_use]
435 pub fn new(config: SlaConfig) -> Self {
436 Self {
437 config,
438 metrics: Arc::new(RwLock::new(SlaMetrics::default())),
439 }
440 }
441
442 #[must_use]
444 pub fn with_default_config() -> Self {
445 Self::new(SlaConfig::default())
446 }
447
448 pub fn record_success(&self, response_time_ms: u64) {
450 let mut metrics = self.metrics.write();
451 metrics.total_requests += 1;
452 metrics.successful_requests += 1;
453 metrics.total_response_time += response_time_ms;
454 self.update_metrics(&mut metrics);
455 }
456
457 pub fn record_failure(&self) {
459 let mut metrics = self.metrics.write();
460 metrics.total_requests += 1;
461 metrics.failed_requests += 1;
462 self.update_metrics(&mut metrics);
463 }
464
465 fn update_metrics(&self, metrics: &mut SlaMetrics) {
467 if metrics.total_requests > 0 {
468 metrics.error_rate =
469 (metrics.failed_requests as f64 / metrics.total_requests as f64) * 100.0;
470 metrics.avg_response_time =
471 metrics.total_response_time as f64 / metrics.total_requests as f64;
472 }
473 }
474
475 #[must_use]
477 pub fn metrics(&self) -> SlaMetrics {
478 self.metrics.read().clone()
479 }
480
481 #[must_use]
483 pub fn is_sla_met(&self) -> bool {
484 let metrics = self.metrics.read();
485 metrics.error_rate <= self.config.error_rate_target
486 && metrics.avg_response_time <= self.config.response_time_target as f64
487 }
488
489 #[must_use]
491 pub fn status(&self) -> SlaStatus {
492 let metrics = self.metrics.read();
493 let error_rate_ratio = metrics.error_rate / self.config.error_rate_target;
494 let response_time_ratio =
495 metrics.avg_response_time / self.config.response_time_target as f64;
496
497 let worst_ratio = error_rate_ratio.max(response_time_ratio);
498
499 if worst_ratio <= self.config.alert_thresholds.critical {
500 SlaStatus::Critical
501 } else if worst_ratio <= self.config.alert_thresholds.warning {
502 SlaStatus::Warning
503 } else {
504 SlaStatus::Healthy
505 }
506 }
507
508 #[must_use]
510 pub fn config(&self) -> &SlaConfig {
511 &self.config
512 }
513}
514
515impl Default for SlaMonitor {
516 fn default() -> Self {
517 Self::with_default_config()
518 }
519}
520
521#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
523pub enum SlaStatus {
524 Healthy,
525 Warning,
526 Critical,
527}
528
529trait PermissionIter {
531 fn iter() -> Box<dyn Iterator<Item = Permission>>;
532}
533
534impl PermissionIter for Permission {
535 fn iter() -> Box<dyn Iterator<Item = Permission>> {
536 Box::new(
537 vec![
538 Permission::CrawlCreate,
539 Permission::CrawlRead,
540 Permission::CrawlDelete,
541 Permission::ApiKeyManage,
542 Permission::AnalyticsView,
543 Permission::UserManage,
544 Permission::TenantManage,
545 Permission::AuditView,
546 Permission::BillingManage,
547 ]
548 .into_iter(),
549 )
550 }
551}
552
553#[cfg(test)]
558mod tests {
559 use super::*;
560
561 #[test]
562 fn test_tenant_manager() {
563 let manager = TenantManager::new();
564
565 let tenant = Tenant {
566 id: "tenant1".to_string(),
567 name: "Acme Corp".to_string(),
568 plan: TenantPlan::Enterprise,
569 max_crawls: 1000,
570 max_pages: 10000,
571 max_concurrent: 10,
572 rate_limit: 60,
573 features: vec!["backlinks".to_string(), "rum".to_string()],
574 };
575
576 manager.add_tenant(tenant);
577 assert_eq!(manager.count(), 1);
578 assert!(manager.get_tenant("tenant1").is_some());
579 }
580
581 #[test]
582 fn test_rbac_manager() {
583 let manager = RbacManager::new();
584
585 let user = User {
586 id: "user1".to_string(),
587 email: "test@example.com".to_string(),
588 name: "Test User".to_string(),
589 tenant_id: "tenant1".to_string(),
590 roles: vec!["admin".to_string()],
591 active: true,
592 };
593
594 manager.add_user(user);
595
596 assert!(manager.has_permission("user1", &Permission::CrawlCreate));
598 assert!(manager.has_permission("user1", &Permission::TenantManage));
599 assert!(manager.has_permission("user1", &Permission::BillingManage));
600
601 assert!(!manager.has_permission("nonexistent", &Permission::CrawlCreate));
603 }
604
605 #[test]
606 fn test_sla_monitor() {
607 let monitor = SlaMonitor::with_default_config();
608
609 for _ in 0..90 {
611 monitor.record_success(100);
612 }
613
614 for _ in 0..10 {
616 monitor.record_failure();
617 }
618
619 let metrics = monitor.metrics();
620 assert_eq!(metrics.total_requests, 100);
621 assert_eq!(metrics.successful_requests, 90);
622 assert_eq!(metrics.failed_requests, 10);
623 assert!((metrics.error_rate - 10.0).abs() < 0.01);
624 }
625}