Skip to main content

litellm_rs/core/models/team/
settings.rs

1//! Team settings and configuration
2
3use super::entity::TeamVisibility;
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6
7/// Team settings
8#[derive(Debug, Clone, Serialize, Deserialize, Default)]
9pub struct TeamSettings {
10    /// Default user role for new members
11    pub default_member_role: Option<String>,
12    /// Require approval for new members
13    pub require_approval: bool,
14    /// Allow members to invite others
15    pub allow_member_invites: bool,
16    /// Team visibility
17    pub visibility: TeamVisibility,
18    /// API access settings
19    pub api_access: ApiAccessSettings,
20    /// Notification settings
21    pub notifications: TeamNotificationSettings,
22    /// Security settings
23    pub security: TeamSecuritySettings,
24}
25
26/// API access settings
27#[derive(Debug, Clone, Serialize, Deserialize, Default)]
28pub struct ApiAccessSettings {
29    /// Enable API access
30    pub enabled: bool,
31    /// Allowed IP addresses
32    pub allowed_ips: Vec<String>,
33    /// Allowed domains
34    pub allowed_domains: Vec<String>,
35    /// Require API key authentication
36    pub require_api_key: bool,
37    /// Default API settings
38    pub default_settings: HashMap<String, serde_json::Value>,
39}
40
41/// Team notification settings
42#[derive(Debug, Clone, Serialize, Deserialize, Default)]
43pub struct TeamNotificationSettings {
44    /// Slack webhook URL
45    pub slack_webhook: Option<String>,
46    /// Email notifications
47    pub email_notifications: bool,
48    /// Webhook notifications
49    pub webhook_notifications: bool,
50    /// Notification channels
51    pub channels: Vec<NotificationChannel>,
52}
53
54/// Notification channel
55#[derive(Debug, Clone, Serialize, Deserialize)]
56pub struct NotificationChannel {
57    /// Channel name
58    pub name: String,
59    /// Channel type
60    pub channel_type: ChannelType,
61    /// Channel configuration
62    pub config: HashMap<String, serde_json::Value>,
63    /// Enabled
64    pub enabled: bool,
65}
66
67/// Channel type
68#[derive(Debug, Clone, Serialize, Deserialize)]
69#[serde(rename_all = "snake_case")]
70pub enum ChannelType {
71    /// Email channel
72    Email,
73    /// Slack channel
74    Slack,
75    /// Webhook channel
76    Webhook,
77    /// Microsoft Teams channel
78    Teams,
79    /// Discord channel
80    Discord,
81}
82
83/// Team security settings
84#[derive(Debug, Clone, Serialize, Deserialize, Default)]
85pub struct TeamSecuritySettings {
86    /// Require two-factor authentication
87    pub require_2fa: bool,
88    /// Password policy
89    pub password_policy: PasswordPolicy,
90    /// Session timeout in minutes
91    pub session_timeout: Option<u32>,
92    /// IP whitelist
93    pub ip_whitelist: Vec<String>,
94    /// Audit logging enabled
95    pub audit_logging: bool,
96}
97
98/// Password policy
99#[derive(Debug, Clone, Serialize, Deserialize, Default)]
100pub struct PasswordPolicy {
101    /// Minimum length
102    pub min_length: u32,
103    /// Require uppercase
104    pub require_uppercase: bool,
105    /// Require lowercase
106    pub require_lowercase: bool,
107    /// Require numbers
108    pub require_numbers: bool,
109    /// Require special characters
110    pub require_special: bool,
111    /// Password expiry in days
112    pub expiry_days: Option<u32>,
113}
114
115#[cfg(test)]
116mod tests {
117    use super::*;
118
119    // ==================== TeamSettings Tests ====================
120
121    #[test]
122    fn test_team_settings_default() {
123        let settings = TeamSettings::default();
124        assert!(settings.default_member_role.is_none());
125        assert!(!settings.require_approval);
126        assert!(!settings.allow_member_invites);
127    }
128
129    #[test]
130    fn test_team_settings_with_values() {
131        let settings = TeamSettings {
132            default_member_role: Some("member".to_string()),
133            require_approval: true,
134            allow_member_invites: true,
135            visibility: TeamVisibility::Public,
136            api_access: ApiAccessSettings::default(),
137            notifications: TeamNotificationSettings::default(),
138            security: TeamSecuritySettings::default(),
139        };
140
141        assert_eq!(settings.default_member_role, Some("member".to_string()));
142        assert!(settings.require_approval);
143        assert!(settings.allow_member_invites);
144    }
145
146    #[test]
147    fn test_team_settings_serialize() {
148        let settings = TeamSettings::default();
149        let json = serde_json::to_string(&settings).unwrap();
150        assert!(json.contains("require_approval"));
151        assert!(json.contains("visibility"));
152    }
153
154    #[test]
155    fn test_team_settings_clone() {
156        let original = TeamSettings {
157            default_member_role: Some("admin".to_string()),
158            ..Default::default()
159        };
160        let cloned = original.clone();
161        assert_eq!(original.default_member_role, cloned.default_member_role);
162    }
163
164    // ==================== ApiAccessSettings Tests ====================
165
166    #[test]
167    fn test_api_access_settings_default() {
168        let settings = ApiAccessSettings::default();
169        assert!(!settings.enabled);
170        assert!(settings.allowed_ips.is_empty());
171        assert!(settings.allowed_domains.is_empty());
172        assert!(!settings.require_api_key);
173    }
174
175    #[test]
176    fn test_api_access_settings_enabled() {
177        let settings = ApiAccessSettings {
178            enabled: true,
179            allowed_ips: vec!["192.168.1.0/24".to_string()],
180            allowed_domains: vec!["example.com".to_string()],
181            require_api_key: true,
182            default_settings: std::collections::HashMap::new(),
183        };
184
185        assert!(settings.enabled);
186        assert_eq!(settings.allowed_ips.len(), 1);
187        assert!(settings.require_api_key);
188    }
189
190    #[test]
191    fn test_api_access_settings_serialize() {
192        let settings = ApiAccessSettings {
193            enabled: true,
194            allowed_ips: vec!["10.0.0.0/8".to_string()],
195            allowed_domains: vec![],
196            require_api_key: false,
197            default_settings: std::collections::HashMap::new(),
198        };
199
200        let json = serde_json::to_string(&settings).unwrap();
201        assert!(json.contains("10.0.0.0/8"));
202    }
203
204    // ==================== TeamNotificationSettings Tests ====================
205
206    #[test]
207    fn test_team_notification_settings_default() {
208        let settings = TeamNotificationSettings::default();
209        assert!(settings.slack_webhook.is_none());
210        assert!(!settings.email_notifications);
211        assert!(!settings.webhook_notifications);
212        assert!(settings.channels.is_empty());
213    }
214
215    #[test]
216    fn test_team_notification_settings_with_slack() {
217        let settings = TeamNotificationSettings {
218            slack_webhook: Some("https://hooks.slack.com/test".to_string()),
219            email_notifications: true,
220            webhook_notifications: false,
221            channels: vec![],
222        };
223
224        assert!(settings.slack_webhook.is_some());
225        assert!(settings.email_notifications);
226    }
227
228    #[test]
229    fn test_team_notification_settings_serialize() {
230        let settings = TeamNotificationSettings {
231            slack_webhook: Some("https://test.slack.com".to_string()),
232            email_notifications: true,
233            webhook_notifications: true,
234            channels: vec![],
235        };
236
237        let json = serde_json::to_string(&settings).unwrap();
238        assert!(json.contains("slack_webhook"));
239    }
240
241    // ==================== NotificationChannel Tests ====================
242
243    #[test]
244    fn test_notification_channel_creation() {
245        let channel = NotificationChannel {
246            name: "alerts".to_string(),
247            channel_type: ChannelType::Slack,
248            config: std::collections::HashMap::new(),
249            enabled: true,
250        };
251
252        assert_eq!(channel.name, "alerts");
253        assert!(matches!(channel.channel_type, ChannelType::Slack));
254        assert!(channel.enabled);
255    }
256
257    #[test]
258    fn test_notification_channel_serialize() {
259        let channel = NotificationChannel {
260            name: "email-alerts".to_string(),
261            channel_type: ChannelType::Email,
262            config: std::collections::HashMap::new(),
263            enabled: false,
264        };
265
266        let json = serde_json::to_string(&channel).unwrap();
267        assert!(json.contains("email-alerts"));
268        assert!(json.contains("\"enabled\":false"));
269    }
270
271    // ==================== ChannelType Tests ====================
272
273    #[test]
274    fn test_channel_type_email() {
275        let t = ChannelType::Email;
276        let json = serde_json::to_string(&t).unwrap();
277        assert_eq!(json, "\"email\"");
278    }
279
280    #[test]
281    fn test_channel_type_slack() {
282        let t = ChannelType::Slack;
283        let json = serde_json::to_string(&t).unwrap();
284        assert_eq!(json, "\"slack\"");
285    }
286
287    #[test]
288    fn test_channel_type_webhook() {
289        let t = ChannelType::Webhook;
290        let json = serde_json::to_string(&t).unwrap();
291        assert_eq!(json, "\"webhook\"");
292    }
293
294    #[test]
295    fn test_channel_type_teams() {
296        let t = ChannelType::Teams;
297        let json = serde_json::to_string(&t).unwrap();
298        assert_eq!(json, "\"teams\"");
299    }
300
301    #[test]
302    fn test_channel_type_discord() {
303        let t = ChannelType::Discord;
304        let json = serde_json::to_string(&t).unwrap();
305        assert_eq!(json, "\"discord\"");
306    }
307
308    #[test]
309    fn test_channel_type_deserialize() {
310        let t: ChannelType = serde_json::from_str("\"slack\"").unwrap();
311        assert!(matches!(t, ChannelType::Slack));
312    }
313
314    // ==================== TeamSecuritySettings Tests ====================
315
316    #[test]
317    fn test_team_security_settings_default() {
318        let settings = TeamSecuritySettings::default();
319        assert!(!settings.require_2fa);
320        assert!(settings.session_timeout.is_none());
321        assert!(settings.ip_whitelist.is_empty());
322        assert!(!settings.audit_logging);
323    }
324
325    #[test]
326    fn test_team_security_settings_strict() {
327        let settings = TeamSecuritySettings {
328            require_2fa: true,
329            password_policy: PasswordPolicy {
330                min_length: 12,
331                require_uppercase: true,
332                require_lowercase: true,
333                require_numbers: true,
334                require_special: true,
335                expiry_days: Some(90),
336            },
337            session_timeout: Some(30),
338            ip_whitelist: vec!["10.0.0.0/8".to_string()],
339            audit_logging: true,
340        };
341
342        assert!(settings.require_2fa);
343        assert_eq!(settings.session_timeout, Some(30));
344        assert!(settings.audit_logging);
345    }
346
347    #[test]
348    fn test_team_security_settings_serialize() {
349        let settings = TeamSecuritySettings {
350            require_2fa: true,
351            password_policy: PasswordPolicy::default(),
352            session_timeout: Some(60),
353            ip_whitelist: vec![],
354            audit_logging: true,
355        };
356
357        let json = serde_json::to_string(&settings).unwrap();
358        assert!(json.contains("require_2fa"));
359        assert!(json.contains("audit_logging"));
360    }
361
362    // ==================== PasswordPolicy Tests ====================
363
364    #[test]
365    fn test_password_policy_default() {
366        let policy = PasswordPolicy::default();
367        assert_eq!(policy.min_length, 0);
368        assert!(!policy.require_uppercase);
369        assert!(!policy.require_lowercase);
370        assert!(!policy.require_numbers);
371        assert!(!policy.require_special);
372        assert!(policy.expiry_days.is_none());
373    }
374
375    #[test]
376    fn test_password_policy_strict() {
377        let policy = PasswordPolicy {
378            min_length: 16,
379            require_uppercase: true,
380            require_lowercase: true,
381            require_numbers: true,
382            require_special: true,
383            expiry_days: Some(30),
384        };
385
386        assert_eq!(policy.min_length, 16);
387        assert!(policy.require_uppercase);
388        assert!(policy.require_special);
389        assert_eq!(policy.expiry_days, Some(30));
390    }
391
392    #[test]
393    fn test_password_policy_serialize() {
394        let policy = PasswordPolicy {
395            min_length: 8,
396            require_uppercase: true,
397            require_lowercase: true,
398            require_numbers: false,
399            require_special: false,
400            expiry_days: None,
401        };
402
403        let json = serde_json::to_string(&policy).unwrap();
404        assert!(json.contains("min_length"));
405        assert!(json.contains("require_uppercase"));
406    }
407
408    #[test]
409    fn test_password_policy_clone() {
410        let original = PasswordPolicy {
411            min_length: 10,
412            require_uppercase: true,
413            require_lowercase: true,
414            require_numbers: true,
415            require_special: false,
416            expiry_days: Some(60),
417        };
418
419        let cloned = original.clone();
420        assert_eq!(original.min_length, cloned.min_length);
421        assert_eq!(original.expiry_days, cloned.expiry_days);
422    }
423}