winterbaume_accessanalyzer/
state.rs1use std::collections::HashMap;
2
3use chrono::Utc;
4use thiserror::Error;
5
6use crate::types::{Analyzer, ArchiveRule, CriterionValue};
7
8#[derive(Debug, Default)]
9pub struct AccessAnalyzerState {
10 pub analyzers: HashMap<String, AnalyzerState>,
12}
13
14#[derive(Debug, Clone)]
16pub struct AnalyzerState {
17 pub analyzer: Analyzer,
18 pub archive_rules: HashMap<String, ArchiveRule>,
19}
20
21#[derive(Debug, Error)]
23pub enum AccessAnalyzerError {
24 #[error("Analyzer {name} already exists.")]
25 AnalyzerAlreadyExists { name: String },
26
27 #[error("Analyzer {name} not found.")]
28 AnalyzerNotFound { name: String },
29
30 #[error("Archive rule {rule_name} already exists for analyzer {analyzer_name}.")]
31 ArchiveRuleAlreadyExists {
32 analyzer_name: String,
33 rule_name: String,
34 },
35
36 #[error("Archive rule {rule_name} not found for analyzer {analyzer_name}.")]
37 ArchiveRuleNotFound {
38 analyzer_name: String,
39 rule_name: String,
40 },
41
42 #[error("Resource {arn} not found.")]
43 ResourceNotFound { arn: String },
44
45 #[error("{message}")]
46 Validation { message: String },
47}
48
49impl AccessAnalyzerState {
50 pub fn create_analyzer(
55 &mut self,
56 name: &str,
57 analyzer_type: &str,
58 tags: HashMap<String, String>,
59 region: &str,
60 account_id: &str,
61 ) -> Result<&Analyzer, AccessAnalyzerError> {
62 if self.analyzers.contains_key(name) {
63 return Err(AccessAnalyzerError::AnalyzerAlreadyExists {
64 name: name.to_string(),
65 });
66 }
67
68 let arn = format!("arn:aws:access-analyzer:{region}:{account_id}:analyzer/{name}");
69 let now = Utc::now().to_rfc3339_opts(chrono::SecondsFormat::Millis, true);
70
71 let analyzer = Analyzer {
72 arn,
73 name: name.to_string(),
74 analyzer_type: analyzer_type.to_string(),
75 status: "ACTIVE".to_string(),
76 created_at: now,
77 tags,
78 };
79
80 let state = AnalyzerState {
81 analyzer,
82 archive_rules: HashMap::new(),
83 };
84
85 self.analyzers.insert(name.to_string(), state);
86 Ok(&self.analyzers.get(name).unwrap().analyzer)
87 }
88
89 pub fn get_analyzer(&self, name: &str) -> Result<&AnalyzerState, AccessAnalyzerError> {
90 self.analyzers
91 .get(name)
92 .ok_or_else(|| AccessAnalyzerError::AnalyzerNotFound {
93 name: name.to_string(),
94 })
95 }
96
97 pub fn delete_analyzer(&mut self, name: &str) -> Result<(), AccessAnalyzerError> {
98 if self.analyzers.remove(name).is_none() {
99 return Err(AccessAnalyzerError::AnalyzerNotFound {
100 name: name.to_string(),
101 });
102 }
103 Ok(())
104 }
105
106 pub fn list_analyzers(&self, type_filter: Option<&str>) -> Vec<&Analyzer> {
107 self.analyzers
108 .values()
109 .filter(|s| {
110 type_filter.is_none() || type_filter == Some(s.analyzer.analyzer_type.as_str())
111 })
112 .map(|s| &s.analyzer)
113 .collect()
114 }
115
116 pub fn create_archive_rule(
121 &mut self,
122 analyzer_name: &str,
123 rule_name: &str,
124 filter: HashMap<String, CriterionValue>,
125 ) -> Result<(), AccessAnalyzerError> {
126 let analyzer_state = self.analyzers.get_mut(analyzer_name).ok_or_else(|| {
127 AccessAnalyzerError::AnalyzerNotFound {
128 name: analyzer_name.to_string(),
129 }
130 })?;
131
132 if analyzer_state.archive_rules.contains_key(rule_name) {
133 return Err(AccessAnalyzerError::ArchiveRuleAlreadyExists {
134 analyzer_name: analyzer_name.to_string(),
135 rule_name: rule_name.to_string(),
136 });
137 }
138
139 let now = Utc::now().to_rfc3339_opts(chrono::SecondsFormat::Millis, true);
140 let rule = ArchiveRule {
141 rule_name: rule_name.to_string(),
142 filter,
143 created_at: now.clone(),
144 updated_at: now,
145 };
146
147 analyzer_state
148 .archive_rules
149 .insert(rule_name.to_string(), rule);
150 Ok(())
151 }
152
153 pub fn get_archive_rule(
154 &self,
155 analyzer_name: &str,
156 rule_name: &str,
157 ) -> Result<&ArchiveRule, AccessAnalyzerError> {
158 let analyzer_state = self.analyzers.get(analyzer_name).ok_or_else(|| {
159 AccessAnalyzerError::AnalyzerNotFound {
160 name: analyzer_name.to_string(),
161 }
162 })?;
163
164 analyzer_state.archive_rules.get(rule_name).ok_or_else(|| {
165 AccessAnalyzerError::ArchiveRuleNotFound {
166 analyzer_name: analyzer_name.to_string(),
167 rule_name: rule_name.to_string(),
168 }
169 })
170 }
171
172 pub fn delete_archive_rule(
173 &mut self,
174 analyzer_name: &str,
175 rule_name: &str,
176 ) -> Result<(), AccessAnalyzerError> {
177 let analyzer_state = self.analyzers.get_mut(analyzer_name).ok_or_else(|| {
178 AccessAnalyzerError::AnalyzerNotFound {
179 name: analyzer_name.to_string(),
180 }
181 })?;
182
183 if analyzer_state.archive_rules.remove(rule_name).is_none() {
184 return Err(AccessAnalyzerError::ArchiveRuleNotFound {
185 analyzer_name: analyzer_name.to_string(),
186 rule_name: rule_name.to_string(),
187 });
188 }
189 Ok(())
190 }
191
192 pub fn list_archive_rules(
193 &self,
194 analyzer_name: &str,
195 ) -> Result<Vec<&ArchiveRule>, AccessAnalyzerError> {
196 let analyzer_state = self.analyzers.get(analyzer_name).ok_or_else(|| {
197 AccessAnalyzerError::AnalyzerNotFound {
198 name: analyzer_name.to_string(),
199 }
200 })?;
201
202 Ok(analyzer_state.archive_rules.values().collect())
203 }
204
205 pub fn tag_resource(
210 &mut self,
211 resource_arn: &str,
212 tags: HashMap<String, String>,
213 ) -> Result<(), AccessAnalyzerError> {
214 if let Some(state) = self
216 .analyzers
217 .values_mut()
218 .find(|s| s.analyzer.arn == resource_arn)
219 {
220 state.analyzer.tags.extend(tags);
221 return Ok(());
222 }
223 Err(AccessAnalyzerError::ResourceNotFound {
224 arn: resource_arn.to_string(),
225 })
226 }
227
228 pub fn untag_resource(
229 &mut self,
230 resource_arn: &str,
231 tag_keys: &[String],
232 ) -> Result<(), AccessAnalyzerError> {
233 if let Some(state) = self
234 .analyzers
235 .values_mut()
236 .find(|s| s.analyzer.arn == resource_arn)
237 {
238 for key in tag_keys {
239 state.analyzer.tags.remove(key);
240 }
241 return Ok(());
242 }
243 Err(AccessAnalyzerError::ResourceNotFound {
244 arn: resource_arn.to_string(),
245 })
246 }
247
248 pub fn list_tags_for_resource(
249 &self,
250 resource_arn: &str,
251 ) -> Result<&HashMap<String, String>, AccessAnalyzerError> {
252 if let Some(state) = self
253 .analyzers
254 .values()
255 .find(|s| s.analyzer.arn == resource_arn)
256 {
257 return Ok(&state.analyzer.tags);
258 }
259 Err(AccessAnalyzerError::ResourceNotFound {
260 arn: resource_arn.to_string(),
261 })
262 }
263}