1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
//! Contains database traits for connecting the authentication service with a database of
//!   your choosing. These are automatically implemented for `Vec<User>` as an example.

use crate::error::AuthError;
use crate::session::Session;
use crate::user::User;

/// Represents a database which can be used to store auth information.
/// Must be implemented for whatever database you are using.
///
/// It is good practice to have a separate collection for authentication information and
///   user details, so whatever collection/table you use in your implementation of this trait
///   should ideally not be used for anything else.
pub trait AuthDatabase {
    /// Returns the user associated with the given UID, or `None` if not found.
    fn get_user_by_uid(&self, uid: impl AsRef<str>) -> Option<User>;
    /// Returns the user who owns the given token, or `None` if not found.
    fn get_user_by_token(&self, token: impl AsRef<str>) -> Option<User>;
    /// Returns the session identified by the given token, or `None` if not found.
    fn get_session_by_token(&self, token: impl AsRef<str>) -> Option<Session>;

    /// Update the user in the database.
    /// The user should be identified by their UID.
    fn update_user(&mut self, user: User) -> Result<(), AuthError>;
    /// Add a user to the database.
    fn add_user(&mut self, user: User) -> Result<(), AuthError>;
    /// Remove the user with the given UID from the database.
    fn remove_user(&mut self, uid: impl AsRef<str>) -> Result<(), AuthError>;
}

impl AuthDatabase for Vec<User> {
    fn get_user_by_uid(&self, uid: impl AsRef<str>) -> Option<User> {
        self.iter()
            .find(|user| user.uid == uid.as_ref())
            .map(|user| (*user).clone())
    }

    fn get_user_by_token(&self, token: impl AsRef<str>) -> Option<User> {
        self.iter()
            .find(|u| u.session.is_some() && u.session.as_ref().unwrap().token == token.as_ref())
            .map(|user| (*user).clone())
    }

    fn get_session_by_token(&self, token: impl AsRef<str>) -> Option<Session> {
        self.iter()
            .find(|u| u.session.is_some() && u.session.as_ref().unwrap().token == token.as_ref())
            .map(|user| user.session.as_ref().unwrap().clone())
    }

    fn update_user(&mut self, user: User) -> Result<(), AuthError> {
        self.iter_mut()
            .find(|old| old.uid == user.uid)
            .map(|old| *old = user)
            .ok_or(AuthError::UserNotFound)
    }

    fn add_user(&mut self, user: User) -> Result<(), AuthError> {
        if self.get_user_by_uid(&user.uid).is_some() {
            return Err(AuthError::UserAlreadyExists);
        }

        self.push(user);

        Ok(())
    }

    fn remove_user(&mut self, uid: impl AsRef<str>) -> Result<(), AuthError> {
        if self.get_user_by_uid(&uid).is_none() {
            return Err(AuthError::UserNotFound);
        }

        self.retain(|user| user.uid != uid.as_ref());

        Ok(())
    }
}