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 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 pub fn invalidate_token(&self, token: &str) {
40 self.token.store.write().unwrap().remove(token);
41 trace!("invalidated token '{}'", token);
42 }
43
44 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 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 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}