diff --git a/Cargo.toml b/Cargo.toml index e0d427f..71a6ed6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,6 +22,7 @@ chrono = { version = "0.4.38", features = ["alloc"]} futures-util = "0.3.30" h2 = "0.4.6" argon2 = "0.5.3" +rand = "0.8.1" [target.'cfg(unix)'.dependencies] daemonize = "0.5.0" \ No newline at end of file diff --git a/migrations/20240921193253_create_auth_table.sql b/migrations/20240921193253_create_auth_table.sql index 0939fac..6df0ec6 100644 --- a/migrations/20240921193253_create_auth_table.sql +++ b/migrations/20240921193253_create_auth_table.sql @@ -2,8 +2,7 @@ CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY NOT NULL, username TEXT NOT NULL, -salt text NOT NULL, -hash TEXT NOT NULL, +saltyhash text NOT NULL, permissions INTEGER DEFAULT 0, -token TEXT +token TEXT NOT NULL ) \ No newline at end of file diff --git a/routes.json b/routes.json index 4d230a9..a7d69df 100644 --- a/routes.json +++ b/routes.json @@ -2,5 +2,7 @@ "/": "static/html/index.html", "/results": "static/html/results.html", "/archives": "static/html/archives.html", - "/favicon.ico": "static/img/favicon.ico" + "/favicon.ico": "static/img/favicon.ico", + "/login": "static/html/login.html", + "/register": "static/html/register.html" } diff --git a/src/main.rs b/src/main.rs index 3be1819..d149ee8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,7 +3,7 @@ use bytes::Bytes; use chrono::{DateTime, Utc}; use futures; use http_body_util::{BodyExt, Full}; -use hyper::body::Incoming; +use hyper::body::{Body, Incoming}; use hyper::server::conn::http1; use hyper::service::service_fn; use hyper::{Error, Method, Request, Response, StatusCode}; @@ -20,7 +20,14 @@ use std::str::FromStr; use std::sync::{Arc, Mutex}; use std::time::SystemTime; use tokio::net::TcpListener; - +use rand::distributions::Standard; +use argon2::{ + password_hash::{ + rand_core::OsRng, + PasswordHash, PasswordHasher, PasswordVerifier, SaltString + }, + Argon2 +}; #[cfg(target_os = "linux")] use daemonize::Daemonize; #[derive(Serialize, Deserialize)] @@ -29,7 +36,7 @@ struct Player { name: String, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize)] struct Vote { plus_player_id: i64, plus_nickname: String, @@ -39,6 +46,19 @@ struct Vote { minus_reason: String, } +#[derive(Serialize, Deserialize)] +struct User { + username: String, + saltyhash: String, + permissions: i64, + token: String +} + +#[derive(Serialize, Deserialize)] +struct Login { + username: String, + password: String +} #[derive(Serialize, Deserialize)] struct Settings { database_url: String, @@ -178,13 +198,27 @@ async fn get_votes(req: &Request, db: Arc>) -> Vec, db: Arc>) -> Result>, Error> { let path = req.uri().path(); - if path != "/post" { - return Ok(Response::builder().status(StatusCode::BAD_REQUEST).body(Full::new(Bytes::from("Bad Request (Bad Route)"))).unwrap()); + match path { + "/vote" => { + post_vote(req, db).await + }, + "/login" => { + login(req, db).await + }, + "/register" => { + not_found().await + }, + _ => { + not_found().await + } } +} + +async fn post_vote(req: Request, db: Arc>) -> Result>, Error> { let body = req.into_body().collect().await?; let data: Result = from_reader(body.aggregate().reader()); if data.is_err() { - return Ok(Response::builder().status(StatusCode::BAD_REQUEST).body(Full::new(Bytes::from("Bad Request (Bad Data)"))).unwrap()); + return Ok(Response::builder().status(StatusCode::BAD_REQUEST).body(Full::new(Bytes::from("Bad Request"))).unwrap()); } let vote = data.unwrap(); let timestamp: DateTime = DateTime::from(SystemTime::now()); @@ -206,6 +240,40 @@ async fn post(req: Request, db: Arc>) -> Result, db: Arc>) -> Result>, Error> { + let body = req.into_body().collect().await; + let data: Result = from_reader(body?.aggregate().reader()); + if data.is_err() { + return Ok(Response::builder().status(StatusCode::BAD_REQUEST).body(Full::new(Bytes::from("Bad Request"))).unwrap()); + } + let data = data.unwrap(); + let pool = db.clone().lock().unwrap().clone(); + let mut conn = pool.acquire().await.unwrap(); + let result = sqlx::query!(r#"SELECT * FROM users WHERE username=?1"#, data.username).fetch_optional(&mut *conn).await; + match result { + Ok(Some(user)) => { + let argon = Argon2::default(); + let hash = PasswordHash::new(&user.saltyhash).unwrap(); + match argon.verify_password(data.password.as_bytes(), &hash) { + Ok(()) => { + Ok(Response::builder().header("Set-Cookie", format!("token={}", user.token)).body(Full::new(Bytes::from("Ok"))).unwrap()) + }, + Err(_) => { + Ok(Response::builder().status(StatusCode::BAD_REQUEST).body(Full::new(Bytes::from("Bad Request"))).unwrap()) + } + } + }, + Ok(None) => { + Ok(Response::builder().status(StatusCode::BAD_REQUEST).body(Full::new(Bytes::from("Bad Request"))).unwrap()) + } + Err(e) => { Ok(Response::builder().status(StatusCode::INTERNAL_SERVER_ERROR).body(Full::new(Bytes::from("Bad Request"))).unwrap()) } + } +} + +async fn register(req: Request, db: Arc>) -> Result>, Error> { + todo!() +} + async fn not_found() -> Result>, Error> { let mut file_path = env::current_dir().expect("Could not get app directory."); file_path.push("static/html/404.html"); diff --git a/static/js/index.js b/static/js/index.js index 67ba756..062d147 100644 --- a/static/js/index.js +++ b/static/js/index.js @@ -59,7 +59,7 @@ async function main() { true, "warning"); return; } - if (await fetch("/post", { + if (await fetch("/vote", { method: "post", body: JSON.stringify(vote) }) .then(r => r.status) === 200) {