mas_data_model/
users.rs

1// Copyright 2024 New Vector Ltd.
2// Copyright 2021-2024 The Matrix.org Foundation C.I.C.
3//
4// SPDX-License-Identifier: AGPL-3.0-only
5// Please see LICENSE in the repository root for full details.
6
7use std::net::IpAddr;
8
9use chrono::{DateTime, Utc};
10use rand::Rng;
11use serde::Serialize;
12use ulid::Ulid;
13use url::Url;
14
15use crate::UserAgent;
16
17#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
18pub struct User {
19    pub id: Ulid,
20    pub username: String,
21    pub sub: String,
22    pub created_at: DateTime<Utc>,
23    pub locked_at: Option<DateTime<Utc>>,
24    pub deactivated_at: Option<DateTime<Utc>>,
25    pub can_request_admin: bool,
26}
27
28impl User {
29    /// Returns `true` unless the user is locked or deactivated.
30    #[must_use]
31    pub fn is_valid(&self) -> bool {
32        self.locked_at.is_none() && self.deactivated_at.is_none()
33    }
34}
35
36impl User {
37    #[doc(hidden)]
38    #[must_use]
39    pub fn samples(now: chrono::DateTime<Utc>, rng: &mut impl Rng) -> Vec<Self> {
40        vec![User {
41            id: Ulid::from_datetime_with_source(now.into(), rng),
42            username: "john".to_owned(),
43            sub: "123-456".to_owned(),
44            created_at: now,
45            locked_at: None,
46            deactivated_at: None,
47            can_request_admin: false,
48        }]
49    }
50}
51
52#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
53pub struct Password {
54    pub id: Ulid,
55    pub hashed_password: String,
56    pub version: u16,
57    pub upgraded_from_id: Option<Ulid>,
58    pub created_at: DateTime<Utc>,
59}
60
61#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
62pub struct Authentication {
63    pub id: Ulid,
64    pub created_at: DateTime<Utc>,
65    pub authentication_method: AuthenticationMethod,
66}
67
68#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
69pub enum AuthenticationMethod {
70    Password { user_password_id: Ulid },
71    UpstreamOAuth2 { upstream_oauth2_session_id: Ulid },
72    Unknown,
73}
74
75/// A session to recover a user if they have lost their credentials
76///
77/// For each session intiated, there may be multiple [`UserRecoveryTicket`]s
78/// sent to the user, either because multiple [`User`] have the same email
79/// address, or because the user asked to send the recovery email again.
80#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
81pub struct UserRecoverySession {
82    pub id: Ulid,
83    pub email: String,
84    pub user_agent: UserAgent,
85    pub ip_address: Option<IpAddr>,
86    pub locale: String,
87    pub created_at: DateTime<Utc>,
88    pub consumed_at: Option<DateTime<Utc>>,
89}
90
91/// A single recovery ticket for a user recovery session
92///
93/// Whenever a new recovery session is initiated, a new ticket is created for
94/// each email address matching in the database. That ticket is sent by email,
95/// as a link that the user can click to recover their account.
96#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
97pub struct UserRecoveryTicket {
98    pub id: Ulid,
99    pub user_recovery_session_id: Ulid,
100    pub user_email_id: Ulid,
101    pub ticket: String,
102    pub created_at: DateTime<Utc>,
103    pub expires_at: DateTime<Utc>,
104}
105
106impl UserRecoveryTicket {
107    #[must_use]
108    pub fn active(&self, now: DateTime<Utc>) -> bool {
109        now < self.expires_at
110    }
111}
112
113/// A user email authentication session
114#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
115pub struct UserEmailAuthentication {
116    pub id: Ulid,
117    pub user_session_id: Option<Ulid>,
118    pub user_registration_id: Option<Ulid>,
119    pub email: String,
120    pub created_at: DateTime<Utc>,
121    pub completed_at: Option<DateTime<Utc>>,
122}
123
124/// A user email authentication code
125#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
126pub struct UserEmailAuthenticationCode {
127    pub id: Ulid,
128    pub user_email_authentication_id: Ulid,
129    pub code: String,
130    pub created_at: DateTime<Utc>,
131    pub expires_at: DateTime<Utc>,
132}
133
134#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
135pub struct BrowserSession {
136    pub id: Ulid,
137    pub user: User,
138    pub created_at: DateTime<Utc>,
139    pub finished_at: Option<DateTime<Utc>>,
140    pub user_agent: Option<UserAgent>,
141    pub last_active_at: Option<DateTime<Utc>>,
142    pub last_active_ip: Option<IpAddr>,
143}
144
145impl BrowserSession {
146    #[must_use]
147    pub fn active(&self) -> bool {
148        self.finished_at.is_none() && self.user.is_valid()
149    }
150}
151
152impl BrowserSession {
153    #[must_use]
154    pub fn samples(now: chrono::DateTime<Utc>, rng: &mut impl Rng) -> Vec<Self> {
155        User::samples(now, rng)
156            .into_iter()
157            .map(|user| BrowserSession {
158                id: Ulid::from_datetime_with_source(now.into(), rng),
159                user,
160                created_at: now,
161                finished_at: None,
162                user_agent: Some(UserAgent::parse(
163                    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.0.0 Safari/537.36".to_owned()
164                )),
165                last_active_at: Some(now),
166                last_active_ip: None,
167            })
168            .collect()
169    }
170}
171
172#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
173pub struct UserEmail {
174    pub id: Ulid,
175    pub user_id: Ulid,
176    pub email: String,
177    pub created_at: DateTime<Utc>,
178}
179
180impl UserEmail {
181    #[must_use]
182    pub fn samples(now: chrono::DateTime<Utc>, rng: &mut impl Rng) -> Vec<Self> {
183        vec![
184            Self {
185                id: Ulid::from_datetime_with_source(now.into(), rng),
186                user_id: Ulid::from_datetime_with_source(now.into(), rng),
187                email: "alice@example.com".to_owned(),
188                created_at: now,
189            },
190            Self {
191                id: Ulid::from_datetime_with_source(now.into(), rng),
192                user_id: Ulid::from_datetime_with_source(now.into(), rng),
193                email: "bob@example.com".to_owned(),
194                created_at: now,
195            },
196        ]
197    }
198}
199
200#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
201pub struct UserRegistrationPassword {
202    pub hashed_password: String,
203    pub version: u16,
204}
205
206#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
207pub struct UserRegistration {
208    pub id: Ulid,
209    pub username: String,
210    pub display_name: Option<String>,
211    pub terms_url: Option<Url>,
212    pub email_authentication_id: Option<Ulid>,
213    pub password: Option<UserRegistrationPassword>,
214    pub post_auth_action: Option<serde_json::Value>,
215    pub ip_address: Option<IpAddr>,
216    pub user_agent: Option<UserAgent>,
217    pub created_at: DateTime<Utc>,
218    pub completed_at: Option<DateTime<Utc>>,
219}