@jpgilldev / steid

steid/src/domain/actor.rs
1.2 KBRaw
1use 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)]
9pub enum Actor {
10 /// Nobody is signed in. Public content is still readable.
11 Anonymous,
12 /// An authenticated user.
13 User(UserId),
14}
15
16impl 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)]
31mod 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}