Skip to main content

fakecloud_secretsmanager/
state.rs

1use chrono::{DateTime, Utc};
2use parking_lot::RwLock;
3use std::collections::HashMap;
4use std::sync::Arc;
5
6#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
7pub struct Secret {
8    pub name: String,
9    pub arn: String,
10    pub description: Option<String>,
11    pub kms_key_id: Option<String>,
12    pub versions: HashMap<String, SecretVersion>,
13    pub current_version_id: Option<String>,
14    pub tags: Vec<(String, String)>,
15    pub tags_ever_set: bool,
16    pub deleted: bool,
17    pub deletion_date: Option<DateTime<Utc>>,
18    pub created_at: DateTime<Utc>,
19    pub last_changed_at: DateTime<Utc>,
20    pub last_accessed_at: Option<DateTime<Utc>>,
21    pub rotation_enabled: Option<bool>,
22    pub rotation_lambda_arn: Option<String>,
23    pub rotation_rules: Option<RotationRules>,
24    pub last_rotated_at: Option<DateTime<Utc>>,
25    pub resource_policy: Option<String>,
26}
27
28#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
29pub struct RotationRules {
30    pub automatically_after_days: Option<i64>,
31}
32
33#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
34pub struct SecretVersion {
35    pub version_id: String,
36    pub secret_string: Option<String>,
37    pub secret_binary: Option<Vec<u8>>,
38    pub stages: Vec<String>,
39    pub created_at: DateTime<Utc>,
40}
41
42#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
43pub struct SecretsManagerState {
44    pub account_id: String,
45    pub region: String,
46    pub secrets: HashMap<String, Secret>,
47}
48
49impl SecretsManagerState {
50    pub fn new(account_id: &str, region: &str) -> Self {
51        Self {
52            account_id: account_id.to_string(),
53            region: region.to_string(),
54            secrets: HashMap::new(),
55        }
56    }
57
58    pub fn reset(&mut self) {
59        self.secrets.clear();
60    }
61}
62
63pub type SharedSecretsManagerState =
64    Arc<RwLock<fakecloud_core::multi_account::MultiAccountState<SecretsManagerState>>>;
65
66impl fakecloud_core::multi_account::AccountState for SecretsManagerState {
67    fn new_for_account(account_id: &str, region: &str, _endpoint: &str) -> Self {
68        Self::new(account_id, region)
69    }
70}
71
72/// On-disk snapshot envelope for Secrets Manager state. Versioned so
73/// format changes fail loudly on upgrade.
74#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
75pub struct SecretsManagerSnapshot {
76    pub schema_version: u32,
77    #[serde(default)]
78    pub accounts: Option<fakecloud_core::multi_account::MultiAccountState<SecretsManagerState>>,
79    #[serde(default)]
80    pub state: Option<SecretsManagerState>,
81}
82
83pub const SECRETSMANAGER_SNAPSHOT_SCHEMA_VERSION: u32 = 2;
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88
89    #[test]
90    fn new_initializes_empty() {
91        let state = SecretsManagerState::new("123456789012", "us-east-1");
92        assert_eq!(state.account_id, "123456789012");
93        assert_eq!(state.region, "us-east-1");
94        assert!(state.secrets.is_empty());
95    }
96
97    #[test]
98    fn reset_clears_secrets() {
99        let mut state = SecretsManagerState::new("123456789012", "us-east-1");
100        state.secrets.insert(
101            "s1".to_string(),
102            Secret {
103                name: "s1".to_string(),
104                arn: "arn".to_string(),
105                description: None,
106                kms_key_id: None,
107                versions: HashMap::new(),
108                current_version_id: None,
109                tags: vec![],
110                tags_ever_set: false,
111                deleted: false,
112                deletion_date: None,
113                created_at: Utc::now(),
114                last_changed_at: Utc::now(),
115                last_accessed_at: None,
116                rotation_enabled: None,
117                rotation_lambda_arn: None,
118                rotation_rules: None,
119                last_rotated_at: None,
120                resource_policy: None,
121            },
122        );
123        state.reset();
124        assert!(state.secrets.is_empty());
125    }
126}