Perfected the create user method, added todo's, started working on token stuff.
This commit is contained in:
parent
964e655e1c
commit
b832b02a2a
@ -1,7 +1,7 @@
|
|||||||
CREATE TABLE IF NOT EXISTS user (
|
CREATE TABLE IF NOT EXISTS user (
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
time_created TIMESTAMP NOT NULL,
|
time_created DATETIME,
|
||||||
last_updated TIMESTAMP NOT NULL,
|
last_updated DATETIME,
|
||||||
email VARCHAR(255) NOT NULL,
|
email VARCHAR(255) NOT NULL,
|
||||||
name VARCHAR(255) NOT NULL,
|
name VARCHAR(255) NOT NULL,
|
||||||
password TEXT NOT NULL,
|
password TEXT NOT NULL,
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
CREATE TABLE IF NOT EXISTS token (
|
CREATE TABLE IF NOT EXISTS token (
|
||||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
user_id INT NOT NULL,
|
user_id INT NOT NULL,
|
||||||
time_created TIMESTAMP NOT NULL,
|
time_created DATETIME,
|
||||||
last_updated TIMESTAMP NOT NULL,
|
last_updated DATETIME,
|
||||||
auth_token TEXT NOT NULL,
|
auth_token TEXT NOT NULL,
|
||||||
refresh_token TEXT NOT NULL
|
refresh_token TEXT NOT NULL
|
||||||
)
|
)
|
3
sql/schema/user/find_with_email.sql
Normal file
3
sql/schema/user/find_with_email.sql
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
SELECT *
|
||||||
|
FROM user
|
||||||
|
WHERE user.email = ?
|
@ -5,3 +5,5 @@ use crate::r#do::token::Token;
|
|||||||
pub async fn insert_token(conn: &mut MySqlConnection, token: &Token) -> Result<MySqlQueryResult, sqlx::Error> {
|
pub async fn insert_token(conn: &mut MySqlConnection, token: &Token) -> Result<MySqlQueryResult, sqlx::Error> {
|
||||||
sqlx::query_file!("sql/schema/token/insert.sql", token.user_id, token.auth_token, token.refresh_token).execute(conn).await
|
sqlx::query_file!("sql/schema/token/insert.sql", token.user_id, token.auth_token, token.refresh_token).execute(conn).await
|
||||||
}
|
}
|
||||||
|
// TODO: Get token with auth token
|
||||||
|
// TODO: Get token with user_id
|
@ -7,6 +7,9 @@ pub async fn insert_user(conn: &mut MySqlConnection, user_to_insert: &User) -> R
|
|||||||
user_to_insert.email, user_to_insert.name, user_to_insert.password, user_to_insert.salt)
|
user_to_insert.email, user_to_insert.name, user_to_insert.password, user_to_insert.salt)
|
||||||
.execute(conn).await
|
.execute(conn).await
|
||||||
}
|
}
|
||||||
|
pub async fn find_user_by_email(conn: &mut MySqlConnection, email: &String) -> Result<User, sqlx::Error>{
|
||||||
|
sqlx::query_file_as!(User, "sql/schema/user/find_with_email.sql", email).fetch_one(conn).await
|
||||||
|
}
|
||||||
//pub async fn _update_user(conn: &mut MySqlConnection, user_to_modify: &User) -> Result<(), sqlx::Error>{
|
//pub async fn _update_user(conn: &mut MySqlConnection, user_to_modify: &User) -> Result<(), sqlx::Error>{
|
||||||
// Ok(())
|
// Ok(())
|
||||||
//}
|
//}
|
||||||
|
@ -6,3 +6,7 @@ pub const ERROR_INVALID_EMAIL: (&str, &str) = ("ERROR.INVALID_EMAIL", "Invalid e
|
|||||||
pub const ERROR_INVALID_NAME: (&str, &str) = ("ERROR.INVALID_NAME", "Invalid name. Names should have at least 4 characters in length and at most 254.");
|
pub const ERROR_INVALID_NAME: (&str, &str) = ("ERROR.INVALID_NAME", "Invalid name. Names should have at least 4 characters in length and at most 254.");
|
||||||
|
|
||||||
pub const ERROR_INVALID_PASSWORD: (&str, &str) = ("ERROR.INVALID_PASSWORD", "Invalid password. Password should have at least 8 characters and at most 128.");
|
pub const ERROR_INVALID_PASSWORD: (&str, &str) = ("ERROR.INVALID_PASSWORD", "Invalid password. Password should have at least 8 characters and at most 128.");
|
||||||
|
|
||||||
|
pub const ERROR_USER_ALREADY_EXISTS: (&str, &str) = ("ERROR.USER_ALREADY_EXISTS", "A user with that email already exists.");
|
||||||
|
|
||||||
|
pub const ERROR_USER_DOES_NOT_EXIST: (&str, &str) = ("ERROR.USER_DOES_NOT_EXIST", "User with this email does not exist.");
|
@ -1,11 +1,12 @@
|
|||||||
|
use core::panic;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
use actix_web::{web::{self, Data}, HttpResponse, post};
|
use actix_web::{web::{self, Data}, HttpResponse, post};
|
||||||
use sqlx::MySqlConnection;
|
use sqlx::MySqlConnection;
|
||||||
|
|
||||||
use crate::{r#do::user::User, dao::{user_dao::{insert_user}, token_dao::insert_token}, dto::{user_dtos::UserForCreationDto, message_resources_dtos::MessageResourceDto}, validation::user_validator, util::hasher, r#do::token::Token};
|
use crate::{r#do::user::User, dao::{user_dao::{insert_user, find_user_by_email}, token_dao::insert_token}, dto::{user_dtos::{UserForCreationDto, UserForLoginDto}, message_resources_dtos::MessageResourceDto}, validation::user_validator, util::hasher, r#do::token::Token, resources::error_messages::{ERROR_USER_ALREADY_EXISTS, ERROR_USER_DOES_NOT_EXIST}};
|
||||||
|
|
||||||
/*#[get("/user/{id}")]
|
/*#[get("/u&ser/{id}")]
|
||||||
pub async fn get_user_from_db(id: Path<i32>, db_conn: Data<Mutex<MySqlConnection>>) -> HttpResponse {
|
pub async fn get_user_from_db(id: Path<i32>, db_conn: Data<Mutex<MySqlConnection>>) -> HttpResponse {
|
||||||
match find_user_by_id(&mut db_conn.lock().unwrap(), *id).await{
|
match find_user_by_id(&mut db_conn.lock().unwrap(), *id).await{
|
||||||
Ok(MySqlQueryResult)
|
Ok(MySqlQueryResult)
|
||||||
@ -28,6 +29,15 @@ pub async fn create_user(incoming_user: web::Json<UserForCreationDto>, db_conn:
|
|||||||
// Validate user
|
// Validate user
|
||||||
user_validator::validate_user_for_creation(incoming_user_obj, &mut message_resources);
|
user_validator::validate_user_for_creation(incoming_user_obj, &mut message_resources);
|
||||||
|
|
||||||
|
// Find if user exists
|
||||||
|
match find_user_by_email(&mut db_conn.lock().unwrap(), &user_to_insert.email).await{
|
||||||
|
Ok(_usr) => {
|
||||||
|
message_resources.push(MessageResourceDto::new_from_error_message(ERROR_USER_ALREADY_EXISTS));
|
||||||
|
return HttpResponse::BadRequest().json(web::Json(message_resources));
|
||||||
|
},
|
||||||
|
Err(_e) => {}
|
||||||
|
};
|
||||||
|
|
||||||
// If validation gave any errors blow up and send them back to the client
|
// If validation gave any errors blow up and send them back to the client
|
||||||
if message_resources.len() > 0 { return HttpResponse::BadRequest().json(web::Json(message_resources)); }
|
if message_resources.len() > 0 { return HttpResponse::BadRequest().json(web::Json(message_resources)); }
|
||||||
|
|
||||||
@ -36,24 +46,25 @@ pub async fn create_user(incoming_user: web::Json<UserForCreationDto>, db_conn:
|
|||||||
user_to_insert.password = hash_result.hash;
|
user_to_insert.password = hash_result.hash;
|
||||||
user_to_insert.salt = hash_result.salt;
|
user_to_insert.salt = hash_result.salt;
|
||||||
|
|
||||||
// Try to insert user in DB
|
// Insert user in DB
|
||||||
match insert_user(&mut db_conn.lock().unwrap(), &user_to_insert).await{
|
match insert_user(&mut db_conn.lock().unwrap(), &user_to_insert).await{
|
||||||
Ok(resultrs) => {
|
Ok(resultrs) => {
|
||||||
user_to_insert.id = resultrs.last_insert_id() as i32;
|
user_to_insert.id = resultrs.last_insert_id() as i32;
|
||||||
},
|
},
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
println!("Error while inserting user in database from create_user method. Log: {}", error);
|
println!("Error while inserting user in database from create_user method. Log: {}", error);
|
||||||
return HttpResponse::InternalServerError().json(web::Json(()))
|
return HttpResponse::InternalServerError().finish();
|
||||||
}};
|
}};
|
||||||
|
|
||||||
// Create token and send it back.
|
// Create token and send it back.
|
||||||
let tokens: Vec<String> = hasher::generate_multiple_random_token_with_rng(2).await.expect("msg");
|
let tokens: Vec<String> = hasher::generate_multiple_random_token_with_rng(2).await.expect("Error creating multiple random tokens.");
|
||||||
let mut token_to_insert =
|
let mut token_to_insert =
|
||||||
Token::new(user_to_insert.id,
|
Token::new(user_to_insert.id,
|
||||||
tokens.get(0).expect("msg").to_string(),
|
tokens.get(0).expect("Error. Token doesn't exist in list.").to_string(),
|
||||||
tokens.get(1).expect("msg").to_string()
|
tokens.get(1).expect("Error. Token doesn't exist in list.").to_string()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Insert token in DB
|
||||||
match insert_token(&mut db_conn.lock().unwrap(), &token_to_insert).await{
|
match insert_token(&mut db_conn.lock().unwrap(), &token_to_insert).await{
|
||||||
Ok(resultrs) => {token_to_insert.id = resultrs.last_insert_id() as i32},
|
Ok(resultrs) => {token_to_insert.id = resultrs.last_insert_id() as i32},
|
||||||
Err(e) => {panic!("{e}")}
|
Err(e) => {panic!("{e}")}
|
||||||
@ -62,3 +73,27 @@ pub async fn create_user(incoming_user: web::Json<UserForCreationDto>, db_conn:
|
|||||||
// All good? Send an OK!
|
// All good? Send an OK!
|
||||||
HttpResponse::Ok().json(web::Json(token_to_insert))
|
HttpResponse::Ok().json(web::Json(token_to_insert))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[post("/user/auth/password")]
|
||||||
|
pub async fn authenticate_user_with_password(incoming_user: web::Json<UserForLoginDto>, db_conn: Data<Mutex<MySqlConnection>>) -> HttpResponse {
|
||||||
|
let mut message_resources: Vec<MessageResourceDto> = Vec::new();
|
||||||
|
let incoming_user_obj = incoming_user.0;
|
||||||
|
|
||||||
|
// Validate user email & password
|
||||||
|
user_validator::validate_user_for_password_authentication(&incoming_user_obj, &mut message_resources);
|
||||||
|
|
||||||
|
// If validation gave any errors blow up and send them back to the client
|
||||||
|
if message_resources.len() > 0 { return HttpResponse::BadRequest().json(web::Json(message_resources)); }
|
||||||
|
|
||||||
|
// If user exists get it, if it doesn't blow up to the client
|
||||||
|
let persisted_user = match find_user_by_email(&mut db_conn.lock().unwrap(), &incoming_user_obj.email).await {
|
||||||
|
Ok(rs) => {rs},
|
||||||
|
Err(_e) => {
|
||||||
|
message_resources.push(MessageResourceDto::new_from_error_message(ERROR_USER_DOES_NOT_EXIST));
|
||||||
|
return HttpResponse::BadRequest().json(web::Json(message_resources));
|
||||||
|
}};
|
||||||
|
|
||||||
|
// TODO: create new token & check for all the others that may have expired.s
|
||||||
|
|
||||||
|
HttpResponse::Ok().finish()
|
||||||
|
}
|
||||||
|
@ -5,6 +5,8 @@ use tokio::task::JoinError;
|
|||||||
|
|
||||||
use crate::dto::hash_dtos::HashResult;
|
use crate::dto::hash_dtos::HashResult;
|
||||||
|
|
||||||
|
const SALT_ROUNDS: u32 = 1000;
|
||||||
|
|
||||||
pub async fn generate_multiple_random_token_with_rng(amount: u8) -> Result<Vec<String>, JoinError> {
|
pub async fn generate_multiple_random_token_with_rng(amount: u8) -> Result<Vec<String>, JoinError> {
|
||||||
// Get a new instance of a Random Number Generator
|
// Get a new instance of a Random Number Generator
|
||||||
let rng = SystemRandom::new();
|
let rng = SystemRandom::new();
|
||||||
@ -34,7 +36,7 @@ pub async fn generate_multiple_random_token_with_rng(amount: u8) -> Result<Vec<S
|
|||||||
pub fn hash_password(password: &String) -> HashResult{
|
pub fn hash_password(password: &String) -> HashResult{
|
||||||
// Get output length from a sha512 hash
|
// Get output length from a sha512 hash
|
||||||
const CREDENTIAL_LEN: usize = digest::SHA512_OUTPUT_LEN;
|
const CREDENTIAL_LEN: usize = digest::SHA512_OUTPUT_LEN;
|
||||||
let n_iter = NonZeroU32::new(100_000).unwrap();
|
let n_iter = NonZeroU32::new(SALT_ROUNDS).unwrap();
|
||||||
let rng = SystemRandom::new();
|
let rng = SystemRandom::new();
|
||||||
|
|
||||||
// Create empty 64-bit byte array for the salt
|
// Create empty 64-bit byte array for the salt
|
||||||
@ -66,7 +68,7 @@ fn _verify_password_hash(input_password: &String, salt: &String, actual_hash: &S
|
|||||||
|
|
||||||
// Get output length from a sha512 hash
|
// Get output length from a sha512 hash
|
||||||
const CREDENTIAL_LEN: usize = digest::SHA512_OUTPUT_LEN;
|
const CREDENTIAL_LEN: usize = digest::SHA512_OUTPUT_LEN;
|
||||||
let n_iter = NonZeroU32::new(100_000).unwrap();
|
let n_iter = NonZeroU32::new(SALT_ROUNDS).unwrap();
|
||||||
|
|
||||||
// Verify the user-inputted password hashed with the salt is the same.
|
// Verify the user-inputted password hashed with the salt is the same.
|
||||||
pbkdf2::verify(
|
pbkdf2::verify(
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
dto::{ user_dtos::UserForCreationDto, message_resources_dtos::MessageResourceDto },
|
dto::{ user_dtos::{UserForCreationDto, UserForLoginDto}, message_resources_dtos::MessageResourceDto },
|
||||||
resources::{ variable_lengths::*, error_messages::* }
|
resources::{ variable_lengths::*, error_messages::* }
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -29,3 +29,11 @@ pub fn validate_user_for_creation(user: UserForCreationDto, message_resources: &
|
|||||||
message_resources.push(MessageResourceDto::new_from_error_message(ERROR_INVALID_PASSWORD));
|
message_resources.push(MessageResourceDto::new_from_error_message(ERROR_INVALID_PASSWORD));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn validate_user_for_password_authentication(user: &UserForLoginDto, message_resources: &mut Vec<MessageResourceDto>){
|
||||||
|
if !validate_user_email(&user.email) {
|
||||||
|
message_resources.push(MessageResourceDto::new_from_error_message(ERROR_INVALID_EMAIL));
|
||||||
|
}
|
||||||
|
if !validate_user_password(&user.password) {
|
||||||
|
message_resources.push(MessageResourceDto::new_from_error_message(ERROR_INVALID_PASSWORD));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user