torii_core/
user.rs

1//! User management and authentication
2//!
3//! This module contains the core user struct and related functionality.
4//!
5//! Users are the core of the authentication system. They are responsible for storing user information and are used to identify users in the system. The core user struct is defined as follows:
6//!
7//! | Field               | Type               | Description                                       |
8//! | ------------------- | ------------------ | ------------------------------------------------- |
9//! | `id`                | `String`           | The unique identifier for the user.               |
10//! | `name`              | `String`           | The name of the user.                             |
11//! | `email`             | `String`           | The email of the user.                            |
12//! | `email_verified_at` | `Option<DateTime>` | The timestamp when the user's email was verified. |
13//! | `created_at`        | `DateTime`         | The timestamp when the user was created.          |
14//! | `updated_at`        | `DateTime`         | The timestamp when the user was last updated.     |
15use chrono::{DateTime, Utc};
16use serde::{Deserialize, Serialize};
17use uuid::Uuid;
18
19use crate::{Error, error::ValidationError};
20
21/// A unique, stable identifier for a specific user
22#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)]
23pub struct UserId(String);
24
25impl UserId {
26    pub fn new(id: &str) -> Self {
27        UserId(id.to_string())
28    }
29
30    pub fn new_random() -> Self {
31        UserId(Uuid::new_v4().to_string())
32    }
33
34    pub fn into_inner(self) -> String {
35        self.0
36    }
37
38    pub fn as_uuid(&self) -> Uuid {
39        Uuid::parse_str(&self.0).unwrap()
40    }
41
42    pub fn as_str(&self) -> &str {
43        &self.0
44    }
45}
46
47impl Default for UserId {
48    fn default() -> Self {
49        Self::new_random()
50    }
51}
52
53impl From<String> for UserId {
54    fn from(s: String) -> Self {
55        Self(s)
56    }
57}
58
59impl From<&str> for UserId {
60    fn from(s: &str) -> Self {
61        Self(s.to_string())
62    }
63}
64
65impl std::fmt::Display for UserId {
66    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67        write!(f, "{}", self.0)
68    }
69}
70
71/// Representation of a user in Torii. This is the user object returned by all authentication plugins.
72///
73/// Many of these fields are optional, as they may not be available from the authentication provider,
74/// or may not be known at the time of authentication.
75#[derive(Debug, Clone, Serialize, Deserialize)]
76pub struct User {
77    // The unique identifier for the user.
78    pub id: UserId,
79
80    // The name of the user.
81    pub name: Option<String>,
82
83    // The email of the user.
84    pub email: String,
85
86    // The email verified at timestamp. If the user has not verified their email, this will be None.
87    pub email_verified_at: Option<DateTime<Utc>>,
88
89    // The created at timestamp.
90    pub created_at: DateTime<Utc>,
91
92    // The updated at timestamp.
93    pub updated_at: DateTime<Utc>,
94}
95
96impl User {
97    pub fn builder() -> UserBuilder {
98        UserBuilder::default()
99    }
100
101    /// Check if the user's email has been verified.
102    pub fn is_email_verified(&self) -> bool {
103        self.email_verified_at.is_some() && self.email_verified_at.unwrap() < Utc::now()
104    }
105}
106
107#[derive(Default)]
108pub struct UserBuilder {
109    id: Option<UserId>,
110    name: Option<String>,
111    email: Option<String>,
112    email_verified_at: Option<DateTime<Utc>>,
113    created_at: Option<DateTime<Utc>>,
114    updated_at: Option<DateTime<Utc>>,
115}
116
117impl UserBuilder {
118    pub fn id(mut self, id: UserId) -> Self {
119        self.id = Some(id);
120        self
121    }
122
123    pub fn name(mut self, name: Option<String>) -> Self {
124        self.name = name;
125        self
126    }
127
128    pub fn email(mut self, email: String) -> Self {
129        self.email = Some(email);
130        self
131    }
132
133    pub fn email_verified_at(mut self, email_verified_at: Option<DateTime<Utc>>) -> Self {
134        self.email_verified_at = email_verified_at;
135        self
136    }
137
138    pub fn created_at(mut self, created_at: DateTime<Utc>) -> Self {
139        self.created_at = Some(created_at);
140        self
141    }
142
143    pub fn updated_at(mut self, updated_at: DateTime<Utc>) -> Self {
144        self.updated_at = Some(updated_at);
145        self
146    }
147
148    pub fn build(self) -> Result<User, Error> {
149        let now = Utc::now();
150        Ok(User {
151            id: self.id.unwrap_or(UserId::new_random()),
152            name: self.name,
153            email: self.email.ok_or(ValidationError::InvalidField(
154                "Email is required".to_string(),
155            ))?,
156            email_verified_at: self.email_verified_at,
157            created_at: self.created_at.unwrap_or(now),
158            updated_at: self.updated_at.unwrap_or(now),
159        })
160    }
161}
162
163#[derive(Debug, Clone, Serialize, Deserialize)]
164pub struct OAuthAccount {
165    pub user_id: UserId,
166    pub provider: String,
167    pub subject: String,
168    pub created_at: DateTime<Utc>,
169    pub updated_at: DateTime<Utc>,
170}
171
172impl OAuthAccount {
173    pub fn builder() -> OAuthAccountBuilder {
174        OAuthAccountBuilder::default()
175    }
176}
177
178#[derive(Default)]
179pub struct OAuthAccountBuilder {
180    user_id: Option<UserId>,
181    provider: Option<String>,
182    subject: Option<String>,
183    created_at: Option<DateTime<Utc>>,
184    updated_at: Option<DateTime<Utc>>,
185}
186
187impl OAuthAccountBuilder {
188    pub fn user_id(mut self, user_id: UserId) -> Self {
189        self.user_id = Some(user_id);
190        self
191    }
192
193    pub fn provider(mut self, provider: String) -> Self {
194        self.provider = Some(provider);
195        self
196    }
197
198    pub fn subject(mut self, subject: String) -> Self {
199        self.subject = Some(subject);
200        self
201    }
202
203    pub fn created_at(mut self, created_at: DateTime<Utc>) -> Self {
204        self.created_at = Some(created_at);
205        self
206    }
207
208    pub fn updated_at(mut self, updated_at: DateTime<Utc>) -> Self {
209        self.updated_at = Some(updated_at);
210        self
211    }
212
213    pub fn build(self) -> Result<OAuthAccount, Error> {
214        let now = Utc::now();
215        Ok(OAuthAccount {
216            user_id: self.user_id.ok_or(ValidationError::MissingField(
217                "User ID is required".to_string(),
218            ))?,
219            provider: self.provider.ok_or(ValidationError::MissingField(
220                "Provider is required".to_string(),
221            ))?,
222            subject: self.subject.ok_or(ValidationError::MissingField(
223                "Subject is required".to_string(),
224            ))?,
225            created_at: self.created_at.unwrap_or(now),
226            updated_at: self.updated_at.unwrap_or(now),
227        })
228    }
229}
230
231#[cfg(test)]
232mod tests {
233    use super::*;
234
235    #[test]
236    fn test_user_id() {
237        let user_id = UserId::new("test");
238        assert_eq!(user_id.as_str(), "test");
239
240        let user_id_from_str = UserId::from(user_id.as_str());
241        assert_eq!(user_id_from_str, user_id);
242
243        let user_id_random = UserId::new_random();
244        assert_ne!(user_id_random, user_id);
245    }
246}