diff --git a/Cargo.lock b/Cargo.lock index 44bbe19..780c494 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -488,6 +488,7 @@ dependencies = [ "hmac", "ldap3", "md-5", + "rand", "secrecy", "serde", "sqlx", diff --git a/Cargo.toml b/Cargo.toml index d9f0ea5..b39e86d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ color-eyre = { version = "0.6.2" } hmac = "0.12.1" ldap3 = { version = "0.11.3", default-features = false, features = ["tls-rustls"] } md-5 = "0.10.6" +rand = "0.8.5" secrecy = { version = "0.8.0", features = ["serde"] } serde = { version = "1.0.196", features = ["derive"] } sqlx = { version = "0.7.3", features = ["runtime-tokio", "tls-rustls", "sqlite"] } diff --git a/src/auth.rs b/src/auth.rs index 0a0901d..007be03 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -1,10 +1,11 @@ use std::sync::Arc; +use rand::{seq::IteratorRandom, thread_rng}; use tokio::sync::Mutex; -use tracing::instrument; +use tracing::{event, instrument, Level}; use crate::{ - db::{/* Database, */ SqliteDatabase}, + db::{/* Database, */ Database, SqliteDatabase}, secrets::{Password, UserToken}, }; @@ -20,7 +21,25 @@ impl Authenticator { #[instrument] pub async fn create_user_token(&mut self, username: &str, password: &Password) -> UserToken { - todo!() + // let created = + + let mut token_str = "".to_owned(); + + let mut rng = thread_rng(); + for _ in 0..30 { + let digit = (0..=15).choose(&mut rng).unwrap(); + let char = (('a' as u8) + digit) as char; + token_str.push(char) + } + + let new_token = UserToken::from(token_str); + + 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 } #[instrument] diff --git a/src/db.rs b/src/db.rs index 7a1b71e..c59e16a 100644 --- a/src/db.rs +++ b/src/db.rs @@ -1,3 +1,4 @@ +use secrecy::ExposeSecret; use sqlx::{query, query_as, Connection, SqliteConnection}; use crate::secrets::UserToken; @@ -5,8 +6,12 @@ use crate::secrets::UserToken; // TODO: allow configuring this via envar const DB_URI_DEFAULT: &str = "sqlite://sqlite.db"; +const TABLE_USER_TOKENS: &str = "user_tokens"; + pub trait Database { async fn get_token(&mut self, username: &str) -> Result; + + async fn save_token(&mut self, username: &str, token: &UserToken) -> Result<(), sqlx::Error>; } #[derive(Debug)] @@ -29,8 +34,8 @@ impl SqliteDatabase { pub async fn init(&mut self) { query( - "CREATE TABLE IF NOT EXISTS user_tokens ( - id INTEGER PRIMARY KEY NOT NULL, + "CREATE TABLE IF NOT EXISTS {TABLE_USER_TOKENS} ( + id INTEGER PRIMARY KEY AUTOINCREMENT, username VARCHAR(255), token NCHAR(30), created DATETIME, @@ -49,7 +54,7 @@ impl Database for SqliteDatabase { "SELECT token FROM user_tokens WHERE - username = {username} + username = ? AND valid = TRUE ORDER BY created DESC", @@ -60,4 +65,20 @@ impl Database for SqliteDatabase { Ok(UserToken::from(row.0)) } + + async fn save_token(&mut self, username: &str, token: &UserToken) -> Result<(), sqlx::Error> { + query( + "INSERT INTO {TABLE_USER_TOKENS} + (username, token, created, valid) + VALUES + (?, ?, DATETIME('NOW'), TRUE) + ", + ) + .bind(username) + .bind(token.0.expose_secret()) + .execute(&mut self.conn) + .await?; + + Ok(()) + } }