1use std::net::IpAddr;
8
9use chrono::{DateTime, Utc};
10use rand::Rng;
11use serde::Serialize;
12use ulid::Ulid;
13use url::Url;
14
15#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
16pub struct User {
17 pub id: Ulid,
18 pub username: String,
19 pub sub: String,
20 pub created_at: DateTime<Utc>,
21 pub locked_at: Option<DateTime<Utc>>,
22 pub deactivated_at: Option<DateTime<Utc>>,
23 pub can_request_admin: bool,
24 pub is_guest: bool,
25}
26
27impl User {
28 #[must_use]
30 pub fn is_valid(&self) -> bool {
31 self.locked_at.is_none() && self.deactivated_at.is_none()
32 }
33
34 #[must_use]
44 pub fn is_valid_actor(&self) -> bool {
45 self.deactivated_at.is_none()
46 }
47}
48
49impl User {
50 #[doc(hidden)]
51 #[must_use]
52 pub fn samples(now: chrono::DateTime<Utc>, rng: &mut impl Rng) -> Vec<Self> {
53 vec![User {
54 id: Ulid::from_datetime_with_source(now.into(), rng),
55 username: "john".to_owned(),
56 sub: "123-456".to_owned(),
57 created_at: now,
58 locked_at: None,
59 deactivated_at: None,
60 can_request_admin: false,
61 is_guest: false,
62 }]
63 }
64}
65
66#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
67pub struct Password {
68 pub id: Ulid,
69 pub hashed_password: String,
70 pub version: u16,
71 pub upgraded_from_id: Option<Ulid>,
72 pub created_at: DateTime<Utc>,
73}
74
75#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
76pub struct Authentication {
77 pub id: Ulid,
78 pub created_at: DateTime<Utc>,
79 pub authentication_method: AuthenticationMethod,
80}
81
82#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
83pub enum AuthenticationMethod {
84 Password { user_password_id: Ulid },
85 UpstreamOAuth2 { upstream_oauth2_session_id: Ulid },
86 Unknown,
87}
88
89#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
95pub struct UserRecoverySession {
96 pub id: Ulid,
97 pub email: String,
98 pub user_agent: String,
99 pub ip_address: Option<IpAddr>,
100 pub locale: String,
101 pub created_at: DateTime<Utc>,
102 pub consumed_at: Option<DateTime<Utc>>,
103}
104
105#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
111pub struct UserRecoveryTicket {
112 pub id: Ulid,
113 pub user_recovery_session_id: Ulid,
114 pub user_email_id: Ulid,
115 pub ticket: String,
116 pub created_at: DateTime<Utc>,
117 pub expires_at: DateTime<Utc>,
118}
119
120impl UserRecoveryTicket {
121 #[must_use]
122 pub fn active(&self, now: DateTime<Utc>) -> bool {
123 now < self.expires_at
124 }
125}
126
127#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
129pub struct UserEmailAuthentication {
130 pub id: Ulid,
131 pub user_session_id: Option<Ulid>,
132 pub user_registration_id: Option<Ulid>,
133 pub email: String,
134 pub created_at: DateTime<Utc>,
135 pub completed_at: Option<DateTime<Utc>>,
136}
137
138#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
140pub struct UserEmailAuthenticationCode {
141 pub id: Ulid,
142 pub user_email_authentication_id: Ulid,
143 pub code: String,
144 pub created_at: DateTime<Utc>,
145 pub expires_at: DateTime<Utc>,
146}
147
148#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
149pub struct BrowserSession {
150 pub id: Ulid,
151 pub user: User,
152 pub created_at: DateTime<Utc>,
153 pub finished_at: Option<DateTime<Utc>>,
154 pub user_agent: Option<String>,
155 pub last_active_at: Option<DateTime<Utc>>,
156 pub last_active_ip: Option<IpAddr>,
157}
158
159impl BrowserSession {
160 #[must_use]
161 pub fn active(&self) -> bool {
162 self.finished_at.is_none() && self.user.is_valid()
163 }
164}
165
166impl BrowserSession {
167 #[must_use]
168 pub fn samples(now: chrono::DateTime<Utc>, rng: &mut impl Rng) -> Vec<Self> {
169 User::samples(now, rng)
170 .into_iter()
171 .map(|user| BrowserSession {
172 id: Ulid::from_datetime_with_source(now.into(), rng),
173 user,
174 created_at: now,
175 finished_at: None,
176 user_agent: Some(
177 "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()
178 ),
179 last_active_at: Some(now),
180 last_active_ip: None,
181 })
182 .collect()
183 }
184}
185
186#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
187pub struct UserEmail {
188 pub id: Ulid,
189 pub user_id: Ulid,
190 pub email: String,
191 pub created_at: DateTime<Utc>,
192}
193
194impl UserEmail {
195 #[must_use]
196 pub fn samples(now: chrono::DateTime<Utc>, rng: &mut impl Rng) -> Vec<Self> {
197 vec![
198 Self {
199 id: Ulid::from_datetime_with_source(now.into(), rng),
200 user_id: Ulid::from_datetime_with_source(now.into(), rng),
201 email: "alice@example.com".to_owned(),
202 created_at: now,
203 },
204 Self {
205 id: Ulid::from_datetime_with_source(now.into(), rng),
206 user_id: Ulid::from_datetime_with_source(now.into(), rng),
207 email: "bob@example.com".to_owned(),
208 created_at: now,
209 },
210 ]
211 }
212}
213
214#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
215pub struct UserRegistrationPassword {
216 pub hashed_password: String,
217 pub version: u16,
218}
219
220#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
221pub struct UserRegistrationToken {
222 pub id: Ulid,
223 pub token: String,
224 pub usage_limit: Option<u32>,
225 pub times_used: u32,
226 pub created_at: DateTime<Utc>,
227 pub last_used_at: Option<DateTime<Utc>>,
228 pub expires_at: Option<DateTime<Utc>>,
229 pub revoked_at: Option<DateTime<Utc>>,
230}
231
232impl UserRegistrationToken {
233 #[must_use]
235 pub fn is_valid(&self, now: DateTime<Utc>) -> bool {
236 if self.revoked_at.is_some() {
238 return false;
239 }
240
241 if let Some(expires_at) = self.expires_at
243 && now >= expires_at
244 {
245 return false;
246 }
247
248 if let Some(usage_limit) = self.usage_limit
250 && self.times_used >= usage_limit
251 {
252 return false;
253 }
254
255 true
256 }
257
258 #[must_use]
261 pub fn can_be_used(&self, now: DateTime<Utc>) -> bool {
262 self.is_valid(now)
263 }
264}
265
266#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
267pub struct UserRegistration {
268 pub id: Ulid,
269 pub username: String,
270 pub display_name: Option<String>,
271 pub terms_url: Option<Url>,
272 pub email_authentication_id: Option<Ulid>,
273 pub user_registration_token_id: Option<Ulid>,
274 pub password: Option<UserRegistrationPassword>,
275 pub post_auth_action: Option<serde_json::Value>,
276 pub ip_address: Option<IpAddr>,
277 pub user_agent: Option<String>,
278 pub created_at: DateTime<Utc>,
279 pub completed_at: Option<DateTime<Utc>>,
280}