use tokio; use tokio::sync::Mutex; use std::{ sync::Arc, }; use warp::{Filter, Reply, Rejection}; use argon2::{ password_hash::{ rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString }, Argon2 }; use serde::Deserialize; mod constants; use constants::*; mod util; mod db; use db::DB; async fn handle_ping() -> Result { return Ok("pong".to_string()) } #[derive(Deserialize)] struct RegisterReq { username: String, password: String, } type Response = Result, Rejection>; macro_rules! mk_bad_request { ($res:expr) => { Ok(Box::new(warp::reply::with_status($res, warp::http::StatusCode::BAD_REQUEST))) } } macro_rules! mk_not_found { ($res:expr) => { Ok(Box::new(warp::reply::with_status($res, warp::http::StatusCode::NOT_FOUND))) } } macro_rules! mk_server_err { () => { Ok(Box::new(warp::reply::with_status("Internal server error", warp::http::StatusCode::INTERNAL_SERVER_ERROR))) } } async fn handle_register(db: DB, req: RegisterReq) -> Response { let salt = SaltString::generate(&mut OsRng); let hash = match Argon2::default().hash_password(req.password.as_bytes(), &salt) { Ok(hash) => hash.to_string(), Err(_) => return mk_bad_request!("bad request"), }; match db::register_account(db, &req.username, &hash.to_string()).await { Ok(()) => Ok(Box::new("Registered")), Err(err) => mk_bad_request!(err), } } async fn handle_login(db: DB, req: RegisterReq) -> Result, Rejection> { let passhash = match db::get_passhash(db.clone(), &req.username).await { Ok(passhash) => passhash, Err(()) => return mk_not_found!("User not found"), }; let parsed_hash = match PasswordHash::new(&passhash) { Ok(parsed_hash) => parsed_hash, Err(err) => { eprintln!("Could not parse password hash: {err}"); return mk_server_err!(); } }; if let Err(_) = Argon2::default().verify_password(req.password.as_bytes(), &parsed_hash) { return Ok(Box::new(warp::reply::with_status("Incorrect password", warp::http::StatusCode::UNAUTHORIZED))); } match db::create_login_token(db, &req.username).await { Ok(token) => Ok(Box::new(token)), Err(()) => { eprintln!("Failed inserting login token for user '{0}'", &req.username); mk_server_err!() } } } macro_rules! db_handler1 { ($db:expr, $handler:ident) => { { let db2 = $db.clone(); move |a| $handler(db2.clone(), a) } } } #[tokio::main] async fn main() { let db: DB = Arc::new(Mutex::new(db::open().await)); println!("Opened database at {DB_FILE_NAME}."); let router = (warp::get().and(warp::path!("ping")) .and_then(handle_ping)) .or(warp::post().and(warp::path!("register")) .and(warp::body::json()) .and_then(db_handler1!(db, handle_register))) .or(warp::post().and(warp::path!("login")) .and(warp::body::json()) .and_then(db_handler1!(db, handle_login))); warp::serve(router) .run(([0, 0, 0, 0], 8775)) .await; }