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 async_trait::async_trait;
16use chrono::{DateTime, Utc};
17use serde::{Deserialize, Serialize};
18use uuid::Uuid;
19
20use crate::{Error, error::ValidationError, storage::NewUser};
21
22/// A unique, stable identifier for a specific user
23/// This value should be treated as opaque, and should not be used as a UUID even if it may look like one
24#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)]
25pub struct UserId(String);
26
27impl UserId {
28    pub fn new(id: &str) -> Self {
29        UserId(id.to_string())
30    }
31
32    pub fn new_random() -> Self {
33        UserId(Uuid::new_v4().to_string())
34    }
35
36    pub fn into_inner(self) -> String {
37        self.0
38    }
39
40    pub fn as_str(&self) -> &str {
41        &self.0
42    }
43}
44
45impl Default for UserId {
46    fn default() -> Self {
47        Self::new_random()
48    }
49}
50
51impl From<String> for UserId {
52    fn from(s: String) -> Self {
53        Self(s)
54    }
55}
56
57impl From<&str> for UserId {
58    fn from(s: &str) -> Self {
59        Self(s.to_string())
60    }
61}
62
63impl std::fmt::Display for UserId {
64    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
65        write!(f, "{}", self.0)
66    }
67}
68
69/// The central manager for user operations
70///
71/// This trait defines core functionality for managing users. Implementations
72/// should provide efficient means of creating, retrieving, and updating users.
73#[async_trait]
74pub trait UserManager: Send + Sync + 'static {
75    /// Create a new user
76    async fn create_user(&self, user: &NewUser) -> Result<User, Error>;
77
78    /// Get a user by ID
79    async fn get_user(&self, id: &UserId) -> Result<Option<User>, Error>;
80
81    /// Get a user by email
82    async fn get_user_by_email(&self, email: &str) -> Result<Option<User>, Error>;
83
84    /// Get an existing user or create a new one if not found
85    async fn get_or_create_user_by_email(&self, email: &str) -> Result<User, Error>;
86
87    /// Update a user's information
88    async fn update_user(&self, user: &User) -> Result<User, Error>;
89
90    /// Delete a user by ID
91    async fn delete_user(&self, id: &UserId) -> Result<(), Error>;
92
93    /// Mark a user's email as verified
94    async fn set_user_email_verified(&self, user_id: &UserId) -> Result<(), Error>;
95}
96
97/// Representation of a user in Torii. This is the user object returned by all authentication plugins.
98///
99/// Many of these fields are optional, as they may not be available from the authentication provider,
100/// or may not be known at the time of authentication.
101#[derive(Debug, Clone, Serialize, Deserialize)]
102pub struct User {
103    // The unique identifier for the user.
104    pub id: UserId,
105
106    // The name of the user.
107    pub name: Option<String>,
108
109    // The email of the user.
110    pub email: String,
111
112    // The email verified at timestamp. If the user has not verified their email, this will be None.
113    pub email_verified_at: Option<DateTime<Utc>>,
114
115    // The created at timestamp.
116    pub created_at: DateTime<Utc>,
117
118    // The updated at timestamp.
119    pub updated_at: DateTime<Utc>,
120}
121
122impl User {
123    pub fn builder() -> UserBuilder {
124        UserBuilder::default()
125    }
126
127    /// Check if the user's email has been verified.
128    pub fn is_email_verified(&self) -> bool {
129        self.email_verified_at.is_some()
130    }
131}
132
133#[derive(Default)]
134pub struct UserBuilder {
135    id: Option<UserId>,
136    name: Option<String>,
137    email: Option<String>,
138    email_verified_at: Option<DateTime<Utc>>,
139    created_at: Option<DateTime<Utc>>,
140    updated_at: Option<DateTime<Utc>>,
141}
142
143impl UserBuilder {
144    pub fn id(mut self, id: UserId) -> Self {
145        self.id = Some(id);
146        self
147    }
148
149    pub fn name(mut self, name: Option<String>) -> Self {
150        self.name = name;
151        self
152    }
153
154    pub fn email(mut self, email: String) -> Self {
155        self.email = Some(email);
156        self
157    }
158
159    pub fn email_verified_at(mut self, email_verified_at: Option<DateTime<Utc>>) -> Self {
160        self.email_verified_at = email_verified_at;
161        self
162    }
163
164    pub fn created_at(mut self, created_at: DateTime<Utc>) -> Self {
165        self.created_at = Some(created_at);
166        self
167    }
168
169    pub fn updated_at(mut self, updated_at: DateTime<Utc>) -> Self {
170        self.updated_at = Some(updated_at);
171        self
172    }
173
174    pub fn build(self) -> Result<User, Error> {
175        let now = Utc::now();
176        Ok(User {
177            id: self.id.unwrap_or_default(),
178            name: self.name,
179            email: self.email.ok_or(ValidationError::InvalidField(
180                "Email is required".to_string(),
181            ))?,
182            email_verified_at: self.email_verified_at,
183            created_at: self.created_at.unwrap_or(now),
184            updated_at: self.updated_at.unwrap_or(now),
185        })
186    }
187}
188
189/// Default implementation of the UserManager trait
190///
191/// This implementation wraps a UserStorage implementation and provides
192/// the core user management functionality needed by authentication systems.
193pub struct DefaultUserManager<S>
194where
195    S: crate::storage::UserStorage,
196{
197    storage: std::sync::Arc<S>,
198}
199
200impl<S> DefaultUserManager<S>
201where
202    S: crate::storage::UserStorage,
203{
204    /// Create a new DefaultUserManager with the specified storage
205    pub fn new(storage: std::sync::Arc<S>) -> Self {
206        Self { storage }
207    }
208}
209
210#[async_trait]
211impl<S> UserManager for DefaultUserManager<S>
212where
213    S: crate::storage::UserStorage,
214{
215    async fn create_user(&self, user: &NewUser) -> Result<User, Error> {
216        self.storage
217            .create_user(user)
218            .await
219            .map_err(|e| Error::Storage(crate::error::StorageError::Database(e.to_string())))
220    }
221
222    async fn get_user(&self, id: &UserId) -> Result<Option<User>, Error> {
223        self.storage
224            .get_user(id)
225            .await
226            .map_err(|e| Error::Storage(crate::error::StorageError::Database(e.to_string())))
227    }
228
229    async fn get_user_by_email(&self, email: &str) -> Result<Option<User>, Error> {
230        self.storage
231            .get_user_by_email(email)
232            .await
233            .map_err(|e| Error::Storage(crate::error::StorageError::Database(e.to_string())))
234    }
235
236    async fn get_or_create_user_by_email(&self, email: &str) -> Result<User, Error> {
237        self.storage
238            .get_or_create_user_by_email(email)
239            .await
240            .map_err(|e| Error::Storage(crate::error::StorageError::Database(e.to_string())))
241    }
242
243    async fn update_user(&self, user: &User) -> Result<User, Error> {
244        self.storage
245            .update_user(user)
246            .await
247            .map_err(|e| Error::Storage(crate::error::StorageError::Database(e.to_string())))
248    }
249
250    async fn delete_user(&self, id: &UserId) -> Result<(), Error> {
251        self.storage
252            .delete_user(id)
253            .await
254            .map_err(|e| Error::Storage(crate::error::StorageError::Database(e.to_string())))
255    }
256
257    async fn set_user_email_verified(&self, user_id: &UserId) -> Result<(), Error> {
258        self.storage
259            .set_user_email_verified(user_id)
260            .await
261            .map_err(|e| Error::Storage(crate::error::StorageError::Database(e.to_string())))
262    }
263}
264
265#[derive(Debug, Clone, Serialize, Deserialize)]
266pub struct OAuthAccount {
267    pub user_id: UserId,
268    pub provider: String,
269    pub subject: String,
270    pub created_at: DateTime<Utc>,
271    pub updated_at: DateTime<Utc>,
272}
273
274impl OAuthAccount {
275    pub fn builder() -> OAuthAccountBuilder {
276        OAuthAccountBuilder::default()
277    }
278}
279
280#[derive(Default)]
281pub struct OAuthAccountBuilder {
282    user_id: Option<UserId>,
283    provider: Option<String>,
284    subject: Option<String>,
285    created_at: Option<DateTime<Utc>>,
286    updated_at: Option<DateTime<Utc>>,
287}
288
289impl OAuthAccountBuilder {
290    pub fn user_id(mut self, user_id: UserId) -> Self {
291        self.user_id = Some(user_id);
292        self
293    }
294
295    pub fn provider(mut self, provider: String) -> Self {
296        self.provider = Some(provider);
297        self
298    }
299
300    pub fn subject(mut self, subject: String) -> Self {
301        self.subject = Some(subject);
302        self
303    }
304
305    pub fn created_at(mut self, created_at: DateTime<Utc>) -> Self {
306        self.created_at = Some(created_at);
307        self
308    }
309
310    pub fn updated_at(mut self, updated_at: DateTime<Utc>) -> Self {
311        self.updated_at = Some(updated_at);
312        self
313    }
314
315    pub fn build(self) -> Result<OAuthAccount, Error> {
316        let now = Utc::now();
317        Ok(OAuthAccount {
318            user_id: self.user_id.ok_or(ValidationError::MissingField(
319                "User ID is required".to_string(),
320            ))?,
321            provider: self.provider.ok_or(ValidationError::MissingField(
322                "Provider is required".to_string(),
323            ))?,
324            subject: self.subject.ok_or(ValidationError::MissingField(
325                "Subject is required".to_string(),
326            ))?,
327            created_at: self.created_at.unwrap_or(now),
328            updated_at: self.updated_at.unwrap_or(now),
329        })
330    }
331}
332
333#[cfg(test)]
334mod tests {
335    use super::*;
336
337    #[test]
338    fn test_user_id() {
339        let user_id = UserId::new("test");
340        assert_eq!(user_id.as_str(), "test");
341
342        let user_id_from_str = UserId::from(user_id.as_str());
343        assert_eq!(user_id_from_str, user_id);
344
345        let user_id_random = UserId::new_random();
346        assert_ne!(user_id_random, user_id);
347    }
348}