Skip to main content

basileus/
token.rs

1use std::{
2    collections::HashMap,
3    sync::RwLock,
4    time::{Duration, SystemTime},
5};
6
7use crate::{Basileus, rand_buf};
8use base64::{Engine, prelude::BASE64_STANDARD};
9
10use tracing::{debug, trace};
11
12pub struct TokenModule {
13    store: RwLock<HashMap<String, (String, SystemTime)>>,
14}
15
16impl TokenModule {
17    pub fn new() -> Self {
18        Self {
19            store: RwLock::new(HashMap::new()),
20        }
21    }
22}
23
24impl Basileus {
25    /// Issue a new token to the specified user.
26    pub fn issue_token(&self, user: &str) -> String {
27        let buf = rand_buf::<64>();
28        let token = BASE64_STANDARD.encode(buf);
29        self.token
30            .store
31            .write()
32            .unwrap()
33            .insert(token.clone(), (user.to_owned(), SystemTime::now()));
34        debug!("issued token '{}**' for '{user}'", &token[0..4]);
35        token
36    }
37
38    /// Invalidate a token.
39    pub fn invalidate_token(&self, token: &str) {
40        self.token.store.write().unwrap().remove(token);
41        trace!("invalidated token '{}'", token);
42    }
43
44    /// Invalidate all tokens related to `user`.
45    pub fn invalidate_user_token(&self, user: &str) {
46        self.token
47            .store
48            .write()
49            .unwrap()
50            .retain(|_, (u, _)| u != user);
51        trace!("invalidated user session '{user}'")
52    }
53
54    /// Make all tokens older than `duration` expire.
55    pub fn expire_token(&self, duration: Duration) {
56        let mut token = self.token.store.write().unwrap();
57        let prev = token.len();
58        token.retain(|_, (_, time)| {
59            SystemTime::now()
60                .duration_since(*time)
61                .is_ok_and(|d| d < duration)
62        });
63        let diff = prev - token.len();
64        trace!("expired {diff} tokens");
65    }
66
67    /// Verify token, return the user it belongs to if successful.
68    pub fn verify_token(&self, token: &str) -> Option<String> {
69        let map = self.token.store.read().unwrap();
70        let res = map.get(token).map(|(user, _)| user.clone());
71        if let Some(user) = &res {
72            trace!("authorized {user} by token")
73        }
74        res
75    }
76}