2023-10-10 19:45:18 +02:00
|
|
|
use crate::jwt::token_factory;
|
2023-10-18 04:04:16 +02:00
|
|
|
use crate::ServerState;
|
2023-10-10 00:03:04 +02:00
|
|
|
|
2023-10-20 20:57:58 +02:00
|
|
|
use actix_web::web::Data;
|
|
|
|
use actix_web::{post, web::Json, HttpResponse, Responder, Result};
|
2023-10-10 19:45:18 +02:00
|
|
|
use argon2::password_hash::rand_core::OsRng;
|
2023-10-10 00:03:04 +02:00
|
|
|
use argon2::password_hash::SaltString;
|
2023-10-10 19:45:18 +02:00
|
|
|
use argon2::password_hash::*;
|
|
|
|
use argon2::Argon2;
|
|
|
|
use argon2::PasswordHasher;
|
|
|
|
use argon2::PasswordVerifier;
|
2023-10-09 19:21:55 +02:00
|
|
|
use log::*;
|
|
|
|
use serde::{Deserialize, Serialize};
|
2023-10-20 20:57:58 +02:00
|
|
|
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
|
|
pub struct LoginData {
|
|
|
|
username: String,
|
|
|
|
password: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
|
|
struct LoginResponse {
|
|
|
|
username: String,
|
|
|
|
token: String,
|
|
|
|
}
|
2023-10-09 19:21:55 +02:00
|
|
|
|
2023-10-10 00:03:04 +02:00
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
|
|
pub struct RegisterData {
|
|
|
|
username: String,
|
|
|
|
password: String,
|
|
|
|
captcha: String,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[post("/register")]
|
2023-10-18 04:04:16 +02:00
|
|
|
pub async fn register(
|
|
|
|
data: Json<RegisterData>,
|
|
|
|
state: Data<ServerState>,
|
|
|
|
) -> Result<impl Responder> {
|
2023-10-20 20:57:58 +02:00
|
|
|
// First check if the user already exists
|
|
|
|
let exists = sqlx::query!(
|
|
|
|
"SELECT username FROM users WHERE username = ?",
|
|
|
|
data.username
|
|
|
|
)
|
|
|
|
.fetch_one(&state.pool)
|
|
|
|
.await
|
|
|
|
.ok()
|
|
|
|
.map(|row| row.username);
|
|
|
|
|
|
|
|
// Bail out if the user already exists
|
|
|
|
if exists.is_some() {
|
2023-10-10 00:03:04 +02:00
|
|
|
info!("User \"{}\" already exists", data.username);
|
|
|
|
return Ok(HttpResponse::BadRequest().json("Error"));
|
|
|
|
}
|
|
|
|
|
2023-10-20 20:57:58 +02:00
|
|
|
// Unwrapping here because if this fails, we have a serious problem
|
|
|
|
let phc_hash = Argon2::default()
|
|
|
|
.hash_password(data.password.as_bytes(), &SaltString::generate(&mut OsRng))
|
|
|
|
.unwrap()
|
|
|
|
.to_string();
|
2023-10-10 00:03:04 +02:00
|
|
|
|
2023-10-20 20:57:58 +02:00
|
|
|
// Insert our new user into the database
|
|
|
|
sqlx::query!(
|
|
|
|
"INSERT INTO users (username, password) VALUES (?, ?)",
|
|
|
|
data.username,
|
|
|
|
phc_hash
|
|
|
|
)
|
|
|
|
.execute(&state.pool)
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
info!("User: {} registered", data.username);
|
2023-10-10 00:03:04 +02:00
|
|
|
Ok(HttpResponse::Ok().json("User registered"))
|
|
|
|
}
|
2023-10-10 17:12:47 +02:00
|
|
|
|
2023-10-20 20:57:58 +02:00
|
|
|
#[post("/login")]
|
|
|
|
pub async fn login(data: Json<LoginData>, state: Data<ServerState>) -> Result<impl Responder> {
|
|
|
|
let uname = data.username.clone();
|
|
|
|
let q = sqlx::query!("SELECT password FROM users WHERE username = ?", uname)
|
|
|
|
.fetch_one(&state.pool)
|
|
|
|
.await
|
|
|
|
.ok();
|
2023-10-10 17:12:47 +02:00
|
|
|
|
2023-10-20 20:57:58 +02:00
|
|
|
if q.is_none() {
|
|
|
|
info!("User \"{}\" failed to log in", data.username);
|
|
|
|
return Ok(HttpResponse::BadRequest().json("Error"));
|
|
|
|
}
|
2023-10-10 17:12:47 +02:00
|
|
|
|
2023-10-20 20:57:58 +02:00
|
|
|
let phc_password = q.unwrap().password;
|
|
|
|
let phc_password = PasswordHash::new(&phc_password).unwrap_or_else(|_| {
|
|
|
|
warn!(
|
|
|
|
"Invalid hash for user {} fetched from database (not a valid PHC string)",
|
|
|
|
data.username
|
|
|
|
);
|
|
|
|
panic!();
|
|
|
|
});
|
2023-10-10 17:12:47 +02:00
|
|
|
|
2023-10-20 20:57:58 +02:00
|
|
|
match Argon2::default().verify_password(data.password.as_bytes(), &phc_password) {
|
|
|
|
Ok(_) => {
|
|
|
|
info!("User {} logged in", data.username);
|
|
|
|
let token = token_factory(&data.username).unwrap();
|
|
|
|
println!("{:?}", token);
|
|
|
|
return Ok(HttpResponse::Ok().json(LoginResponse {
|
|
|
|
username: data.username.clone(),
|
|
|
|
token: token,
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
Err(_) => {
|
|
|
|
info!("User \"{}\" failed to log in", data.username);
|
|
|
|
return Ok(HttpResponse::BadRequest().json("Error"));
|
2023-10-10 17:12:47 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|