2024-02-10 12:06:53 +01:00
|
|
|
use std::sync::Arc;
|
|
|
|
|
2024-02-10 12:37:38 +01:00
|
|
|
use base64::{prelude::BASE64_STANDARD, Engine};
|
|
|
|
use hmac::{Hmac, Mac};
|
|
|
|
use md5::Md5;
|
2024-02-10 13:32:49 +01:00
|
|
|
use rand::{
|
|
|
|
distributions::{Alphanumeric, DistString},
|
|
|
|
thread_rng,
|
|
|
|
};
|
2024-02-10 12:37:38 +01:00
|
|
|
use secrecy::{ExposeSecret, SecretVec};
|
2024-02-10 13:08:45 +01:00
|
|
|
use thiserror::Error;
|
2024-02-10 12:37:38 +01:00
|
|
|
use time::{macros::format_description, OffsetDateTime};
|
2024-02-10 12:06:53 +01:00
|
|
|
use tokio::sync::Mutex;
|
2024-02-10 13:07:36 +01:00
|
|
|
use tracing::{event, instrument, Level};
|
2024-02-10 11:10:58 +01:00
|
|
|
|
2024-02-10 12:32:01 +01:00
|
|
|
use crate::{
|
2024-02-10 13:07:36 +01:00
|
|
|
db::{/* Database, */ Database, SqliteDatabase},
|
2024-02-10 12:37:38 +01:00
|
|
|
secrets::{Password, ServerHash, ServerPadlock, UserServerKey, UserToken},
|
2024-02-10 12:32:01 +01:00
|
|
|
};
|
2024-02-10 11:10:58 +01:00
|
|
|
|
2024-02-10 13:08:45 +01:00
|
|
|
#[derive(Debug, Error)]
|
|
|
|
pub enum AuthenticationError {
|
|
|
|
#[error("Invalid username or password")]
|
|
|
|
InvalidUserOrPassword,
|
|
|
|
#[error("Invalid token")]
|
|
|
|
InvalidToken,
|
|
|
|
#[error("Invalid server hash")]
|
|
|
|
InvalidServerHash,
|
|
|
|
#[error("Authentication backend error")]
|
|
|
|
Backend(#[from] sqlx::Error),
|
|
|
|
}
|
|
|
|
|
2024-02-10 11:20:35 +01:00
|
|
|
#[derive(Debug)]
|
2024-02-10 13:35:05 +01:00
|
|
|
pub struct UserAuthenticator {
|
2024-02-10 12:56:35 +01:00
|
|
|
db: Arc<Mutex<SqliteDatabase>>,
|
2024-02-10 10:51:56 +01:00
|
|
|
}
|
|
|
|
|
2024-02-10 13:35:05 +01:00
|
|
|
impl UserAuthenticator {
|
|
|
|
const TOKEN_LEN: usize = 30;
|
2024-02-10 13:32:49 +01:00
|
|
|
|
2024-02-10 12:06:53 +01:00
|
|
|
pub fn new(db: Arc<Mutex<SqliteDatabase>>) -> Self {
|
|
|
|
Self { db }
|
2024-02-10 10:51:56 +01:00
|
|
|
}
|
|
|
|
|
2024-02-10 11:20:35 +01:00
|
|
|
#[instrument]
|
2024-02-10 12:06:53 +01:00
|
|
|
pub async fn create_user_token(&mut self, username: &str, password: &Password) -> UserToken {
|
2024-02-10 13:32:49 +01:00
|
|
|
let new_token =
|
2024-02-10 13:35:05 +01:00
|
|
|
UserToken::from(Alphanumeric.sample_string(&mut thread_rng(), Self::TOKEN_LEN));
|
2024-02-10 13:07:36 +01:00
|
|
|
|
|
|
|
let mut db = self.db.lock().await;
|
|
|
|
if let Err(err) = db.save_token(username, &new_token).await {
|
|
|
|
event!(Level::ERROR, %err, "Failed to save token in database");
|
|
|
|
}
|
|
|
|
|
|
|
|
new_token
|
2024-02-10 11:11:46 +01:00
|
|
|
}
|
|
|
|
|
2024-02-10 11:20:35 +01:00
|
|
|
#[instrument]
|
2024-02-10 13:08:45 +01:00
|
|
|
pub async fn verify_user_token(
|
|
|
|
&self,
|
|
|
|
username: &str,
|
|
|
|
token: &UserToken,
|
|
|
|
) -> Result<(), AuthenticationError> {
|
2024-02-10 13:10:54 +01:00
|
|
|
let mut db = self.db.lock().await;
|
|
|
|
|
2024-02-10 13:39:10 +01:00
|
|
|
match db.get_token(username).await? {
|
|
|
|
Some(_) => Ok(()),
|
|
|
|
None => Err(AuthenticationError::InvalidToken),
|
|
|
|
}
|
2024-02-10 10:51:56 +01:00
|
|
|
}
|
|
|
|
}
|
2024-02-10 12:37:38 +01:00
|
|
|
|
|
|
|
pub struct ServerPadlockGenerator {
|
|
|
|
secret: SecretVec<u8>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ServerPadlockGenerator {
|
|
|
|
pub fn new(/* secret */) -> Self {
|
|
|
|
todo!()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn generate_padlock(&self, server_hash: &ServerHash) -> ServerPadlock {
|
|
|
|
todo!()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct UserServerKeyGenerator {
|
2024-02-10 13:35:05 +01:00
|
|
|
user_authenticator: Arc<UserAuthenticator>,
|
2024-02-10 12:37:38 +01:00
|
|
|
padlock_generator: Arc<ServerPadlockGenerator>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl UserServerKeyGenerator {
|
2024-02-10 13:35:05 +01:00
|
|
|
pub fn new(/* UserAuthenticator, ServerPadlockGenerator? */) -> Self {
|
2024-02-10 12:37:38 +01:00
|
|
|
todo!()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn generate_user_server_key(
|
|
|
|
&self,
|
|
|
|
username: &str,
|
|
|
|
token: &UserToken,
|
|
|
|
server_hash: &ServerHash,
|
|
|
|
) -> Result<(UserServerKey, String), AuthenticationError> {
|
|
|
|
self.user_authenticator
|
|
|
|
.verify_user_token(username, token)
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
let padlock = self.padlock_generator.generate_padlock(server_hash);
|
|
|
|
|
|
|
|
let timestamp = OffsetDateTime::now_utc()
|
|
|
|
.format(format_description!(
|
|
|
|
"[year repr:last_two][month][day][hour repr:24][minute][second]"
|
|
|
|
))
|
|
|
|
.expect("timestamp format should be validated at compile-time");
|
|
|
|
|
|
|
|
event!(Level::DEBUG, timestamp, "Generating user_server_key");
|
|
|
|
|
|
|
|
let mut mac: Hmac<Md5> = Hmac::new_from_slice(padlock.0.expose_secret().as_bytes())
|
|
|
|
.map_err(|_e| AuthenticationError::InvalidServerHash)?;
|
|
|
|
mac.update(format!("{}_{}_{}", username, padlock.0.expose_secret(), timestamp).as_bytes());
|
|
|
|
let user_server_key =
|
|
|
|
UserServerKey(BASE64_STANDARD.encode(mac.finalize().into_bytes()).into());
|
|
|
|
|
|
|
|
Ok((user_server_key, timestamp))
|
|
|
|
}
|
|
|
|
}
|