128 lines
3.5 KiB
Rust
Raw Permalink Normal View History

2023-04-21 18:48:39 +03:00
use rand::Rng;
2023-05-05 09:29:54 +03:00
use scc::{HashMap, HashSet};
2023-04-21 18:48:39 +03:00
use std::borrow::Cow;
use std::fmt::Write;
use std::path::Path;
use std::sync::Arc;
2023-05-05 09:29:54 +03:00
use std::time::UNIX_EPOCH;
2023-04-21 18:48:39 +03:00
use crate::error::AppError;
2023-05-05 09:29:54 +03:00
fn get_current_time() -> u64 {
UNIX_EPOCH.elapsed().unwrap().as_secs()
}
2023-04-21 18:48:39 +03:00
fn hash_string(data: &[u8]) -> Result<String, argon2::Error> {
argon2::hash_encoded(data, "11111111".as_bytes(), &argon2::Config::original())
2023-04-21 18:48:39 +03:00
}
2023-05-05 09:29:54 +03:00
fn generate_random_string(len: usize) -> String {
rand::thread_rng()
.sample_iter(rand::distributions::Alphanumeric)
.take(len)
.map(|c| c as char)
.collect::<String>()
}
2023-04-21 18:48:39 +03:00
#[derive(Debug, Clone)]
pub(crate) struct Tokens {
hashed: Arc<HashSet<Cow<'static, str>>>,
raw_contents: &'static str,
}
impl Tokens {
pub async fn read(path: impl AsRef<Path>) -> Result<Self, AppError> {
2023-05-09 08:30:48 +03:00
let tokens = tokio::fs::read_to_string(path).await?;
2023-04-21 18:48:39 +03:00
let this = Self {
hashed: Arc::new(HashSet::new()),
2023-05-09 08:30:48 +03:00
// this is okay since we only call this once and it will be
// used for all of it's lifetime
raw_contents: Box::leak(tokens.into_boxed_str()),
2023-04-21 18:48:39 +03:00
};
for token in this.raw_contents.lines() {
let token = token.trim();
if !token.is_empty() {
this.hashed
.insert_async(Cow::Borrowed(token))
.await
.expect("the set will be empty");
}
}
Ok(this)
}
pub async fn write(&self, path: impl AsRef<Path>) -> Result<(), AppError> {
let mut contents = String::new();
self.hashed
2023-10-13 21:50:59 +03:00
.scan_async(|hash| {
2023-04-21 18:48:39 +03:00
writeln!(&mut contents, "{hash}").expect("if this fails then too bad")
})
.await;
tokio::fs::write(path, contents).await.map_err(Into::into)
}
pub async fn generate(&self) -> Result<String, AppError> {
2023-05-05 09:29:54 +03:00
let token = generate_random_string(30);
2023-04-21 18:48:39 +03:00
let token_hash = hash_string(token.as_bytes())?;
self.hashed.insert_async(token_hash.into()).await?;
Ok(token)
}
pub async fn verify(&self, token: impl AsRef<str>) -> Result<bool, AppError> {
let token = token.as_ref();
let token_hash = hash_string(token.as_bytes())?;
Ok(self.hashed.contains_async(&Cow::Owned(token_hash)).await)
}
pub async fn revoke_all(&self) {
self.hashed.clear_async().await;
}
2023-04-21 18:48:39 +03:00
}
2023-05-05 09:29:54 +03:00
#[derive(Clone)]
pub(crate) struct MusicScopedTokens {
map: Arc<HashMap<String, MusicScopedToken>>,
expiry_time: u64,
}
impl MusicScopedTokens {
pub fn new(expiry_time: u64) -> Self {
Self {
map: Arc::new(HashMap::new()),
expiry_time,
}
}
pub async fn generate_for_id(&self, music_id: String) -> String {
let data = MusicScopedToken {
creation: get_current_time(),
music_id,
};
let token = generate_random_string(12);
let _ = self.map.insert_async(token.clone(), data).await;
return token;
}
pub async fn verify(&self, token: impl AsRef<str>) -> Option<String> {
let token = token.as_ref();
let data = self.map.read_async(token, |_, v| v.clone()).await?;
if get_current_time() - data.creation > self.expiry_time {
self.map.remove_async(token).await;
return None;
}
Some(data.music_id)
}
}
#[derive(Clone)]
2023-05-09 08:30:48 +03:00
struct MusicScopedToken {
2023-05-05 09:29:54 +03:00
creation: u64,
music_id: String,
}