1use serde::{Deserialize, Deserializer, Serialize, Serializer};
16use serde_json::value::RawValue;
17use std::collections::HashMap;
18use time::OffsetDateTime;
19
20use crate::BackendInfo;
21
22#[derive(Debug, Serialize, Deserialize, Default, PartialEq, Eq)]
23pub enum AccountStatus {
24 #[serde(rename = "enabled")]
25 Enabled,
26 #[serde(rename = "disabled")]
27 #[default]
28 Disabled,
29}
30
31impl AsRef<str> for AccountStatus {
32 fn as_ref(&self) -> &str {
33 match self {
34 AccountStatus::Enabled => "enabled",
35 AccountStatus::Disabled => "disabled",
36 }
37 }
38}
39
40impl TryFrom<&str> for AccountStatus {
41 type Error = String;
42
43 fn try_from(s: &str) -> Result<Self, Self::Error> {
44 match s {
45 "enabled" => Ok(AccountStatus::Enabled),
46 "disabled" => Ok(AccountStatus::Disabled),
47 _ => Err(format!("invalid account status: {s}")),
48 }
49 }
50}
51
52#[derive(Debug, Serialize, Deserialize)]
53pub enum UserAuthType {
54 #[serde(rename = "builtin")]
55 Builtin,
56 #[serde(rename = "ldap")]
57 Ldap,
58}
59
60#[derive(Debug, Serialize, Deserialize)]
61pub struct UserAuthInfo {
62 #[serde(rename = "type")]
63 pub auth_type: UserAuthType,
64
65 #[serde(rename = "authServer", skip_serializing_if = "Option::is_none")]
66 pub auth_server: Option<String>,
67
68 #[serde(rename = "authServerUserID", skip_serializing_if = "Option::is_none")]
69 pub auth_server_user_id: Option<String>,
70}
71
72#[derive(Debug, Serialize, Deserialize, Default)]
73pub struct UserInfo {
74 #[serde(rename = "userAuthInfo", skip_serializing_if = "Option::is_none")]
75 pub auth_info: Option<UserAuthInfo>,
76
77 #[serde(rename = "secretKey", skip_serializing_if = "Option::is_none")]
78 pub secret_key: Option<String>,
79
80 #[serde(rename = "policyName", skip_serializing_if = "Option::is_none")]
81 pub policy_name: Option<String>,
82
83 #[serde(rename = "status")]
84 pub status: AccountStatus,
85
86 #[serde(rename = "memberOf", skip_serializing_if = "Option::is_none")]
87 pub member_of: Option<Vec<String>>,
88
89 #[serde(rename = "updatedAt")]
90 pub updated_at: Option<OffsetDateTime>,
91}
92
93#[derive(Debug, Serialize, Deserialize)]
94pub struct AddOrUpdateUserReq {
95 #[serde(rename = "secretKey")]
96 pub secret_key: String,
97
98 #[serde(rename = "policy", skip_serializing_if = "Option::is_none")]
99 pub policy: Option<String>,
100
101 #[serde(rename = "status")]
102 pub status: AccountStatus,
103}
104
105#[derive(Debug, Serialize, Deserialize)]
106pub struct ServiceAccountInfo {
107 #[serde(rename = "parentUser")]
108 pub parent_user: String,
109
110 #[serde(rename = "accountStatus")]
111 pub account_status: String,
112
113 #[serde(rename = "impliedPolicy")]
114 pub implied_policy: bool,
115
116 #[serde(rename = "accessKey")]
117 pub access_key: String,
118
119 #[serde(rename = "name", skip_serializing_if = "Option::is_none")]
120 pub name: Option<String>,
121
122 #[serde(rename = "description", skip_serializing_if = "Option::is_none")]
123 pub description: Option<String>,
124
125 #[serde(rename = "expiration", with = "time::serde::rfc3339::option")]
126 pub expiration: Option<OffsetDateTime>,
127}
128
129#[derive(Debug, Serialize, Deserialize)]
130pub struct ListServiceAccountsResp {
131 #[serde(rename = "accounts")]
132 pub accounts: Vec<ServiceAccountInfo>,
133}
134
135#[derive(Debug, Serialize, Deserialize)]
136pub struct AddServiceAccountReq {
137 #[serde(rename = "policy", skip_serializing_if = "Option::is_none")]
138 pub policy: Option<String>,
139
140 #[serde(rename = "targetUser", skip_serializing_if = "Option::is_none")]
141 pub target_user: Option<String>,
142
143 #[serde(rename = "accessKey")]
144 pub access_key: String,
145
146 #[serde(rename = "secretKey")]
147 pub secret_key: String,
148
149 #[serde(rename = "name")]
150 pub name: Option<String>,
151
152 #[serde(rename = "description", skip_serializing_if = "Option::is_none")]
153 pub description: Option<String>,
154
155 #[serde(rename = "expiration", with = "time::serde::rfc3339::option")]
156 pub expiration: Option<OffsetDateTime>,
157}
158
159impl AddServiceAccountReq {
160 pub fn validate(&self) -> Result<(), String> {
161 if self.access_key.is_empty() {
162 return Err("accessKey is empty".to_string());
163 }
164
165 if self.secret_key.is_empty() {
166 return Err("secretKey is empty".to_string());
167 }
168
169 if self.name.is_none() {
170 return Err("name is empty".to_string());
171 }
172
173 Ok(())
176 }
177}
178
179#[derive(Serialize)]
180#[serde(rename_all = "camelCase")]
181pub struct Credentials<'a> {
182 pub access_key: &'a str,
183 pub secret_key: &'a str,
184 #[serde(skip_serializing_if = "Option::is_none")]
185 pub session_token: Option<&'a str>,
186 #[serde(skip_serializing_if = "Option::is_none")]
187 #[serde(with = "time::serde::rfc3339::option")]
188 pub expiration: Option<OffsetDateTime>,
189}
190
191#[derive(Serialize)]
192pub struct AddServiceAccountResp<'a> {
193 pub credentials: Credentials<'a>,
194}
195
196#[derive(Serialize)]
197#[serde(rename_all = "camelCase")]
198pub struct InfoServiceAccountResp {
199 pub parent_user: String,
200 pub account_status: String,
201 pub implied_policy: bool,
202 #[serde(skip_serializing_if = "Option::is_none")]
203 pub policy: Option<String>,
204 #[serde(skip_serializing_if = "Option::is_none")]
205 pub name: Option<String>,
206 #[serde(skip_serializing_if = "Option::is_none")]
207 pub description: Option<String>,
208
209 #[serde(skip_serializing_if = "Option::is_none")]
210 #[serde(with = "time::serde::rfc3339::option")]
211 pub expiration: Option<OffsetDateTime>,
212}
213
214#[derive(Debug, Serialize, Deserialize)]
215pub struct UpdateServiceAccountReq {
216 #[serde(rename = "newPolicy", skip_serializing_if = "Option::is_none")]
217 pub new_policy: Option<String>,
218
219 #[serde(rename = "newSecretKey", skip_serializing_if = "Option::is_none")]
220 pub new_secret_key: Option<String>,
221
222 #[serde(rename = "newStatus", skip_serializing_if = "Option::is_none")]
223 pub new_status: Option<String>,
224
225 #[serde(rename = "newName", skip_serializing_if = "Option::is_none")]
226 pub new_name: Option<String>,
227
228 #[serde(rename = "newDescription", skip_serializing_if = "Option::is_none")]
229 pub new_description: Option<String>,
230
231 #[serde(rename = "newExpiration", skip_serializing_if = "Option::is_none")]
232 #[serde(with = "time::serde::rfc3339::option")]
233 pub new_expiration: Option<OffsetDateTime>,
234}
235
236impl UpdateServiceAccountReq {
237 pub fn validate(&self) -> Result<(), String> {
238 Ok(())
240 }
241}
242
243#[derive(Debug, Serialize, Deserialize, Default)]
244pub struct AccountInfo {
245 pub account_name: String,
246 pub server: BackendInfo,
247 pub policy: serde_json::Value, pub buckets: Vec<BucketAccessInfo>,
249}
250
251#[derive(Debug, Serialize, Deserialize, Default)]
252pub struct BucketAccessInfo {
253 pub name: String,
254 pub size: u64,
255 pub objects: u64,
256 pub object_sizes_histogram: HashMap<String, u64>,
257 pub object_versions_histogram: HashMap<String, u64>,
258 pub details: Option<BucketDetails>,
259 pub prefix_usage: HashMap<String, u64>,
260 #[serde(rename = "expiration", with = "time::serde::rfc3339::option")]
261 pub created: Option<OffsetDateTime>,
262 pub access: AccountAccess,
263}
264
265#[derive(Debug, Serialize, Deserialize, Default)]
266pub struct BucketDetails {
267 pub versioning: bool,
268 pub versioning_suspended: bool,
269 pub locking: bool,
270 pub replication: bool,
271 }
273
274#[derive(Debug, Serialize, Deserialize, Default)]
275pub struct AccountAccess {
276 pub read: bool,
277 pub write: bool,
278}
279
280#[derive(Debug, Clone)]
282pub struct SRSessionPolicy(Option<Box<RawValue>>);
283
284impl SRSessionPolicy {
285 pub fn new() -> Self {
286 SRSessionPolicy(None)
287 }
288
289 pub fn from_json(json: &str) -> Result<Self, serde_json::Error> {
290 if json == "null" {
291 Ok(SRSessionPolicy(None))
292 } else {
293 let raw_value = serde_json::from_str(json)?;
294 Ok(SRSessionPolicy(Some(raw_value)))
295 }
296 }
297
298 pub fn is_null(&self) -> bool {
299 self.0.is_none()
300 }
301
302 pub fn as_str(&self) -> Option<&str> {
303 self.0.as_ref().map(|v| v.get())
304 }
305}
306
307impl Default for SRSessionPolicy {
308 fn default() -> Self {
309 Self::new()
310 }
311}
312
313impl PartialEq for SRSessionPolicy {
314 fn eq(&self, other: &Self) -> bool {
315 self.0.as_ref().map(|v| v.get()) == other.0.as_ref().map(|v| v.get())
316 }
317}
318
319impl Serialize for SRSessionPolicy {
320 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
321 where
322 S: Serializer,
323 {
324 match &self.0 {
325 Some(raw_value) => raw_value.serialize(serializer),
326 None => serializer.serialize_none(),
327 }
328 }
329}
330
331impl<'de> Deserialize<'de> for SRSessionPolicy {
332 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
333 where
334 D: Deserializer<'de>,
335 {
336 let raw_value: Option<Box<RawValue>> = Option::deserialize(deserializer)?;
337 Ok(SRSessionPolicy(raw_value))
338 }
339}
340
341#[derive(Debug, Clone, Serialize, Deserialize)]
343pub struct SRSvcAccCreate {
344 pub parent: String,
345
346 #[serde(rename = "accessKey")]
347 pub access_key: String,
348
349 #[serde(rename = "secretKey")]
350 pub secret_key: String,
351
352 pub groups: Vec<String>,
353
354 pub claims: HashMap<String, serde_json::Value>,
355
356 #[serde(rename = "sessionPolicy")]
357 pub session_policy: SRSessionPolicy,
358
359 pub status: String,
360
361 pub name: String,
362
363 pub description: String,
364
365 #[serde(skip_serializing_if = "Option::is_none")]
366 pub expiration: Option<OffsetDateTime>,
367
368 #[serde(rename = "apiVersion", skip_serializing_if = "Option::is_none")]
369 pub api_version: Option<String>,
370}
371
372#[derive(Debug, Clone, Serialize, Deserialize, Default)]
374pub struct ImportIAMResult {
375 pub skipped: IAMEntities,
379
380 pub removed: IAMEntities,
383
384 pub added: IAMEntities,
386
387 pub failed: IAMErrEntities,
390}
391
392#[derive(Default, Debug, Clone, Serialize, Deserialize)]
394pub struct IAMEntities {
395 pub policies: Vec<String>,
397
398 pub users: Vec<String>,
400
401 pub groups: Vec<String>,
403
404 #[serde(rename = "serviceAccounts")]
406 pub service_accounts: Vec<String>,
407
408 #[serde(rename = "userPolicies")]
411 pub user_policies: Vec<HashMap<String, Vec<String>>>,
412
413 #[serde(rename = "groupPolicies")]
416 pub group_policies: Vec<HashMap<String, Vec<String>>>,
417
418 #[serde(rename = "stsPolicies")]
421 pub sts_policies: Vec<HashMap<String, Vec<String>>>,
422}
423
424#[derive(Debug, Clone, Serialize, Deserialize, Default)]
426pub struct IAMErrEntities {
427 pub policies: Vec<IAMErrEntity>,
429
430 pub users: Vec<IAMErrEntity>,
432
433 pub groups: Vec<IAMErrEntity>,
435
436 #[serde(rename = "serviceAccounts")]
438 pub service_accounts: Vec<IAMErrEntity>,
439
440 #[serde(rename = "userPolicies")]
442 pub user_policies: Vec<IAMErrPolicyEntity>,
443
444 #[serde(rename = "groupPolicies")]
446 pub group_policies: Vec<IAMErrPolicyEntity>,
447
448 #[serde(rename = "stsPolicies")]
450 pub sts_policies: Vec<IAMErrPolicyEntity>,
451}
452
453#[derive(Debug, Clone, Serialize, Deserialize)]
455pub struct IAMErrEntity {
456 pub name: String,
457 pub error: String,
458}
459
460#[derive(Debug, Clone, Serialize, Deserialize)]
462pub struct IAMErrPolicyEntity {
463 pub name: String,
464 pub policies: Vec<String>,
465 pub error: String,
466}
467
468#[cfg(test)]
469mod tests {
470 use super::*;
471 use serde_json;
472 use time::OffsetDateTime;
473
474 #[test]
475 fn test_account_status_default() {
476 let status = AccountStatus::default();
477 assert_eq!(status, AccountStatus::Disabled);
478 }
479
480 #[test]
481 fn test_account_status_as_ref() {
482 assert_eq!(AccountStatus::Enabled.as_ref(), "enabled");
483 assert_eq!(AccountStatus::Disabled.as_ref(), "disabled");
484 }
485
486 #[test]
487 fn test_account_status_try_from_valid() {
488 assert_eq!(AccountStatus::try_from("enabled").unwrap(), AccountStatus::Enabled);
489 assert_eq!(AccountStatus::try_from("disabled").unwrap(), AccountStatus::Disabled);
490 }
491
492 #[test]
493 fn test_account_status_try_from_invalid() {
494 let result = AccountStatus::try_from("invalid");
495 assert!(result.is_err());
496 assert!(result.unwrap_err().contains("invalid account status"));
497 }
498
499 #[test]
500 fn test_account_status_serialization() {
501 let enabled = AccountStatus::Enabled;
502 let disabled = AccountStatus::Disabled;
503
504 let enabled_json = serde_json::to_string(&enabled).unwrap();
505 let disabled_json = serde_json::to_string(&disabled).unwrap();
506
507 assert_eq!(enabled_json, "\"enabled\"");
508 assert_eq!(disabled_json, "\"disabled\"");
509 }
510
511 #[test]
512 fn test_account_status_deserialization() {
513 let enabled: AccountStatus = serde_json::from_str("\"enabled\"").unwrap();
514 let disabled: AccountStatus = serde_json::from_str("\"disabled\"").unwrap();
515
516 assert_eq!(enabled, AccountStatus::Enabled);
517 assert_eq!(disabled, AccountStatus::Disabled);
518 }
519
520 #[test]
521 fn test_user_auth_type_serialization() {
522 let builtin = UserAuthType::Builtin;
523 let ldap = UserAuthType::Ldap;
524
525 let builtin_json = serde_json::to_string(&builtin).unwrap();
526 let ldap_json = serde_json::to_string(&ldap).unwrap();
527
528 assert_eq!(builtin_json, "\"builtin\"");
529 assert_eq!(ldap_json, "\"ldap\"");
530 }
531
532 #[test]
533 fn test_user_auth_info_creation() {
534 let auth_info = UserAuthInfo {
535 auth_type: UserAuthType::Ldap,
536 auth_server: Some("ldap.example.com".to_string()),
537 auth_server_user_id: Some("user123".to_string()),
538 };
539
540 assert!(matches!(auth_info.auth_type, UserAuthType::Ldap));
541 assert_eq!(auth_info.auth_server.unwrap(), "ldap.example.com");
542 assert_eq!(auth_info.auth_server_user_id.unwrap(), "user123");
543 }
544
545 #[test]
546 fn test_user_auth_info_serialization() {
547 let auth_info = UserAuthInfo {
548 auth_type: UserAuthType::Builtin,
549 auth_server: None,
550 auth_server_user_id: None,
551 };
552
553 let json = serde_json::to_string(&auth_info).unwrap();
554 assert!(json.contains("builtin"));
555 assert!(!json.contains("authServer"), "None fields should be skipped");
556 }
557
558 #[test]
559 fn test_user_info_default() {
560 let user_info = UserInfo::default();
561 assert!(user_info.auth_info.is_none());
562 assert!(user_info.secret_key.is_none());
563 assert!(user_info.policy_name.is_none());
564 assert_eq!(user_info.status, AccountStatus::Disabled);
565 assert!(user_info.member_of.is_none());
566 assert!(user_info.updated_at.is_none());
567 }
568
569 #[test]
570 fn test_user_info_with_values() {
571 let now = OffsetDateTime::now_utc();
572 let user_info = UserInfo {
573 auth_info: Some(UserAuthInfo {
574 auth_type: UserAuthType::Builtin,
575 auth_server: None,
576 auth_server_user_id: None,
577 }),
578 secret_key: Some("secret123".to_string()),
579 policy_name: Some("ReadOnlyAccess".to_string()),
580 status: AccountStatus::Enabled,
581 member_of: Some(vec!["group1".to_string(), "group2".to_string()]),
582 updated_at: Some(now),
583 };
584
585 assert!(user_info.auth_info.is_some());
586 assert_eq!(user_info.secret_key.unwrap(), "secret123");
587 assert_eq!(user_info.policy_name.unwrap(), "ReadOnlyAccess");
588 assert_eq!(user_info.status, AccountStatus::Enabled);
589 assert_eq!(user_info.member_of.unwrap().len(), 2);
590 assert!(user_info.updated_at.is_some());
591 }
592
593 #[test]
594 fn test_add_or_update_user_req_creation() {
595 let req = AddOrUpdateUserReq {
596 secret_key: "newsecret".to_string(),
597 policy: Some("FullAccess".to_string()),
598 status: AccountStatus::Enabled,
599 };
600
601 assert_eq!(req.secret_key, "newsecret");
602 assert_eq!(req.policy.unwrap(), "FullAccess");
603 assert_eq!(req.status, AccountStatus::Enabled);
604 }
605
606 #[test]
607 fn test_service_account_info_creation() {
608 let now = OffsetDateTime::now_utc();
609 let service_account = ServiceAccountInfo {
610 parent_user: "admin".to_string(),
611 account_status: "enabled".to_string(),
612 implied_policy: true,
613 access_key: "AKIAIOSFODNN7EXAMPLE".to_string(),
614 name: Some("test-service".to_string()),
615 description: Some("Test service account".to_string()),
616 expiration: Some(now),
617 };
618
619 assert_eq!(service_account.parent_user, "admin");
620 assert_eq!(service_account.account_status, "enabled");
621 assert!(service_account.implied_policy);
622 assert_eq!(service_account.access_key, "AKIAIOSFODNN7EXAMPLE");
623 assert_eq!(service_account.name.unwrap(), "test-service");
624 assert!(service_account.expiration.is_some());
625 }
626
627 #[test]
628 fn test_list_service_accounts_resp_creation() {
629 let resp = ListServiceAccountsResp {
630 accounts: vec![
631 ServiceAccountInfo {
632 parent_user: "user1".to_string(),
633 account_status: "enabled".to_string(),
634 implied_policy: false,
635 access_key: "KEY1".to_string(),
636 name: Some("service1".to_string()),
637 description: None,
638 expiration: None,
639 },
640 ServiceAccountInfo {
641 parent_user: "user2".to_string(),
642 account_status: "disabled".to_string(),
643 implied_policy: true,
644 access_key: "KEY2".to_string(),
645 name: Some("service2".to_string()),
646 description: Some("Second service".to_string()),
647 expiration: None,
648 },
649 ],
650 };
651
652 assert_eq!(resp.accounts.len(), 2);
653 assert_eq!(resp.accounts[0].parent_user, "user1");
654 assert_eq!(resp.accounts[1].account_status, "disabled");
655 }
656
657 #[test]
658 fn test_add_service_account_req_validate_success() {
659 let req = AddServiceAccountReq {
660 policy: Some("ReadOnlyAccess".to_string()),
661 target_user: Some("testuser".to_string()),
662 access_key: "AKIAIOSFODNN7EXAMPLE".to_string(),
663 secret_key: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY".to_string(),
664 name: Some("test-service".to_string()),
665 description: Some("Test service account".to_string()),
666 expiration: None,
667 };
668
669 let result = req.validate();
670 assert!(result.is_ok());
671 }
672
673 #[test]
674 fn test_add_service_account_req_validate_empty_access_key() {
675 let req = AddServiceAccountReq {
676 policy: None,
677 target_user: None,
678 access_key: "".to_string(),
679 secret_key: "secret".to_string(),
680 name: Some("test".to_string()),
681 description: None,
682 expiration: None,
683 };
684
685 let result = req.validate();
686 assert!(result.is_err());
687 assert!(result.unwrap_err().contains("accessKey is empty"));
688 }
689
690 #[test]
691 fn test_add_service_account_req_validate_empty_secret_key() {
692 let req = AddServiceAccountReq {
693 policy: None,
694 target_user: None,
695 access_key: "AKIAIOSFODNN7EXAMPLE".to_string(),
696 secret_key: "".to_string(),
697 name: Some("test".to_string()),
698 description: None,
699 expiration: None,
700 };
701
702 let result = req.validate();
703 assert!(result.is_err());
704 assert!(result.unwrap_err().contains("secretKey is empty"));
705 }
706
707 #[test]
708 fn test_add_service_account_req_validate_empty_name() {
709 let req = AddServiceAccountReq {
710 policy: None,
711 target_user: None,
712 access_key: "AKIAIOSFODNN7EXAMPLE".to_string(),
713 secret_key: "secret".to_string(),
714 name: None,
715 description: None,
716 expiration: None,
717 };
718
719 let result = req.validate();
720 assert!(result.is_err());
721 assert!(result.unwrap_err().contains("name is empty"));
722 }
723
724 #[test]
725 fn test_credentials_serialization() {
726 let now = OffsetDateTime::now_utc();
727 let credentials = Credentials {
728 access_key: "AKIAIOSFODNN7EXAMPLE",
729 secret_key: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
730 session_token: Some("session123"),
731 expiration: Some(now),
732 };
733
734 let json = serde_json::to_string(&credentials).unwrap();
735 assert!(json.contains("AKIAIOSFODNN7EXAMPLE"));
736 assert!(json.contains("wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"));
737 assert!(json.contains("session123"));
738 }
739
740 #[test]
741 fn test_credentials_without_optional_fields() {
742 let credentials = Credentials {
743 access_key: "AKIAIOSFODNN7EXAMPLE",
744 secret_key: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
745 session_token: None,
746 expiration: None,
747 };
748
749 let json = serde_json::to_string(&credentials).unwrap();
750 assert!(json.contains("AKIAIOSFODNN7EXAMPLE"));
751 assert!(!json.contains("sessionToken"), "None fields should be skipped");
752 assert!(!json.contains("expiration"), "None fields should be skipped");
753 }
754
755 #[test]
756 fn test_add_service_account_resp_creation() {
757 let credentials = Credentials {
758 access_key: "AKIAIOSFODNN7EXAMPLE",
759 secret_key: "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
760 session_token: None,
761 expiration: None,
762 };
763
764 let resp = AddServiceAccountResp { credentials };
765
766 assert_eq!(resp.credentials.access_key, "AKIAIOSFODNN7EXAMPLE");
767 assert_eq!(resp.credentials.secret_key, "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY");
768 }
769
770 #[test]
771 fn test_info_service_account_resp_creation() {
772 let now = OffsetDateTime::now_utc();
773 let resp = InfoServiceAccountResp {
774 parent_user: "admin".to_string(),
775 account_status: "enabled".to_string(),
776 implied_policy: true,
777 policy: Some("ReadOnlyAccess".to_string()),
778 name: Some("test-service".to_string()),
779 description: Some("Test service account".to_string()),
780 expiration: Some(now),
781 };
782
783 assert_eq!(resp.parent_user, "admin");
784 assert_eq!(resp.account_status, "enabled");
785 assert!(resp.implied_policy);
786 assert_eq!(resp.policy.unwrap(), "ReadOnlyAccess");
787 assert_eq!(resp.name.unwrap(), "test-service");
788 assert!(resp.expiration.is_some());
789 }
790
791 #[test]
792 fn test_update_service_account_req_validate() {
793 let req = UpdateServiceAccountReq {
794 new_policy: Some("FullAccess".to_string()),
795 new_secret_key: Some("newsecret".to_string()),
796 new_status: Some("enabled".to_string()),
797 new_name: Some("updated-service".to_string()),
798 new_description: Some("Updated description".to_string()),
799 new_expiration: None,
800 };
801
802 let result = req.validate();
803 assert!(result.is_ok());
804 }
805
806 #[test]
807 fn test_account_info_creation() {
808 use crate::BackendInfo;
809
810 let account_info = AccountInfo {
811 account_name: "testuser".to_string(),
812 server: BackendInfo::default(),
813 policy: serde_json::json!({"Version": "2012-10-17"}),
814 buckets: vec![],
815 };
816
817 assert_eq!(account_info.account_name, "testuser");
818 assert!(account_info.buckets.is_empty());
819 assert!(account_info.policy.is_object());
820 }
821
822 #[test]
823 fn test_bucket_access_info_creation() {
824 let now = OffsetDateTime::now_utc();
825 let mut sizes_histogram = HashMap::new();
826 sizes_histogram.insert("small".to_string(), 100);
827 sizes_histogram.insert("large".to_string(), 50);
828
829 let mut versions_histogram = HashMap::new();
830 versions_histogram.insert("v1".to_string(), 80);
831 versions_histogram.insert("v2".to_string(), 70);
832
833 let mut prefix_usage = HashMap::new();
834 prefix_usage.insert("logs/".to_string(), 1000000);
835 prefix_usage.insert("data/".to_string(), 5000000);
836
837 let bucket_info = BucketAccessInfo {
838 name: "test-bucket".to_string(),
839 size: 6000000,
840 objects: 150,
841 object_sizes_histogram: sizes_histogram,
842 object_versions_histogram: versions_histogram,
843 details: Some(BucketDetails {
844 versioning: true,
845 versioning_suspended: false,
846 locking: true,
847 replication: false,
848 }),
849 prefix_usage,
850 created: Some(now),
851 access: AccountAccess {
852 read: true,
853 write: false,
854 },
855 };
856
857 assert_eq!(bucket_info.name, "test-bucket");
858 assert_eq!(bucket_info.size, 6000000);
859 assert_eq!(bucket_info.objects, 150);
860 assert_eq!(bucket_info.object_sizes_histogram.len(), 2);
861 assert_eq!(bucket_info.object_versions_histogram.len(), 2);
862 assert!(bucket_info.details.is_some());
863 assert_eq!(bucket_info.prefix_usage.len(), 2);
864 assert!(bucket_info.created.is_some());
865 assert!(bucket_info.access.read);
866 assert!(!bucket_info.access.write);
867 }
868
869 #[test]
870 fn test_bucket_details_creation() {
871 let details = BucketDetails {
872 versioning: true,
873 versioning_suspended: false,
874 locking: true,
875 replication: true,
876 };
877
878 assert!(details.versioning);
879 assert!(!details.versioning_suspended);
880 assert!(details.locking);
881 assert!(details.replication);
882 }
883
884 #[test]
885 fn test_account_access_creation() {
886 let read_only = AccountAccess {
887 read: true,
888 write: false,
889 };
890
891 let full_access = AccountAccess { read: true, write: true };
892
893 let no_access = AccountAccess {
894 read: false,
895 write: false,
896 };
897
898 assert!(read_only.read && !read_only.write);
899 assert!(full_access.read && full_access.write);
900 assert!(!no_access.read && !no_access.write);
901 }
902
903 #[test]
904 fn test_serialization_deserialization_roundtrip() {
905 let user_info = UserInfo {
906 auth_info: Some(UserAuthInfo {
907 auth_type: UserAuthType::Ldap,
908 auth_server: Some("ldap.example.com".to_string()),
909 auth_server_user_id: Some("user123".to_string()),
910 }),
911 secret_key: Some("secret123".to_string()),
912 policy_name: Some("ReadOnlyAccess".to_string()),
913 status: AccountStatus::Enabled,
914 member_of: Some(vec!["group1".to_string()]),
915 updated_at: None,
916 };
917
918 let json = serde_json::to_string(&user_info).unwrap();
919 let deserialized: UserInfo = serde_json::from_str(&json).unwrap();
920
921 assert_eq!(deserialized.secret_key.unwrap(), "secret123");
922 assert_eq!(deserialized.policy_name.unwrap(), "ReadOnlyAccess");
923 assert_eq!(deserialized.status, AccountStatus::Enabled);
924 assert_eq!(deserialized.member_of.unwrap().len(), 1);
925 }
926
927 #[test]
928 fn test_debug_format_all_structures() {
929 let account_status = AccountStatus::Enabled;
930 let user_auth_type = UserAuthType::Builtin;
931 let user_info = UserInfo::default();
932 let service_account = ServiceAccountInfo {
933 parent_user: "test".to_string(),
934 account_status: "enabled".to_string(),
935 implied_policy: false,
936 access_key: "key".to_string(),
937 name: None,
938 description: None,
939 expiration: None,
940 };
941
942 assert!(!format!("{account_status:?}").is_empty());
944 assert!(!format!("{user_auth_type:?}").is_empty());
945 assert!(!format!("{user_info:?}").is_empty());
946 assert!(!format!("{service_account:?}").is_empty());
947 }
948
949 #[test]
950 fn test_memory_efficiency() {
951 assert!(std::mem::size_of::<AccountStatus>() < 100);
953 assert!(std::mem::size_of::<UserAuthType>() < 100);
954 assert!(std::mem::size_of::<UserInfo>() < 2000);
955 assert!(std::mem::size_of::<ServiceAccountInfo>() < 2000);
956 assert!(std::mem::size_of::<AccountAccess>() < 100);
957 }
958
959 #[test]
960 fn test_edge_cases() {
961 let req = AddServiceAccountReq {
963 policy: Some("".to_string()),
964 target_user: Some("".to_string()),
965 access_key: "valid_key".to_string(),
966 secret_key: "valid_secret".to_string(),
967 name: Some("valid_name".to_string()),
968 description: Some("".to_string()),
969 expiration: None,
970 };
971
972 assert!(req.validate().is_ok());
974
975 let long_string = "a".repeat(1000);
977 let long_req = AddServiceAccountReq {
978 policy: Some(long_string.clone()),
979 target_user: Some(long_string.clone()),
980 access_key: long_string.clone(),
981 secret_key: long_string.clone(),
982 name: Some(long_string.clone()),
983 description: Some(long_string),
984 expiration: None,
985 };
986
987 assert!(long_req.validate().is_ok());
988 }
989}