1use 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 #[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#[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#[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#[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#[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}