Skip to main content

fakecloud_secretsmanager/
state.rs

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