| 1 | use super::UserId; |
| 2 | |
| 3 | /// Who is performing an operation. |
| 4 | /// |
| 5 | /// Every use case takes one. Anonymous is a variant rather than an absent value, so a |
| 6 | /// caller cannot forget to model it — the previous attempt used a placeholder |
| 7 | /// `UserId("ssh-anonymous")` for this and it became a security hole. |
| 8 | #[derive(Debug, Clone, PartialEq, Eq)] |
| 9 | pub enum Actor { |
| 10 | /// Nobody is signed in. Public content is still readable. |
| 11 | Anonymous, |
| 12 | /// An authenticated user. |
| 13 | User(UserId), |
| 14 | } |
| 15 | |
| 16 | impl Actor { |
| 17 | /// The acting user, or `None` when anonymous. |
| 18 | pub fn user_id(&self) -> Option<&UserId> { |
| 19 | match self { |
| 20 | Self::Anonymous => None, |
| 21 | Self::User(id) => Some(id), |
| 22 | } |
| 23 | } |
| 24 | |
| 25 | pub fn is_authenticated(&self) -> bool { |
| 26 | matches!(self, Self::User(_)) |
| 27 | } |
| 28 | } |
| 29 | |
| 30 | #[cfg(test)] |
| 31 | mod tests { |
| 32 | use super::*; |
| 33 | |
| 34 | #[test] |
| 35 | fn anonymous_actors_carry_no_user() { |
| 36 | let actor = Actor::Anonymous; |
| 37 | |
| 38 | assert_eq!(actor.user_id(), None); |
| 39 | assert!(!actor.is_authenticated()); |
| 40 | } |
| 41 | |
| 42 | #[test] |
| 43 | fn authenticated_actors_expose_their_user() { |
| 44 | let id = UserId::generate(); |
| 45 | let actor = Actor::User(id.clone()); |
| 46 | |
| 47 | assert_eq!(actor.user_id(), Some(&id)); |
| 48 | assert!(actor.is_authenticated()); |
| 49 | } |
| 50 | } |