mas_handlers/views/register/
cookie.rs1use std::collections::BTreeSet;
9
10use chrono::{DateTime, Duration, Utc};
11use mas_axum_utils::cookies::CookieJar;
12use mas_data_model::{Clock, UserRegistration};
13use serde::{Deserialize, Serialize};
14use thiserror::Error;
15use ulid::Ulid;
16
17static COOKIE_NAME: &str = "user-registration-sessions";
19
20static SESSION_MAX_TIME: Duration = Duration::hours(1);
22
23#[derive(Serialize, Deserialize, Default, Debug)]
25pub struct UserRegistrationSessions(BTreeSet<Ulid>);
26
27#[derive(Debug, Error, PartialEq, Eq)]
28#[error("user registration session not found")]
29pub struct UserRegistrationSessionNotFound;
30
31impl UserRegistrationSessions {
32 pub fn load(cookie_jar: &CookieJar) -> Self {
34 match cookie_jar.load(COOKIE_NAME) {
35 Ok(Some(sessions)) => sessions,
36 Ok(None) => Self::default(),
37 Err(e) => {
38 tracing::warn!(
39 error = &e as &dyn std::error::Error,
40 "Invalid upstream sessions cookie"
41 );
42 Self::default()
43 }
44 }
45 }
46
47 pub fn is_empty(&self) -> bool {
49 self.0.is_empty()
50 }
51
52 pub fn save<C>(self, cookie_jar: CookieJar, clock: &C) -> CookieJar
54 where
55 C: Clock,
56 {
57 let this = self.expire(clock.now());
58
59 if this.is_empty() {
60 cookie_jar.remove(COOKIE_NAME)
61 } else {
62 cookie_jar.save(COOKIE_NAME, &this, false)
63 }
64 }
65
66 fn expire(mut self, now: DateTime<Utc>) -> Self {
67 self.0.retain(|id| {
68 let Ok(ts) = id.timestamp_ms().try_into() else {
69 return false;
70 };
71 let Some(when) = DateTime::from_timestamp_millis(ts) else {
72 return false;
73 };
74 now - when < SESSION_MAX_TIME
75 });
76
77 self
78 }
79
80 pub fn add(mut self, user_registration: &UserRegistration) -> Self {
82 self.0.insert(user_registration.id);
83 self
84 }
85
86 pub fn contains(&self, user_registration: &UserRegistration) -> bool {
88 self.0.contains(&user_registration.id)
89 }
90
91 pub fn consume_session(
93 mut self,
94 user_registration: &UserRegistration,
95 ) -> Result<Self, UserRegistrationSessionNotFound> {
96 if !self.0.remove(&user_registration.id) {
97 return Err(UserRegistrationSessionNotFound);
98 }
99
100 Ok(self)
101 }
102}