1use {
2 crate::{AssumedRole, CanonicalUser, FederatedUser, PrincipalError, RootUser, Service, User},
3 scratchstack_arn::Arn,
4 std::{
5 default::Default,
6 fmt::{Debug, Display, Formatter, Result as FmtResult},
7 hash::Hash,
8 iter::IntoIterator,
9 ops::Deref,
10 str::FromStr,
11 },
12};
13
14#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
16pub enum PrincipalSource {
17 Aws,
19
20 CanonicalUser,
22
23 Federated,
25
26 Service,
28}
29
30impl Display for PrincipalSource {
31 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
32 match self {
33 Self::Aws => f.write_str("AWS"),
34 Self::CanonicalUser => f.write_str("CanonicalUser"),
35 Self::Federated => f.write_str("Federated"),
36 Self::Service => f.write_str("Service"),
37 }
38 }
39}
40
41#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
46pub struct Principal {
47 identities: Vec<PrincipalIdentity>,
49}
50
51impl Principal {
52 pub fn new(mut identities: Vec<PrincipalIdentity>) -> Self {
54 identities.sort_unstable();
55 identities.dedup();
56 Self {
57 identities,
58 }
59 }
60
61 pub fn with_capacity(capacity: usize) -> Self {
63 Self {
64 identities: Vec::with_capacity(capacity),
65 }
66 }
67
68 pub fn add(&mut self, identity: PrincipalIdentity) {
70 self.identities.push(identity);
71 self.identities.sort_unstable();
72 self.identities.dedup();
73 }
74
75 pub fn as_slice(&self) -> &[PrincipalIdentity] {
77 &self.identities
78 }
79
80 pub fn clear(&mut self) {
82 self.identities.clear();
83 }
84
85 pub fn is_empty(&self) -> bool {
87 self.identities.is_empty()
88 }
89
90 pub fn len(&self) -> usize {
92 self.identities.len()
93 }
94
95 pub fn remove(&mut self, index: usize) -> PrincipalIdentity {
100 self.identities.remove(index)
101 }
102
103 pub fn truncate(&mut self, len: usize) {
106 self.identities.truncate(len)
107 }
108}
109
110impl AsRef<[PrincipalIdentity]> for Principal {
111 fn as_ref(&self) -> &[PrincipalIdentity] {
112 self.identities.as_ref()
113 }
114}
115
116impl Deref for Principal {
117 type Target = [PrincipalIdentity];
118
119 fn deref(&self) -> &[PrincipalIdentity] {
120 self.identities.deref()
121 }
122}
123
124impl Display for Principal {
125 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
126 f.write_str("Principal(")?;
127 let mut first = true;
128 for identity in &self.identities {
129 if first {
130 first = false;
131 } else {
132 f.write_str(", ")?;
133 }
134 Display::fmt(identity, f)?;
135 }
136 f.write_str(")")
137 }
138}
139
140impl From<&[PrincipalIdentity]> for Principal {
141 fn from(identities: &[PrincipalIdentity]) -> Self {
142 Self::new(identities.to_vec())
143 }
144}
145
146impl From<Vec<PrincipalIdentity>> for Principal {
147 fn from(identities: Vec<PrincipalIdentity>) -> Self {
148 Self::new(identities)
149 }
150}
151
152impl From<&Vec<PrincipalIdentity>> for Principal {
153 fn from(identities: &Vec<PrincipalIdentity>) -> Self {
154 Self::new(identities.clone())
155 }
156}
157
158impl From<AssumedRole> for Principal {
159 fn from(role: AssumedRole) -> Self {
160 Self::new(vec![PrincipalIdentity::AssumedRole(role)])
161 }
162}
163
164impl From<CanonicalUser> for Principal {
165 fn from(user: CanonicalUser) -> Self {
166 Self::new(vec![PrincipalIdentity::CanonicalUser(user)])
167 }
168}
169
170impl From<FederatedUser> for Principal {
171 fn from(user: FederatedUser) -> Self {
172 Self::new(vec![PrincipalIdentity::FederatedUser(user)])
173 }
174}
175
176impl From<RootUser> for Principal {
177 fn from(user: RootUser) -> Self {
178 Self::new(vec![PrincipalIdentity::RootUser(user)])
179 }
180}
181
182impl From<Service> for Principal {
183 fn from(service: Service) -> Self {
184 Self::new(vec![PrincipalIdentity::Service(service)])
185 }
186}
187
188impl From<User> for Principal {
189 fn from(user: User) -> Self {
190 Self::new(vec![PrincipalIdentity::User(user)])
191 }
192}
193
194impl<'a> IntoIterator for &'a Principal {
195 type Item = &'a PrincipalIdentity;
196 type IntoIter = std::slice::Iter<'a, PrincipalIdentity>;
197
198 fn into_iter(self) -> Self::IntoIter {
199 self.identities.iter()
200 }
201}
202
203impl IntoIterator for Principal {
204 type Item = PrincipalIdentity;
205 type IntoIter = std::vec::IntoIter<PrincipalIdentity>;
206
207 fn into_iter(self) -> Self::IntoIter {
208 self.identities.into_iter()
209 }
210}
211
212#[derive(Clone, Eq, Hash, Ord, PartialEq, PartialOrd)]
225pub enum PrincipalIdentity {
226 AssumedRole(AssumedRole),
228
229 CanonicalUser(CanonicalUser),
231
232 FederatedUser(FederatedUser),
234
235 RootUser(RootUser),
237
238 Service(Service),
240
241 User(User),
243}
244
245impl PrincipalIdentity {
246 pub fn source(&self) -> PrincipalSource {
248 match self {
249 Self::AssumedRole(_) | Self::RootUser(_) | Self::User(_) => PrincipalSource::Aws,
250 Self::CanonicalUser(_) => PrincipalSource::CanonicalUser,
251 Self::FederatedUser(_) => PrincipalSource::Federated,
252 Self::Service(_) => PrincipalSource::Service,
253 }
254 }
255
256 pub fn has_arn(&self) -> bool {
268 !matches!(self, Self::CanonicalUser(_) | Self::Service(_))
269 }
270
271 #[inline]
273 pub fn as_assumed_role(&self) -> Option<&AssumedRole> {
274 match self {
275 Self::AssumedRole(role) => Some(role),
276 _ => None,
277 }
278 }
279
280 #[inline]
282 pub fn as_canonical_user(&self) -> Option<&CanonicalUser> {
283 match self {
284 Self::CanonicalUser(user) => Some(user),
285 _ => None,
286 }
287 }
288
289 #[inline]
291 pub fn as_federated_user(&self) -> Option<&FederatedUser> {
292 match self {
293 Self::FederatedUser(user) => Some(user),
294 _ => None,
295 }
296 }
297
298 #[inline]
300 pub fn as_root_user(&self) -> Option<&RootUser> {
301 match self {
302 Self::RootUser(user) => Some(user),
303 _ => None,
304 }
305 }
306
307 #[inline]
309 pub fn as_service(&self) -> Option<&Service> {
310 match self {
311 Self::Service(service) => Some(service),
312 _ => None,
313 }
314 }
315
316 #[inline]
318 pub fn as_user(&self) -> Option<&User> {
319 match self {
320 Self::User(user) => Some(user),
321 _ => None,
322 }
323 }
324
325 pub fn parse_arn(arn: &str) -> Result<Self, PrincipalError> {
335 let parsed_arn = Arn::from_str(arn)?;
336 let service = parsed_arn.service();
337 let resource = parsed_arn.resource();
338
339 match service {
340 "sts" if resource.starts_with("assumed-role/") => {
341 return Ok(AssumedRole::try_from(&parsed_arn)?.into());
342 }
343 "iam" => {
344 if resource.starts_with("user/") {
345 return Ok(User::try_from(&parsed_arn)?.into());
346 }
347 }
348 _ => {}
349 }
350
351 Err(PrincipalError::InvalidArn(arn.to_string()))
352 }
353}
354
355impl From<AssumedRole> for PrincipalIdentity {
357 fn from(assumed_role: AssumedRole) -> Self {
358 PrincipalIdentity::AssumedRole(assumed_role)
359 }
360}
361
362impl From<CanonicalUser> for PrincipalIdentity {
364 fn from(canonical_user: CanonicalUser) -> Self {
365 PrincipalIdentity::CanonicalUser(canonical_user)
366 }
367}
368
369impl From<FederatedUser> for PrincipalIdentity {
371 fn from(federated_user: FederatedUser) -> Self {
372 PrincipalIdentity::FederatedUser(federated_user)
373 }
374}
375
376impl From<RootUser> for PrincipalIdentity {
378 fn from(root_user: RootUser) -> Self {
379 PrincipalIdentity::RootUser(root_user)
380 }
381}
382
383impl From<Service> for PrincipalIdentity {
385 fn from(service: Service) -> Self {
386 PrincipalIdentity::Service(service)
387 }
388}
389
390impl From<User> for PrincipalIdentity {
392 fn from(user: User) -> Self {
393 PrincipalIdentity::User(user)
394 }
395}
396
397impl Debug for PrincipalIdentity {
398 fn fmt(&self, f: &mut Formatter) -> FmtResult {
399 match self {
400 PrincipalIdentity::AssumedRole(assumed_role) => f.debug_tuple("AssumedRole").field(assumed_role).finish(),
401 PrincipalIdentity::CanonicalUser(canonical_user) => {
402 f.debug_tuple("CanonicalUser").field(canonical_user).finish()
403 }
404 PrincipalIdentity::FederatedUser(federated_user) => {
405 f.debug_tuple("FederatedUser").field(federated_user).finish()
406 }
407 PrincipalIdentity::RootUser(root_user) => f.debug_tuple("RootUser").field(root_user).finish(),
408 PrincipalIdentity::Service(service) => f.debug_tuple("Service").field(service).finish(),
409 PrincipalIdentity::User(user) => f.debug_tuple("User").field(user).finish(),
410 }
411 }
412}
413
414impl Display for PrincipalIdentity {
415 fn fmt(&self, f: &mut Formatter) -> FmtResult {
416 match self {
417 Self::AssumedRole(ref inner) => Display::fmt(inner, f),
418 Self::CanonicalUser(ref inner) => Display::fmt(inner, f),
419 Self::FederatedUser(ref inner) => Display::fmt(inner, f),
420 Self::RootUser(ref inner) => Display::fmt(inner, f),
421 Self::Service(ref inner) => Display::fmt(inner, f),
422 Self::User(ref inner) => Display::fmt(inner, f),
423 }
424 }
425}
426
427impl TryFrom<&PrincipalIdentity> for Arn {
428 type Error = PrincipalError;
429 fn try_from(p: &PrincipalIdentity) -> Result<Arn, Self::Error> {
430 match p {
431 PrincipalIdentity::AssumedRole(ref d) => Ok(d.into()),
432 PrincipalIdentity::CanonicalUser(_) => Err(PrincipalError::CannotConvertToArn),
433 PrincipalIdentity::FederatedUser(ref d) => Ok(d.into()),
434 PrincipalIdentity::RootUser(ref d) => Ok(d.into()),
435 PrincipalIdentity::Service(_) => Err(PrincipalError::CannotConvertToArn),
436 PrincipalIdentity::User(ref d) => Ok(d.into()),
437 }
438 }
439}
440
441impl TryFrom<PrincipalIdentity> for Arn {
442 type Error = PrincipalError;
443 fn try_from(p: PrincipalIdentity) -> Result<Arn, Self::Error> {
444 match p {
445 PrincipalIdentity::AssumedRole(ref d) => Ok(d.into()),
446 PrincipalIdentity::CanonicalUser(_) => Err(PrincipalError::CannotConvertToArn),
447 PrincipalIdentity::FederatedUser(ref d) => Ok(d.into()),
448 PrincipalIdentity::RootUser(ref d) => Ok(d.into()),
449 PrincipalIdentity::Service(_) => Err(PrincipalError::CannotConvertToArn),
450 PrincipalIdentity::User(ref d) => Ok(d.into()),
451 }
452 }
453}
454
455#[cfg(test)]
456mod test {
457 use {
458 crate::{
459 AssumedRole, CanonicalUser, FederatedUser, Principal, PrincipalError, PrincipalIdentity, PrincipalSource,
460 RootUser, Service, User,
461 },
462 scratchstack_arn::Arn,
463 std::{
464 collections::hash_map::DefaultHasher,
465 hash::{Hash, Hasher},
466 io::Write,
467 str::FromStr,
468 },
469 };
470
471 #[test]
472 fn check_source_derived() {
473 let s1a = PrincipalSource::Aws;
474 let s1b = PrincipalSource::Aws;
475 let s2 = PrincipalSource::CanonicalUser;
476 let s3 = PrincipalSource::Federated;
477 let s4 = PrincipalSource::Service;
478
479 assert_eq!(s1a, s1b);
480 assert_ne!(s1a, s2);
481 assert_eq!(s1a.clone(), s1a);
482 assert_ne!(s1a, s3);
483 assert_ne!(s1a, s4);
484 assert_ne!(s2, s3);
485 assert_ne!(s2, s4);
486 assert_ne!(s3, s4);
487
488 let mut h1a = DefaultHasher::new();
490 let mut h1b = DefaultHasher::new();
491 let mut h2 = DefaultHasher::new();
492 s1a.hash(&mut h1a);
493 s1b.hash(&mut h1b);
494 s2.hash(&mut h2);
495 let hash1a = h1a.finish();
496 let hash1b = h1b.finish();
497 let hash2 = h2.finish();
498 assert_eq!(hash1a, hash1b);
499 assert_ne!(hash1a, hash2);
500
501 assert!(s1a <= s1b);
503 assert!(s1a < s2);
504 assert!(s2 < s3);
505 assert!(s3 < s4);
506 assert_eq!(s1a.max(s2), s2);
507 assert_eq!(s1a.min(s2), s1a);
508
509 let _ = format!("{s1a:?}");
511 }
512
513 #[test]
514 fn check_hash_ord() {
515 let p1 = PrincipalIdentity::from(AssumedRole::new("aws", "123456789012", "Role_name", "session_name").unwrap());
516 let p2 = PrincipalIdentity::from(
517 CanonicalUser::new("9da4bcba2132ad952bba3c8ecb37e668d99b310ce313da30c98aba4cdf009a7d").unwrap(),
518 );
519 let p3 = PrincipalIdentity::from(FederatedUser::new("aws", "123456789012", "user@domain").unwrap());
520 let p4 = PrincipalIdentity::from(RootUser::new("aws", "123456789012").unwrap());
521 let p5 = PrincipalIdentity::from(Service::new("service-name", None, "amazonaws.com").unwrap());
522 let p6 = PrincipalIdentity::from(User::new("aws", "123456789012", "/", "user-name").unwrap());
523
524 let mut h1 = DefaultHasher::new();
525 let mut h2 = DefaultHasher::new();
526 let mut h3 = DefaultHasher::new();
527 let mut h4 = DefaultHasher::new();
528 let mut h5 = DefaultHasher::new();
529 let mut h6 = DefaultHasher::new();
530 p1.hash(&mut h1);
531 p2.hash(&mut h2);
532 p3.hash(&mut h3);
533 p4.hash(&mut h4);
534 p5.hash(&mut h5);
535 p6.hash(&mut h6);
536 let hash1 = h1.finish();
537 let hash2 = h2.finish();
538 let hash3 = h3.finish();
539 let hash4 = h4.finish();
540 let hash5 = h5.finish();
541 let hash6 = h6.finish();
542 assert_ne!(hash1, hash2);
543 assert_ne!(hash1, hash3);
544 assert_ne!(hash1, hash4);
545 assert_ne!(hash1, hash5);
546 assert_ne!(hash1, hash6);
547 assert_ne!(hash2, hash3);
548 assert_ne!(hash2, hash4);
549 assert_ne!(hash2, hash5);
550 assert_ne!(hash2, hash6);
551 assert_ne!(hash3, hash4);
552 assert_ne!(hash3, hash5);
553 assert_ne!(hash3, hash6);
554 assert_ne!(hash4, hash5);
555 assert_ne!(hash4, hash6);
556 assert_ne!(hash5, hash6);
557
558 assert!(p1 < p2);
559 assert!(p1 < p3);
560 assert!(p1 < p4);
561 assert!(p1 < p5);
562 assert_eq!(p1.clone().max(p2.clone()), p2);
563 assert_eq!(p1.clone().min(p2), p1);
564 }
565
566 #[test]
567 fn check_assumed_role() {
568 let r1a = AssumedRole::new("aws", "123456789012", "Role_name", "session_name").unwrap();
569
570 let r1b = AssumedRole::new("aws", "123456789012", "Role_name", "session_name").unwrap();
571
572 let r2 =
573 AssumedRole::new("aws2", "123456789012", "Role@Foo=bar,baz_=world-1234", "Session@1234,_=-,.OK").unwrap();
574
575 let p1a = PrincipalIdentity::from(r1a);
576 let p1b = PrincipalIdentity::from(r1b);
577 let p2 = PrincipalIdentity::from(r2);
578
579 assert_eq!(p1a, p1b);
580 assert_ne!(p1a, p2);
581 assert_eq!(p1a, p1a.clone());
582
583 assert_eq!(p1a.to_string(), "arn:aws:sts::123456789012:assumed-role/Role_name/session_name");
584 assert_eq!(p1a.source(), PrincipalSource::Aws);
585 assert!(p1a.has_arn());
586
587 let _ = format!("{p1a:?}");
589
590 let arn: Arn = (&p1a).try_into().unwrap();
591 assert_eq!(arn.partition(), "aws");
592 assert_eq!(arn.service(), "sts");
593 assert_eq!(arn.region(), "");
594 assert_eq!(arn.account_id(), "123456789012");
595 assert_eq!(arn.resource(), "assumed-role/Role_name/session_name");
596
597 let arn: Arn = p1a.try_into().unwrap();
598 assert_eq!(arn.partition(), "aws");
599 assert_eq!(arn.service(), "sts");
600 assert_eq!(arn.region(), "");
601 assert_eq!(arn.account_id(), "123456789012");
602 assert_eq!(arn.resource(), "assumed-role/Role_name/session_name");
603 }
604
605 #[test]
606 fn check_canonical_user() {
607 let cu1a = CanonicalUser::new("9da4bcba2132ad952bba3c8ecb37e668d99b310ce313da30c98aba4cdf009a7d").unwrap();
608 let cu1b = CanonicalUser::new("9da4bcba2132ad952bba3c8ecb37e668d99b310ce313da30c98aba4cdf009a7d").unwrap();
609 let cu2 = CanonicalUser::new("772183b840c93fe103e45cd24ca8b8c94425a373465c6eb535b7c4b9593811e5").unwrap();
610
611 let p1a = PrincipalIdentity::from(cu1a);
612 let p1b = PrincipalIdentity::from(cu1b);
613 let p2 = PrincipalIdentity::from(cu2);
614
615 assert_eq!(p1a, p1b);
616 assert_ne!(p1a, p2);
617 assert_eq!(p1a, p1a.clone());
618
619 assert_eq!(p1a.to_string(), "9da4bcba2132ad952bba3c8ecb37e668d99b310ce313da30c98aba4cdf009a7d");
620 assert_eq!(p1a.source(), PrincipalSource::CanonicalUser);
621 assert!(!p1a.has_arn());
622
623 let _ = format!("{p1a:?}");
625
626 let err = TryInto::<Arn>::try_into(&p1a).unwrap_err();
627 assert_eq!(err, PrincipalError::CannotConvertToArn);
628 assert_eq!(err.to_string(), "Cannot convert entity to ARN");
629
630 let err = TryInto::<Arn>::try_into(p1a).unwrap_err();
631 assert_eq!(err, PrincipalError::CannotConvertToArn);
632 assert_eq!(err.to_string(), "Cannot convert entity to ARN");
633 }
634
635 #[test]
636 fn check_federated_user() {
637 let f1a = FederatedUser::new("aws", "123456789012", "user@domain").unwrap();
638 let f1b = FederatedUser::new("aws", "123456789012", "user@domain").unwrap();
639 let f2 = FederatedUser::new("partition-with-32-characters1234", "123456789012", "user@domain").unwrap();
640
641 let p1a = PrincipalIdentity::from(f1a);
642 let p1b = PrincipalIdentity::from(f1b);
643 let p2 = PrincipalIdentity::from(f2);
644
645 assert_eq!(p1a, p1b);
646 assert_ne!(p1a, p2);
647 assert_eq!(p1a, p1a.clone());
648
649 assert_eq!(p1a.to_string(), "arn:aws:sts::123456789012:federated-user/user@domain");
650 assert_eq!(p1a.source(), PrincipalSource::Federated);
651 assert!(p1a.has_arn());
652
653 let _ = format!("{p1a:?}");
655
656 let arn: Arn = (&p1a).try_into().unwrap();
657 assert_eq!(arn.partition(), "aws");
658 assert_eq!(arn.service(), "sts");
659 assert_eq!(arn.region(), "");
660 assert_eq!(arn.account_id(), "123456789012");
661 assert_eq!(arn.resource(), "federated-user/user@domain");
662
663 let arn: Arn = p1a.try_into().unwrap();
664 assert_eq!(arn.partition(), "aws");
665 assert_eq!(arn.service(), "sts");
666 assert_eq!(arn.region(), "");
667 assert_eq!(arn.account_id(), "123456789012");
668 assert_eq!(arn.resource(), "federated-user/user@domain");
669 }
670
671 #[test]
672 fn check_root_user() {
673 let r1a = RootUser::new("aws", "123456789012").unwrap();
674 let r1b = RootUser::new("aws", "123456789012").unwrap();
675 let r2 = RootUser::new("aws", "123456789099").unwrap();
676
677 let p1a = PrincipalIdentity::from(r1a);
678 let p1b = PrincipalIdentity::from(r1b);
679 let p2 = PrincipalIdentity::from(r2);
680
681 assert_eq!(p1a, p1b);
682 assert_ne!(p1a, p2);
683 assert_eq!(p1a, p1a.clone());
684
685 assert_eq!(p1a.to_string(), "123456789012");
686 assert_eq!(p1a.source(), PrincipalSource::Aws);
687 assert!(p1a.has_arn());
688
689 let _ = format!("{p1a:?}");
691
692 let arn: Arn = (&p1a).try_into().unwrap();
693 assert_eq!(arn.partition(), "aws");
694 assert_eq!(arn.service(), "iam");
695 assert_eq!(arn.region(), "");
696 assert_eq!(arn.account_id(), "123456789012");
697 assert_eq!(arn.resource(), "root");
698
699 let arn: Arn = p1a.try_into().unwrap();
700 assert_eq!(arn.partition(), "aws");
701 assert_eq!(arn.service(), "iam");
702 assert_eq!(arn.region(), "");
703 assert_eq!(arn.account_id(), "123456789012");
704 assert_eq!(arn.resource(), "root");
705 }
706
707 #[test]
708 fn check_service() {
709 let s1a = Service::new("service-name", None, "amazonaws.com").unwrap();
710 let s1b = Service::new("service-name", None, "amazonaws.com").unwrap();
711 let s2 = Service::new("service-name2", None, "amazonaws.com").unwrap();
712
713 let p1a = PrincipalIdentity::from(s1a);
714 let p1b = PrincipalIdentity::from(s1b);
715 let p2 = PrincipalIdentity::from(s2);
716
717 assert_eq!(p1a, p1b);
718 assert_ne!(p1a, p2);
719 assert_eq!(p1a, p1a.clone());
720
721 assert_eq!(p1a.to_string(), "service-name.amazonaws.com");
722 assert_eq!(p1a.source(), PrincipalSource::Service);
723 assert!(!p1a.has_arn());
724
725 let _ = format!("{p1a:?}");
727
728 let err = TryInto::<Arn>::try_into(&p1a).unwrap_err();
729 assert_eq!(err, PrincipalError::CannotConvertToArn);
730
731 let err = TryInto::<Arn>::try_into(p1a).unwrap_err();
732 assert_eq!(err, PrincipalError::CannotConvertToArn);
733 }
734
735 #[test]
736 fn check_user() {
737 let u1a = User::new("aws", "123456789012", "/", "user-name").unwrap();
738 let u1b = User::new("aws", "123456789012", "/", "user-name").unwrap();
739 let u2 = User::new("aws", "123456789012", "/", "user-name2").unwrap();
740
741 let p1a = PrincipalIdentity::from(u1a);
742 let p1b = PrincipalIdentity::from(u1b);
743 let p2 = PrincipalIdentity::from(u2);
744
745 assert_eq!(p1a, p1b);
746 assert_ne!(p1a, p2);
747 assert_eq!(p1a, p1a.clone());
748
749 assert_eq!(p1a.to_string(), "arn:aws:iam::123456789012:user/user-name");
750 assert_eq!(p1a.source(), PrincipalSource::Aws);
751 assert!(p1a.has_arn());
752
753 let _ = format!("{p1a:?}");
755
756 let arn: Arn = (&p1a).try_into().unwrap();
757 assert_eq!(arn.partition(), "aws");
758 assert_eq!(arn.service(), "iam");
759 assert_eq!(arn.region(), "");
760 assert_eq!(arn.account_id(), "123456789012");
761 assert_eq!(arn.resource(), "user/user-name");
762
763 let arn: Arn = p1a.try_into().unwrap();
764 assert_eq!(arn.partition(), "aws");
765 assert_eq!(arn.service(), "iam");
766 assert_eq!(arn.region(), "");
767 assert_eq!(arn.account_id(), "123456789012");
768 assert_eq!(arn.resource(), "user/user-name");
769 }
770
771 #[test]
772 fn check_principal_basics() {
773 let ar1a = AssumedRole::new("aws", "123456789012", "Role_name", "session_name").unwrap();
774 let ar1b = AssumedRole::new("aws", "123456789012", "Role_name", "session_name").unwrap();
775 let cu1a = CanonicalUser::new("9da4bcba2132ad952bba3c8ecb37e668d99b310ce313da30c98aba4cdf009a7d").unwrap();
776 let cu1b = CanonicalUser::new("9da4bcba2132ad952bba3c8ecb37e668d99b310ce313da30c98aba4cdf009a7d").unwrap();
777 let f1a = FederatedUser::new("aws", "123456789012", "user@domain").unwrap();
778 let f1b = FederatedUser::new("aws", "123456789012", "user@domain").unwrap();
779 let r1a = RootUser::new("aws", "123456789012").unwrap();
780 let r1b = RootUser::new("aws", "123456789012").unwrap();
781 let s1a = Service::new("service-name", None, "amazonaws.com").unwrap();
782 let s1b = Service::new("service-name", None, "amazonaws.com").unwrap();
783 let u1a = User::new("aws", "123456789012", "/", "user-name").unwrap();
784 let u1b = User::new("aws", "123456789012", "/", "user-name").unwrap();
785 let u2 = User::new("aws", "123456789012", "/", "user-name2").unwrap();
786
787 let principals1a = vec![
788 PrincipalIdentity::from(ar1a.clone()),
789 PrincipalIdentity::from(cu1a.clone()),
790 PrincipalIdentity::from(f1a.clone()),
791 PrincipalIdentity::from(r1a.clone()),
792 PrincipalIdentity::from(s1a.clone()),
793 PrincipalIdentity::from(u1a.clone()),
794 PrincipalIdentity::from(ar1b),
795 PrincipalIdentity::from(cu1b),
796 PrincipalIdentity::from(f1b),
797 PrincipalIdentity::from(r1b),
798 PrincipalIdentity::from(s1b),
799 PrincipalIdentity::from(u1b),
800 ];
801 let principals1b = vec![
802 PrincipalIdentity::from(u1a.clone()),
803 PrincipalIdentity::from(s1a.clone()),
804 PrincipalIdentity::from(r1a.clone()),
805 PrincipalIdentity::from(f1a.clone()),
806 PrincipalIdentity::from(cu1a.clone()),
807 PrincipalIdentity::from(ar1a.clone()),
808 ];
809 let principals2 = vec![
810 PrincipalIdentity::from(u1a.clone()),
811 PrincipalIdentity::from(s1a.clone()),
812 PrincipalIdentity::from(r1a.clone()),
813 PrincipalIdentity::from(f1a.clone()),
814 PrincipalIdentity::from(cu1a.clone()),
815 PrincipalIdentity::from(ar1a.clone()),
816 PrincipalIdentity::from(u2.clone()),
817 ];
818
819 let p1a = Principal::from(&principals1a);
820 let p1b = Principal::from(principals1b);
821 let mut p2 = Principal::from(principals2.as_slice());
822 let mut p3 = Principal::with_capacity(p1a.len());
823
824 assert!(!p1a.is_empty());
825 assert!(p3.is_empty());
826
827 assert_eq!(p1a, p1b);
828 assert_eq!(p1a, p1a.clone());
829 assert_ne!(p1a, p2);
830
831 assert_eq!(p1a.as_slice(), p1b.as_slice());
832
833 let p1adebug = format!("{p1a:?}");
834 let p1bdebug = format!("{p1b:?}");
835 assert_eq!(p1adebug, p1bdebug);
836
837 let p1adisplay = format!("{p1a}");
838 let p1bdisplay = format!("{p1b}");
839 assert_eq!(p1adisplay, p1bdisplay);
840
841 let mut p1a_emptied = p1a.clone();
842 p1a_emptied.clear();
843 assert_eq!(p1a_emptied, p3);
844
845 let p2b_slice: &[PrincipalIdentity] = p1b.as_ref();
846 for identity in p1a.iter() {
847 assert!(p2b_slice.contains(identity));
848 }
849
850 p3.add(u1a.into());
851 p3.add(s1a.into());
852 p3.add(r1a.into());
853 p3.add(f1a.into());
854 p3.add(cu1a.into());
855 p3.add(ar1a.into());
856
857 assert_eq!(p3, p1a);
858
859 let mut h1a = DefaultHasher::new();
861 let mut h1b = DefaultHasher::new();
862 let mut h2 = DefaultHasher::new();
863 p1a.hash(&mut h1a);
864 p1b.hash(&mut h1b);
865 p2.hash(&mut h2);
866 let hash1a = h1a.finish();
867 let hash1b = h1b.finish();
868 let hash2 = h2.finish();
869 assert_eq!(hash1a, hash1b);
870 assert_ne!(hash1a, hash2);
871
872 p2.remove(p2.len() - 1);
873 assert_eq!(p1a, p2);
874
875 p2.add(u2.into());
876 assert_ne!(p1a, p2);
877 p2.truncate(p2.len() - 1);
878 assert_eq!(p1a, p2);
879
880 (&p1a).into_iter().for_each(|identity| {
881 assert!(p1b.contains(identity));
882 });
883
884 p1a.into_iter().for_each(|identity| {
885 assert!(p1b.contains(&identity));
886 });
887 }
888
889 #[test]
890 fn failing_principal_display() {
891 let u1 = User::new("aws", "123456789012", "/", "user-name").unwrap();
892 let u2 = User::new("aws", "123456789012", "/", "user-name2").unwrap();
893
894 for bufsize in 0..94 {
896 let mut buf = vec![0u8; bufsize];
897 let mut p: Principal = Default::default();
898 p.add(u1.clone().into());
899 p.add(u2.clone().into());
900 write!(buf.as_mut_slice(), "{p}").unwrap_err();
901 }
902 }
903
904 #[test]
905 fn test_conversions() {
906 let p = Principal::from(
907 AssumedRole::from_str("arn:aws:sts::123456789012:assumed-role/role-name/session-name").unwrap(),
908 );
909 assert_eq!(p.len(), 1);
910 assert!(p[0].as_assumed_role().is_some());
911 assert!(p[0].as_canonical_user().is_none());
912 assert!(p[0].as_federated_user().is_none());
913 assert!(p[0].as_root_user().is_none());
914 assert!(p[0].as_service().is_none());
915 assert!(p[0].as_user().is_none());
916
917 let p = Principal::from(
918 CanonicalUser::new("9da4bcba2132ad952bba3c8ecb37e668d99b310ce313da30c98aba4cdf009a7d").unwrap(),
919 );
920 assert_eq!(p.len(), 1);
921 assert!(p[0].as_assumed_role().is_none());
922 assert!(p[0].as_canonical_user().is_some());
923 assert!(p[0].as_federated_user().is_none());
924 assert!(p[0].as_root_user().is_none());
925 assert!(p[0].as_service().is_none());
926 assert!(p[0].as_user().is_none());
927
928 let p = Principal::from(FederatedUser::new("aws", "123456789012", "dacut@kanga.org").unwrap());
929 assert_eq!(p.len(), 1);
930 assert!(p[0].as_assumed_role().is_none());
931 assert!(p[0].as_canonical_user().is_none());
932 assert!(p[0].as_federated_user().is_some());
933 assert!(p[0].as_root_user().is_none());
934 assert!(p[0].as_service().is_none());
935 assert!(p[0].as_user().is_none());
936
937 let p = Principal::from(RootUser::new("aws", "123456789012").unwrap());
938 assert_eq!(p.len(), 1);
939 assert!(p[0].as_assumed_role().is_none());
940 assert!(p[0].as_canonical_user().is_none());
941 assert!(p[0].as_federated_user().is_none());
942 assert!(p[0].as_root_user().is_some());
943 assert!(p[0].as_service().is_none());
944 assert!(p[0].as_user().is_none());
945
946 let p = Principal::from(Service::new("ec2", Some("us-west-2".to_string()), "amazonaws.com").unwrap());
947 assert_eq!(p.len(), 1);
948 assert!(p[0].as_assumed_role().is_none());
949 assert!(p[0].as_canonical_user().is_none());
950 assert!(p[0].as_federated_user().is_none());
951 assert!(p[0].as_root_user().is_none());
952 assert!(p[0].as_service().is_some());
953 assert!(p[0].as_user().is_none());
954
955 let p = Principal::from(User::from_str("arn:aws:iam::123456789012:user/user-name").unwrap());
956 assert_eq!(p.len(), 1);
957 assert!(p[0].as_assumed_role().is_none());
958 assert!(p[0].as_canonical_user().is_none());
959 assert!(p[0].as_federated_user().is_none());
960 assert!(p[0].as_root_user().is_none());
961 assert!(p[0].as_service().is_none());
962 assert!(p[0].as_user().is_some());
963
964 let p = Principal::from(vec![
965 PrincipalIdentity::parse_arn("arn:aws:sts::123456789012:assumed-role/role-name/session-name").unwrap(),
966 PrincipalIdentity::parse_arn("arn:aws:iam::123456789012:user/user-name").unwrap(),
967 ]);
968 assert_eq!(p.len(), 2);
969 }
970
971 #[test]
972 fn test_invalid_arns() {
973 let e =
974 PrincipalIdentity::parse_arn("arn:-aws:sts::123456789012:assumed-role/role-name/session-name").unwrap_err();
975 assert_eq!(e.to_string(), r#"Invalid partition: "-aws""#);
976
977 let e = PrincipalIdentity::parse_arn("arn:aws:sts:us-west-1:123456789012:assumed-role/role-name/session-name")
978 .unwrap_err();
979 assert_eq!(e.to_string(), r#"Invalid region: "us-west-1""#);
980
981 let e = PrincipalIdentity::parse_arn("arn:aws:sts::123456789012:role/role-name/session-name").unwrap_err();
982 assert_eq!(e.to_string(), r#"Invalid ARN: "arn:aws:sts::123456789012:role/role-name/session-name""#);
983
984 let e = PrincipalIdentity::parse_arn("arn:aws:iam:us-west-1:123456789012:user/path/user-name").unwrap_err();
985 assert_eq!(e.to_string(), r#"Invalid region: "us-west-1""#);
986
987 let e = PrincipalIdentity::parse_arn("arn:aws:iam::123456789012:role/role-name/session-name").unwrap_err();
988 assert_eq!(e.to_string(), r#"Invalid ARN: "arn:aws:iam::123456789012:role/role-name/session-name""#);
989
990 let e = PrincipalIdentity::parse_arn("arn:aws:s3::123456789012:role/role-name").unwrap_err();
991 assert_eq!(e.to_string(), r#"Invalid ARN: "arn:aws:s3::123456789012:role/role-name""#);
992 }
993}