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