Merge of dev-auth #1
@@ -22,6 +22,7 @@ chrono = { version = "0.4.38", features = ["alloc"]}
|
|||||||
futures-util = "0.3.30"
|
futures-util = "0.3.30"
|
||||||
h2 = "0.4.6"
|
h2 = "0.4.6"
|
||||||
argon2 = "0.5.3"
|
argon2 = "0.5.3"
|
||||||
|
rand = "0.8.1"
|
||||||
|
|
||||||
[target.'cfg(unix)'.dependencies]
|
[target.'cfg(unix)'.dependencies]
|
||||||
daemonize = "0.5.0"
|
daemonize = "0.5.0"
|
||||||
@@ -2,8 +2,7 @@ CREATE TABLE IF NOT EXISTS users
|
|||||||
(
|
(
|
||||||
id INTEGER PRIMARY KEY NOT NULL,
|
id INTEGER PRIMARY KEY NOT NULL,
|
||||||
username TEXT NOT NULL,
|
username TEXT NOT NULL,
|
||||||
salt text NOT NULL,
|
saltyhash text NOT NULL,
|
||||||
hash TEXT NOT NULL,
|
|
||||||
permissions INTEGER DEFAULT 0,
|
permissions INTEGER DEFAULT 0,
|
||||||
token TEXT
|
token TEXT NOT NULL
|
||||||
)
|
)
|
||||||
@@ -2,5 +2,7 @@
|
|||||||
"/": "static/html/index.html",
|
"/": "static/html/index.html",
|
||||||
"/results": "static/html/results.html",
|
"/results": "static/html/results.html",
|
||||||
"/archives": "static/html/archives.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"
|
||||||
}
|
}
|
||||||
|
|||||||
80
src/main.rs
80
src/main.rs
@@ -3,7 +3,7 @@ use bytes::Bytes;
|
|||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use futures;
|
use futures;
|
||||||
use http_body_util::{BodyExt, Full};
|
use http_body_util::{BodyExt, Full};
|
||||||
use hyper::body::Incoming;
|
use hyper::body::{Body, Incoming};
|
||||||
use hyper::server::conn::http1;
|
use hyper::server::conn::http1;
|
||||||
use hyper::service::service_fn;
|
use hyper::service::service_fn;
|
||||||
use hyper::{Error, Method, Request, Response, StatusCode};
|
use hyper::{Error, Method, Request, Response, StatusCode};
|
||||||
@@ -20,7 +20,14 @@ use std::str::FromStr;
|
|||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::time::SystemTime;
|
use std::time::SystemTime;
|
||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
|
use rand::distributions::Standard;
|
||||||
|
use argon2::{
|
||||||
|
password_hash::{
|
||||||
|
rand_core::OsRng,
|
||||||
|
PasswordHash, PasswordHasher, PasswordVerifier, SaltString
|
||||||
|
},
|
||||||
|
Argon2
|
||||||
|
};
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
use daemonize::Daemonize;
|
use daemonize::Daemonize;
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
@@ -29,7 +36,7 @@ struct Player {
|
|||||||
name: String,
|
name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize)]
|
||||||
struct Vote {
|
struct Vote {
|
||||||
plus_player_id: i64,
|
plus_player_id: i64,
|
||||||
plus_nickname: String,
|
plus_nickname: String,
|
||||||
@@ -39,6 +46,19 @@ struct Vote {
|
|||||||
minus_reason: String,
|
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)]
|
#[derive(Serialize, Deserialize)]
|
||||||
struct Settings {
|
struct Settings {
|
||||||
database_url: String,
|
database_url: String,
|
||||||
@@ -178,13 +198,27 @@ async fn get_votes(req: &Request<Incoming>, db: Arc<Mutex<SqlitePool>>) -> Vec<V
|
|||||||
|
|
||||||
async fn post(req: Request<Incoming>, db: Arc<Mutex<SqlitePool>>) -> Result<Response<Full<Bytes>>, Error> {
|
async fn post(req: Request<Incoming>, db: Arc<Mutex<SqlitePool>>) -> Result<Response<Full<Bytes>>, Error> {
|
||||||
let path = req.uri().path();
|
let path = req.uri().path();
|
||||||
if path != "/post" {
|
match path {
|
||||||
return Ok(Response::builder().status(StatusCode::BAD_REQUEST).body(Full::new(Bytes::from("Bad Request (Bad Route)"))).unwrap());
|
"/vote" => {
|
||||||
|
post_vote(req, db).await
|
||||||
|
},
|
||||||
|
"/login" => {
|
||||||
|
login(req, db).await
|
||||||
|
},
|
||||||
|
"/register" => {
|
||||||
|
not_found().await
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
not_found().await
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn post_vote(req: Request<Incoming>, db: Arc<Mutex<SqlitePool>>) -> Result<Response<Full<Bytes>>, Error> {
|
||||||
let body = req.into_body().collect().await?;
|
let body = req.into_body().collect().await?;
|
||||||
let data: Result<Vote, serde_json::Error> = from_reader(body.aggregate().reader());
|
let data: Result<Vote, serde_json::Error> = from_reader(body.aggregate().reader());
|
||||||
if data.is_err() {
|
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 vote = data.unwrap();
|
||||||
let timestamp: DateTime<Utc> = DateTime::from(SystemTime::now());
|
let timestamp: DateTime<Utc> = DateTime::from(SystemTime::now());
|
||||||
@@ -206,6 +240,40 @@ async fn post(req: Request<Incoming>, db: Arc<Mutex<SqlitePool>>) -> Result<Resp
|
|||||||
Ok(Response::builder().body(Full::new(Bytes::new())).unwrap())
|
Ok(Response::builder().body(Full::new(Bytes::new())).unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn login(req: Request<Incoming>, db: Arc<Mutex<SqlitePool>>) -> Result<Response<Full<Bytes>>, Error> {
|
||||||
|
let body = req.into_body().collect().await;
|
||||||
|
let data: Result<Login, serde_json::Error> = 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<Incoming>, db: Arc<Mutex<SqlitePool>>) -> Result<Response<Full<Bytes>>, Error> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
async fn not_found() -> Result<Response<Full<Bytes>>, Error> {
|
async fn not_found() -> Result<Response<Full<Bytes>>, Error> {
|
||||||
let mut file_path = env::current_dir().expect("Could not get app directory.");
|
let mut file_path = env::current_dir().expect("Could not get app directory.");
|
||||||
file_path.push("static/html/404.html");
|
file_path.push("static/html/404.html");
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ async function main() {
|
|||||||
true, "warning");
|
true, "warning");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (await fetch("/post", {
|
if (await fetch("/vote", {
|
||||||
method: "post", body: JSON.stringify(vote)
|
method: "post", body: JSON.stringify(vote)
|
||||||
})
|
})
|
||||||
.then(r => r.status) === 200) {
|
.then(r => r.status) === 200) {
|
||||||
|
|||||||
Reference in New Issue
Block a user