senax_common/session/
session_key.rs1use rand::{Rng as _, distr::Alphanumeric};
2use std::convert::TryFrom;
3use std::time::{SystemTime, UNIX_EPOCH};
4
5const KEY_LENGTH: usize = 64;
6const FULL_KEY_LENGTH: usize = 80;
7
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub struct SessionKey(String);
10
11impl TryFrom<String> for SessionKey {
12 type Error = anyhow::Error;
13
14 fn try_from(val: String) -> Result<Self, Self::Error> {
15 anyhow::ensure!(
16 val.len() == FULL_KEY_LENGTH,
17 "Session ID length is invalid."
18 );
19 Ok(SessionKey(val))
20 }
21}
22
23impl From<SessionKey> for String {
24 fn from(key: SessionKey) -> Self {
25 key.0
26 }
27}
28
29impl From<&SessionKey> for String {
30 fn from(key: &SessionKey) -> Self {
31 key.0.clone()
32 }
33}
34
35impl Default for SessionKey {
36 fn default() -> Self {
37 Self::new()
38 }
39}
40
41impl std::fmt::Display for SessionKey {
42 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43 write!(f, "{}", self.0)
44 }
45}
46
47impl SessionKey {
48 pub fn new() -> SessionKey {
49 Self::generate(SystemTime::now())
50 }
51 pub fn generate_past(ttl: std::time::Duration) -> SessionKey {
52 Self::generate(SystemTime::now().checked_sub(ttl).unwrap())
53 }
54 fn generate(time: SystemTime) -> SessionKey {
55 let mut rng = rand::rng();
56 let value = std::iter::repeat(())
57 .map(|()| rng.sample(Alphanumeric))
58 .take(KEY_LENGTH)
59 .collect::<Vec<_>>();
60 let time = (time.duration_since(UNIX_EPOCH).unwrap().as_nanos() >> 2) as u64;
61 SessionKey(format!(
62 "{:016X}{}",
63 time,
64 String::from_utf8(value).unwrap()
65 ))
66 }
67 pub fn hash(&self) -> u32 {
68 crc32fast::hash(self.0.as_bytes())
69 }
70}
71
72#[cfg(test)]
73mod tests {
74 use super::*;
75
76 #[test]
77 fn test() {
78 let key = SessionKey::new();
79 let str: String = key.into();
80 assert_eq!(str.len(), FULL_KEY_LENGTH);
81 }
82}