1use crate::validation::{Validate, ValidationContext, ValidationResult, helpers};
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4
5#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
7pub enum PrincipalType {
8 #[serde(rename = "AWS")]
10 Aws,
11 #[serde(rename = "Federated")]
13 Federated,
14 #[serde(rename = "Service")]
16 Service,
17 #[serde(rename = "CanonicalUser")]
19 CanonicalUser,
20}
21
22impl std::fmt::Display for PrincipalType {
23 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
24 match self {
25 PrincipalType::Aws => write!(f, "AWS"),
26 PrincipalType::Federated => write!(f, "Federated"),
27 PrincipalType::Service => write!(f, "Service"),
28 PrincipalType::CanonicalUser => write!(f, "CanonicalUser"),
29 }
30 }
31}
32
33#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
42#[serde(untagged)]
43pub enum Principal {
44 Wildcard,
46 Mapped(HashMap<PrincipalType, serde_json::Value>),
48}
49
50impl Validate for Principal {
51 fn validate(&self, context: &mut ValidationContext) -> ValidationResult {
52 context.with_segment("Principal", |ctx| {
53 match self {
54 Principal::Wildcard => {
55 Ok(())
57 }
58 Principal::Mapped(map) => {
59 if map.is_empty() {
60 return Err(crate::validation::ValidationError::InvalidValue {
61 field: "Principal".to_string(),
62 value: "{}".to_string(),
63 reason: "Principal mapping cannot be empty".to_string(),
64 });
65 }
66
67 let mut results = Vec::new();
68
69 for (key, value) in map {
70 ctx.with_segment(&key.to_string(), |nested_ctx| {
72 match value {
74 serde_json::Value::String(s) => {
75 results.push(helpers::validate_principal(s, nested_ctx));
76 }
77 serde_json::Value::Array(arr) => {
78 for (i, item) in arr.iter().enumerate() {
79 if let serde_json::Value::String(s) = item {
80 nested_ctx.with_segment(
81 &format!("[{}]", i),
82 |item_ctx| {
83 results.push(helpers::validate_principal(
84 s, item_ctx,
85 ));
86 },
87 );
88 } else {
89 results.push(Err(
90 crate::validation::ValidationError::InvalidValue {
91 field: "Principal value".to_string(),
92 value: item.to_string(),
93 reason: "Principal values must be strings"
94 .to_string(),
95 },
96 ));
97 }
98 }
99 }
100 _ => {
101 results.push(Err(
102 crate::validation::ValidationError::InvalidValue {
103 field: "Principal value".to_string(),
104 value: value.to_string(),
105 reason:
106 "Principal value must be string or array of strings"
107 .to_string(),
108 },
109 ));
110 }
111 }
112 });
113 }
114
115 helpers::collect_errors(results)
116 }
117 }
118 })
119 }
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125 use serde_json::json;
126
127 #[test]
128 fn test_principal_validation() {
129 let mut valid_mapped = HashMap::new();
130 valid_mapped.insert(
131 PrincipalType::Aws,
132 json!("arn:aws:iam::123456789012:user/alice"),
133 );
134 let valid_mapped = Principal::Mapped(valid_mapped);
135 assert!(valid_mapped.is_valid());
136
137 let valid_wildcard = Principal::Wildcard;
138 assert!(valid_wildcard.is_valid());
139
140 let mut another_valid_mapped = HashMap::new();
141 another_valid_mapped.insert(
142 PrincipalType::Aws,
143 json!("arn:aws:iam::123456789012:user/alice"),
144 );
145 let another_valid_mapped = Principal::Mapped(another_valid_mapped);
146 assert!(another_valid_mapped.is_valid());
147
148 let mut invalid_mapped = HashMap::new();
149 invalid_mapped.insert(PrincipalType::Aws, json!("invalid-principal"));
150 let invalid_mapped = Principal::Mapped(invalid_mapped);
151 assert!(!invalid_mapped.is_valid());
152
153 let empty_mapped = Principal::Mapped(HashMap::new());
154 assert!(!empty_mapped.is_valid());
155 }
156
157 #[test]
158 fn test_principal_mapped_validation() {
159 let mut service_map = HashMap::new();
161 service_map.insert(PrincipalType::Service, json!("lambda.amazonaws.com"));
162 let service_principal = Principal::Mapped(service_map);
163 assert!(service_principal.is_valid());
164
165 let mut array_map = HashMap::new();
170 array_map.insert(
171 PrincipalType::Aws,
172 json!([
173 "arn:aws:iam::123456789012:user/alice",
174 "arn:aws:iam::123456789012:user/bob"
175 ]),
176 );
177 let array_principal = Principal::Mapped(array_map);
178 assert!(array_principal.is_valid());
179 }
180}