diff --git a/src/main.rs b/src/main.rs index 03b99ec..b047540 100644 --- a/src/main.rs +++ b/src/main.rs @@ -44,6 +44,7 @@ async fn main() -> std::io::Result<()> { ) .service(login) .service(logout) + .service(register) .service(get_case) .service(get_cases) .service(get_item) diff --git a/src/users.rs b/src/users.rs index 9f20003..80aa595 100644 --- a/src/users.rs +++ b/src/users.rs @@ -28,20 +28,21 @@ use crate::AppState; use actix_web::cookie::Cookie; use actix_web::cookie::time::Duration; use actix_web::web::{Data, Json}; -use actix_web::{HttpRequest, HttpResponse, Responder, post}; -use argon2::Argon2; -use argon2::password_hash::{PasswordHash, PasswordVerifier}; +use actix_web::{App, HttpRequest, HttpResponse, Responder, post}; +use argon2::{Argon2, PasswordHasher}; +use argon2::password_hash::{PasswordHash, PasswordVerifier, SaltString}; +use argon2::password_hash::rand_core::OsRng; use jsonwebtoken::{Algorithm, EncodingKey, Header, encode, get_current_timestamp}; use rand::Rng; use rand::distr::Alphanumeric; use serde::{Deserialize, Deserializer}; use serde_json::{json, to_string}; +use sqlx::sqlite::SqliteQueryResult; use sqlx::{Pool, Sqlite, query, query_as}; use std::error::Error; use std::fmt::{Display, Formatter}; use std::fs::File; use std::io::Read; -use sqlx::sqlite::SqliteQueryResult; #[derive(Debug)] struct Password { @@ -94,6 +95,23 @@ impl User { } } } + + async fn create(database: &Pool, hash: PasswordHash<'_>, username: &Username, email: &String) -> Result { + let mut uuid = uuid::Uuid::new_v4().to_string(); + while let Ok(Some(_)) = query!("SELECT id FROM users WHERE 'uuid' = ?1", uuid).fetch_optional(database).await { + uuid = uuid::Uuid::new_v4().to_string(); + } + let hash_string = hash.to_string(); + query_as!(User, + "INSERT INTO users ('uuid', 'username', 'email', 'hash') VALUES (?1, ?2, ?3, ?4) RETURNING *", + uuid, + username.value, + email, + hash_string + ) + .fetch_one(database) + .await + } } struct PasswordError; @@ -245,6 +263,28 @@ async fn logout( } } +#[derive(Deserialize, Debug)] +struct RegisterInfo { + username: Username, + password: Password, + email: String, +} + +#[post("/register")] +async fn register( + app_state: Data, + register_info: Json, +) -> Result> { + if let Ok(Some(_)) = User::fetch_optional(&app_state.database, None, None, Some(®ister_info.username)).await { + return Ok(HttpResponse::InternalServerError().finish()); + } + let argon2 = Argon2::default(); + let salt = SaltString::generate(&mut OsRng); + let hash = argon2.hash_password(register_info.password.value.as_bytes(), &salt)?; + User::create(&app_state.database, hash, ®ister_info.username, ®ister_info.email).await?; + Ok(HttpResponse::Ok().finish()) +} + fn validate_authentication(user: &User, password: &Password) -> Result> { let argon2 = Argon2::default(); let hash = PasswordHash::new(&user.hash)?;