aws_iam/model/
impls.rs

1/*!
2Provides implementations for the types in `crate::model::types`.
3*/
4
5use crate::model::containers::OneOrAll;
6use crate::model::qstring::QString;
7use crate::model::types::*;
8use crate::model::OneOrAny;
9use serde::{de, de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
10use std::collections::HashMap;
11use std::fmt;
12use std::fmt::Display;
13use std::str::FromStr;
14use std::string::ToString;
15use uuid::Uuid;
16
17// ------------------------------------------------------------------------------------------------
18// Public Types
19// ------------------------------------------------------------------------------------------------
20
21///
22/// Error conditions which may arise from `FromStr::from_str()`.
23///
24#[derive(Debug, PartialEq)]
25pub enum ConditionOperatorError {
26    /// Empty strings are not valid.
27    EmptyString,
28    /// The condition quantifier (preceeding ':') is invalid.
29    InvalidQuantifier,
30    /// Invalid unqualified condition operator.
31    InvalidGlobalConditionOperator,
32    /// Gobbledegook.
33    InvalidFormat,
34}
35
36// ------------------------------------------------------------------------------------------------
37// Implementations
38// ------------------------------------------------------------------------------------------------
39
40// Policy -----------------------------------------------------------------------------------------
41
42impl Policy {
43    ///
44    /// Create a minimal `Policy` with only required fields.
45    ///
46    pub fn new(statement: OneOrAll<Statement>) -> Self {
47        Policy {
48            version: Some(Self::default_version()),
49            id: None,
50            statement,
51        }
52    }
53
54    ///
55    /// The default version for a policy. Specifically according to the IAM documentation
56    /// if no version is specified in a document it is assumed to be the 2008 version.
57    ///
58    pub fn default_version() -> Version {
59        Version::V2008
60    }
61
62    ///
63    /// Construct a new, random, unique, ID for a Policy.
64    ///
65    pub fn new_id() -> String {
66        random_id("pid_")
67    }
68}
69
70impl Display for Policy {
71    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
72        match serde_json::to_string(self) {
73            Ok(json) => write!(f, "{}", json),
74            Err(_) => Err(fmt::Error),
75        }
76    }
77}
78
79impl FromStr for Policy {
80    type Err = String;
81
82    fn from_str(s: &str) -> Result<Self, Self::Err> {
83        match serde_json::from_str(s) {
84            Ok(policy) => Ok(policy),
85            Err(e) => Err(e.to_string()),
86        }
87    }
88}
89
90// Statement --------------------------------------------------------------------------------------
91
92impl Statement {
93    /// Create a minimal `Statement` with only required fields.
94    ///
95    /// # Example
96    ///
97    /// ```
98    /// use aws_iam::model::*;
99    /// use aws_iam::model::builder::*;
100    /// use std::str::FromStr;
101    ///
102    /// let statement = Statement::new(
103    ///     Effect::Allow,
104    ///     Action::Action(OneOrAny::One("s3:ListBucket".parse().unwrap())),
105    ///     Resource::this("arn:aws:s3:::example_bucket".to_string()),
106    /// );
107    /// ```
108    ///
109    pub fn new(effect: Effect, action: Action, resource: Resource) -> Self {
110        Statement {
111            sid: None,
112            principal: None,
113            effect,
114            action,
115            resource,
116            condition: None,
117        }
118    }
119
120    ///
121    /// Construct a new, random, unique, ID for a Statement.
122    ///
123    pub fn new_sid() -> String {
124        random_id("sid_")
125    }
126}
127
128// Action, Principal, Resource --------------------------------------------------------------------
129
130impl Action {
131    /// Construct a wildcard Action.
132    pub fn any() -> Self {
133        Action::Action(OneOrAny::Any)
134    }
135
136    /// Construct an Action with one value.
137    pub fn this(one: QString) -> Self {
138        Action::Action(OneOrAny::One(one))
139    }
140
141    /// Construct an Action with a list of values.
142    pub fn these(any_of: &mut Vec<QString>) -> Self {
143        Action::Action(OneOrAny::AnyOf(any_of.drain(0..).collect()))
144    }
145
146    /// Construct a negative wildcard Action.
147    pub fn none() -> Self {
148        Action::NotAction(OneOrAny::Any)
149    }
150
151    /// Construct an Action with one negative value.
152    pub fn not_this(one: QString) -> Self {
153        Action::NotAction(OneOrAny::One(one))
154    }
155
156    /// Construct an Action with a list of negative values.
157    pub fn not_these(any_of: &mut Vec<QString>) -> Self {
158        Action::NotAction(OneOrAny::AnyOf(any_of.drain(0..).collect()))
159    }
160}
161
162impl Principal {
163    /// Construct a wildcard Principal.
164    pub fn any(p_type: PrincipalType) -> Self {
165        let mut map: HashMap<PrincipalType, OneOrAny> = Default::default();
166        map.insert(p_type, OneOrAny::Any);
167        Principal::Principal(map)
168    }
169
170    /// Construct a Principal with one value.
171    pub fn this(p_type: PrincipalType, one: String) -> Self {
172        let mut map: HashMap<PrincipalType, OneOrAny> = Default::default();
173        map.insert(p_type, OneOrAny::One(one));
174        Principal::Principal(map)
175    }
176
177    /// Construct a Principal with a list of values.
178    pub fn these(p_type: PrincipalType, any_of: &mut Vec<String>) -> Self {
179        let mut map: HashMap<PrincipalType, OneOrAny> = Default::default();
180        map.insert(p_type, OneOrAny::AnyOf(any_of.drain(0..).collect()));
181        Principal::Principal(map)
182    }
183
184    /// Construct a negative wildcard Principal.
185    pub fn none(p_type: PrincipalType) -> Self {
186        let mut map: HashMap<PrincipalType, OneOrAny> = Default::default();
187        map.insert(p_type, OneOrAny::Any);
188        Principal::NotPrincipal(map)
189    }
190
191    /// Construct a Principal with one negative value.
192    pub fn not_this(p_type: PrincipalType, one: String) -> Self {
193        let mut map: HashMap<PrincipalType, OneOrAny> = Default::default();
194        map.insert(p_type, OneOrAny::One(one));
195        Principal::NotPrincipal(map)
196    }
197
198    /// Construct a Principal with a list of negative values.
199    pub fn not_these(p_type: PrincipalType, any_of: &mut Vec<String>) -> Self {
200        let mut map: HashMap<PrincipalType, OneOrAny> = Default::default();
201        map.insert(p_type, OneOrAny::AnyOf(any_of.drain(0..).collect()));
202        Principal::NotPrincipal(map)
203    }
204}
205
206impl Resource {
207    /// Construct a wildcard Resource.
208    pub fn any() -> Self {
209        Resource::Resource(OneOrAny::Any)
210    }
211
212    /// Construct a Resource with one value.
213    pub fn this(one: String) -> Self {
214        Resource::Resource(OneOrAny::One(one))
215    }
216
217    /// Construct a Resource with a list of values.
218    pub fn these(any_of: &mut Vec<String>) -> Self {
219        Resource::Resource(OneOrAny::AnyOf(any_of.drain(0..).collect()))
220    }
221
222    /// Construct a negative wildcard Resource.
223    pub fn none() -> Self {
224        Resource::NotResource(OneOrAny::Any)
225    }
226
227    /// Construct a Resource with one negative value.
228    pub fn not_this(one: String) -> Self {
229        Resource::NotResource(OneOrAny::One(one))
230    }
231
232    /// Construct a Resource with a list of negative values.
233    pub fn not_these(any_of: &mut Vec<String>) -> Self {
234        Resource::NotResource(OneOrAny::AnyOf(any_of.drain(0..).collect()))
235    }
236}
237
238// ConditionOperator ------------------------------------------------------------------------------
239
240impl ConditionOperator {
241    ///
242    /// Construct a new operator using one of the global operators.
243    ///
244    pub fn new(base: GlobalConditionOperator) -> Self {
245        match base {
246            GlobalConditionOperator::Other(other) => Self::new_other(other),
247            base => ConditionOperator {
248                quantifier: None,
249                operator: base,
250                if_exists: false,
251            },
252        }
253    }
254
255    ///
256    /// Construct a new operator which isn't one of the global ones.
257    ///
258    pub fn new_other(condition: QString) -> Self {
259        ConditionOperator {
260            quantifier: None,
261            operator: GlobalConditionOperator::Other(condition),
262            if_exists: false,
263        }
264    }
265
266    /// Set the quantifier to _for-all-values_.
267    pub fn for_all(self) -> Self {
268        ConditionOperator {
269            quantifier: Some(ConditionOperatorQuantifier::ForAllValues),
270            ..self
271        }
272    }
273
274    /// Set the quantifier to _for-any-value_.
275    pub fn for_any(self) -> Self {
276        ConditionOperator {
277            quantifier: Some(ConditionOperatorQuantifier::ForAnyValue),
278            ..self
279        }
280    }
281
282    /// Set the value of the constraint to `true`.
283    pub fn if_exists(self) -> Self {
284        ConditionOperator {
285            if_exists: true,
286            ..self
287        }
288    }
289}
290
291impl Display for ConditionOperator {
292    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
293        write!(
294            f,
295            "{}{:?}{}",
296            match &self.quantifier {
297                Some(quantifier) => format!("{:?}:", quantifier),
298                None => "".to_string(),
299            },
300            self.operator,
301            if self.if_exists { "IfExists" } else { "" }
302        )
303    }
304}
305
306impl Display for ConditionOperatorError {
307    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
308        write!(f, "{:?}", self)
309    }
310}
311
312impl FromStr for ConditionOperator {
313    type Err = ConditionOperatorError;
314
315    fn from_str(s: &str) -> Result<Self, Self::Err> {
316        if s.is_empty() {
317            return Err(ConditionOperatorError::EmptyString);
318        }
319        // TODO: regex this.
320        #[allow(clippy::clone_double_ref)]
321        let mut s = s.clone();
322        let quantifier = if s.starts_with("ForAllValues:") {
323            s = &s[13..];
324            Some(ConditionOperatorQuantifier::ForAllValues)
325        } else if s.starts_with("ForAnyValue:") {
326            s = &s[12..];
327            Some(ConditionOperatorQuantifier::ForAnyValue)
328        } else {
329            None
330        };
331        if s.contains(':') {
332            return Err(ConditionOperatorError::InvalidQuantifier);
333        }
334        let only_if_exists = if s.ends_with("IfExists") {
335            let end = s.len() - 8;
336            s = &s[0..end];
337            true
338        } else {
339            false
340        };
341        if !s.chars().all(|c| c.is_ascii_alphabetic()) {
342            return Err(ConditionOperatorError::InvalidGlobalConditionOperator);
343        }
344        let operator = match s {
345            "StringEquals" => GlobalConditionOperator::StringEquals,
346            "StringNotEquals" => GlobalConditionOperator::StringNotEquals,
347            "StringEqualsIgnoreCase" => GlobalConditionOperator::StringEqualsIgnoreCase,
348            "StringNotEqualsIgnoreCase" => GlobalConditionOperator::StringNotEqualsIgnoreCase,
349            "StringLike" => GlobalConditionOperator::StringLike,
350            "StringNotLike" => GlobalConditionOperator::StringNotLike,
351            "NumericEquals" => GlobalConditionOperator::NumericEquals,
352            "NumericNotEquals" => GlobalConditionOperator::NumericNotEquals,
353            "NumericLessThan" => GlobalConditionOperator::NumericLessThan,
354            "NumericLessThanEquals" => GlobalConditionOperator::NumericLessThanEquals,
355            "NumericGreaterThan" => GlobalConditionOperator::NumericGreaterThan,
356            "NumericGreaterThanEquals" => GlobalConditionOperator::NumericGreaterThanEquals,
357            "DateEquals" => GlobalConditionOperator::DateEquals,
358            "DateNotEquals" => GlobalConditionOperator::DateNotEquals,
359            "DateLessThan" => GlobalConditionOperator::DateLessThan,
360            "DateLessThanEquals" => GlobalConditionOperator::DateLessThanEquals,
361            "DateGreaterThan" => GlobalConditionOperator::DateGreaterThan,
362            "DateGreaterThanEquals" => GlobalConditionOperator::DateGreaterThanEquals,
363            "Bool" => GlobalConditionOperator::Bool,
364            "BinaryEquals" => GlobalConditionOperator::BinaryEquals,
365            "IpAddress" => GlobalConditionOperator::IpAddress,
366            "NotIpAddress" => GlobalConditionOperator::NotIpAddress,
367            "ArnEquals" => GlobalConditionOperator::ArnEquals,
368            "ArnLike" => GlobalConditionOperator::ArnLike,
369            "ArnNotEquals" => GlobalConditionOperator::ArnNotEquals,
370            "ArnNotLike" => GlobalConditionOperator::ArnNotLike,
371            "Null" => GlobalConditionOperator::Null,
372            other => GlobalConditionOperator::Other(other.parse().unwrap()),
373        };
374        Ok(ConditionOperator {
375            quantifier,
376            operator,
377            if_exists: only_if_exists,
378        })
379    }
380}
381
382impl Serialize for ConditionOperator {
383    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
384    where
385        S: Serializer,
386    {
387        serializer.serialize_str(&self.to_string())
388    }
389}
390
391impl<'de> Deserialize<'de> for ConditionOperator {
392    fn deserialize<D>(deserializer: D) -> Result<ConditionOperator, D::Error>
393    where
394        D: Deserializer<'de>,
395    {
396        deserializer.deserialize_string(ConditionOperatorVisitor)
397    }
398}
399
400struct ConditionOperatorVisitor;
401
402impl<'de> Visitor<'de> for ConditionOperatorVisitor {
403    type Value = ConditionOperator;
404
405    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
406        formatter.write_str("a condition type string")
407    }
408
409    fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
410    where
411        E: de::Error,
412    {
413        ConditionOperator::from_str(value).map_err(de::Error::custom)
414    }
415
416    fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
417    where
418        E: de::Error,
419    {
420        self.visit_str(&value)
421    }
422}
423
424// ------------------------------------------------------------------------------------------------
425// Private Functions
426// ------------------------------------------------------------------------------------------------
427
428fn random_id(prefix: &str) -> String {
429    format!(
430        "{}{}",
431        prefix,
432        Uuid::new_v4()
433            .as_hyphenated()
434            .encode_lower(&mut Uuid::encode_buffer())
435    )
436}
437
438// ------------------------------------------------------------------------------------------------
439// Unit Tests
440// ------------------------------------------------------------------------------------------------
441
442#[cfg(test)]
443mod test {
444    use super::*;
445
446    #[test]
447    fn test_valid_new() {
448        let q_string = QString::new(String::from("foo"), String::from("bar"));
449        assert_eq!(q_string.qualifier(), &Some(String::from("foo")));
450        assert_eq!(q_string.value(), &String::from("bar"));
451    }
452
453    #[test]
454    fn test_valid_unqualified() {
455        let q_string = QString::unqualified(String::from("bar"));
456        assert_eq!(q_string.qualifier(), &None);
457        assert_eq!(q_string.value(), &String::from("bar"));
458    }
459
460    #[test]
461    fn test_valid_from_str() {
462        let q_string = QString::from_str("foo:bar").unwrap();
463        assert_eq!(q_string.qualifier(), &Some(String::from("foo")));
464        assert_eq!(q_string.value(), &String::from("bar"));
465
466        let q_string = QString::from_str("bar").unwrap();
467        assert_eq!(q_string.qualifier(), &None);
468        assert_eq!(q_string.value(), &String::from("bar"));
469    }
470
471    #[test]
472    fn test_condition_type_ok() {
473        assert!(ConditionOperator::from_str("StringEquals").is_ok());
474        assert!(ConditionOperator::from_str("Null").is_ok());
475        assert!(ConditionOperator::from_str("FooTest").is_ok());
476        assert!(ConditionOperator::from_str("ForAllValues:Null").is_ok());
477        assert!(ConditionOperator::from_str("NullIfExists").is_ok());
478        assert!(ConditionOperator::from_str("ForAllValues:NullIfExists").is_ok());
479    }
480
481    #[test]
482    fn test_condition_type_parts_ok() {
483        let c_type = ConditionOperator::from_str("ForAllValues:NullIfExists").unwrap();
484        assert_eq!(
485            c_type.quantifier,
486            Some(ConditionOperatorQuantifier::ForAllValues)
487        );
488        assert_eq!(c_type.operator, GlobalConditionOperator::Null);
489        assert_eq!(c_type.if_exists, true);
490    }
491
492    #[test]
493    fn test_condition_type_bad() {
494        assert_eq!(
495            ConditionOperator::from_str(""),
496            Err(ConditionOperatorError::EmptyString)
497        );
498        assert_eq!(
499            ConditionOperator::from_str("ForNone:StringEquals"),
500            Err(ConditionOperatorError::InvalidQuantifier)
501        );
502        assert_eq!(
503            ConditionOperator::from_str("String="),
504            Err(ConditionOperatorError::InvalidGlobalConditionOperator)
505        );
506    }
507}