Create user endpoint perfected and finished

This commit is contained in:
franklinblanco 2022-06-27 17:54:51 -04:00
parent a71402aad0
commit 964e655e1c
13 changed files with 96 additions and 23 deletions

View File

@ -13,4 +13,5 @@ sqlx = { version = "0.5", features = [ "runtime-tokio-rustls", "mysql", "chrono"
actix-web = "4" actix-web = "4"
chrono = { version = "0.4", features = [ "serde" ] } chrono = { version = "0.4", features = [ "serde" ] }
ring = "0.16.20" ring = "0.16.20"
data-encoding = "2.3.2" data-encoding = "2.3.2"
futures-util = "0.3"

View File

@ -4,6 +4,6 @@ CREATE TABLE IF NOT EXISTS user (
last_updated TIMESTAMP NOT NULL, last_updated TIMESTAMP NOT NULL,
email VARCHAR(255) NOT NULL, email VARCHAR(255) NOT NULL,
name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL,
password VARCHAR(255) NOT NULL, password TEXT NOT NULL,
salt VARCHAR(255) NOT NULL salt TEXT NOT NULL
) )

View File

@ -3,6 +3,6 @@ CREATE TABLE IF NOT EXISTS token (
user_id INT NOT NULL, user_id INT NOT NULL,
time_created TIMESTAMP NOT NULL, time_created TIMESTAMP NOT NULL,
last_updated TIMESTAMP NOT NULL, last_updated TIMESTAMP NOT NULL,
auth_token VARCHAR(255) NOT NULL, auth_token TEXT NOT NULL,
refresh_token VARCHAR(255) NOT NULL refresh_token TEXT NOT NULL
) )

View File

@ -3,6 +3,6 @@ CREATE TABLE IF NOT EXISTS token (
user_id INT NOT NULL, user_id INT NOT NULL,
time_created TIMESTAMP NOT NULL, time_created TIMESTAMP NOT NULL,
last_updated TIMESTAMP NOT NULL, last_updated TIMESTAMP NOT NULL,
auth_token VARCHAR(255) NOT NULL, auth_token TEXT NOT NULL,
refresh_token VARCHAR(255) NOT NULL refresh_token TEXT NOT NULL
) )

View File

@ -4,6 +4,6 @@ CREATE TABLE IF NOT EXISTS user (
last_updated TIMESTAMP NOT NULL, last_updated TIMESTAMP NOT NULL,
email VARCHAR(255) NOT NULL, email VARCHAR(255) NOT NULL,
name VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL,
password VARCHAR(255) NOT NULL, password TEXT NOT NULL,
salt VARCHAR(255) NOT NULL salt VARCHAR(255) NOT NULL
) )

View File

@ -1,2 +1,3 @@
pub mod main_dao; pub mod main_dao;
pub mod user_dao; pub mod user_dao;
pub mod token_dao;

7
src/dao/token_dao.rs Normal file
View File

@ -0,0 +1,7 @@
use sqlx::{MySqlConnection, mysql::MySqlQueryResult};
use crate::r#do::token::Token;
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
}

View File

@ -1,2 +1,3 @@
pub mod shared_state; pub mod shared_state;
pub mod user; pub mod user;
pub mod token;

24
src/do/token.rs Normal file
View File

@ -0,0 +1,24 @@
use chrono::NaiveDateTime;
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize)]
pub struct Token {
pub id: i32,
pub user_id: i32,
pub time_created: Option<NaiveDateTime>,
pub last_updated: Option<NaiveDateTime>,
pub auth_token: String,
pub refresh_token: String
}
impl Token{
pub fn new(user_id: i32, auth_token: String, refresh_token: String) -> Token{
Token {
id: 0,
user_id,
time_created: None,
last_updated: None,
auth_token,
refresh_token
}
}
}

View File

@ -1,11 +1,10 @@
use std::{sync::Mutex}; use std::{sync::Mutex};
use actix_web::{HttpServer, App, web}; use actix_web::{HttpServer, App, web};
use crate::r#do::shared_state::SharedStateObj; use crate::{r#do::shared_state::SharedStateObj};
use super::user_routes; use super::user_routes;
// This function is to be used in case code is meant to be run after server startup // This function is to be used in case code is meant to be run after server startup
pub fn after_startup_fn(){ pub fn after_startup_fn() {
println!("{}", "Started server."); println!("{}", "Started server.");
} }
@ -47,6 +46,6 @@ pub async fn start_all_routes(after_startup_fn_call: &dyn Fn(), state: SharedSta
// Actual server start and after startup call // Actual server start and after startup call
let (server_start_result, _after_startup_value) = let (server_start_result, _after_startup_value) =
tokio::join!(server_future, async {after_startup_fn_call();}); tokio::join!(server_future, async {after_startup_fn_call});
return server_start_result; // Return server return server_start_result; // Return server
} }

View File

@ -1,9 +1,9 @@
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}, dto::{user_dtos::UserForCreationDto, message_resources_dtos::MessageResourceDto}, validation::user_validator, util::hasher}; 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};
/*#[get("/user/{id}")] /*#[get("/user/{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 {
@ -38,13 +38,27 @@ pub async fn create_user(incoming_user: web::Json<UserForCreationDto>, db_conn:
// Try to insert user in DB // Try to 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;
},
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().json(web::Json(()))
}}; }};
// TODO: 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 mut token_to_insert =
Token::new(user_to_insert.id,
tokens.get(0).expect("msg").to_string(),
tokens.get(1).expect("msg").to_string()
);
match insert_token(&mut db_conn.lock().unwrap(), &token_to_insert).await{
Ok(resultrs) => {token_to_insert.id = resultrs.last_insert_id() as i32},
Err(e) => {panic!("{e}")}
}
// All good? Send an OK! // All good? Send an OK!
HttpResponse::Ok().body(()) HttpResponse::Ok().json(web::Json(token_to_insert))
} }

View File

@ -1,9 +1,35 @@
use std::{num::NonZeroU32}; use std::num::NonZeroU32;
use data_encoding::HEXUPPER; use data_encoding::HEXUPPER;
use ring::{digest, rand::{SecureRandom, SystemRandom}, pbkdf2, error::Unspecified}; use ring::{digest, rand::{SecureRandom, SystemRandom}, pbkdf2, error::Unspecified};
use tokio::task::JoinError;
use crate::dto::hash_dtos::HashResult; use crate::dto::hash_dtos::HashResult;
pub async fn generate_multiple_random_token_with_rng(amount: u8) -> Result<Vec<String>, JoinError> {
// Get a new instance of a Random Number Generator
let rng = SystemRandom::new();
let mut tokens = Vec::with_capacity(amount.into());
for _i in 0 .. amount {
let cloned_rng = rng.clone();
let future_token = async move {
let mut token_arr = [0u8; digest::SHA512_OUTPUT_LEN];
match cloned_rng.fill(&mut token_arr){
Ok(()) => {HEXUPPER.encode(&token_arr)},
Err(_e) => { panic!("Failed to generate random token for some reason.") }
}};
tokens.push(tokio::spawn(future_token));
}
let all_tokens = futures_util::future::join_all(tokens).await;
let all_tokens_solved: Vec<String> = all_tokens.into_iter().map(|result| match result {
Ok(string) => {string},
Err(_e) => {"".to_string()}
}).rev().collect();
Ok(all_tokens_solved)
}
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

View File

@ -19,13 +19,13 @@ fn validate_user_password(password: &String) -> bool {
} }
// User dto SHOULD die here. // User dto SHOULD die here.
pub fn validate_user_for_creation(user: UserForCreationDto, message_resources: &mut Vec<MessageResourceDto>){ pub fn validate_user_for_creation(user: UserForCreationDto, message_resources: &mut Vec<MessageResourceDto>){
if validate_user_email(&user.email) { if !validate_user_email(&user.email) {
message_resources.push(MessageResourceDto::new_from_error_message(ERROR_INVALID_EMAIL)); message_resources.push(MessageResourceDto::new_from_error_message(ERROR_INVALID_EMAIL));
} }
if validate_user_name(&user.name) { if !validate_user_name(&user.name) {
message_resources.push(MessageResourceDto::new_from_error_message(ERROR_INVALID_NAME)); message_resources.push(MessageResourceDto::new_from_error_message(ERROR_INVALID_NAME));
} }
if validate_user_password(&user.password) { if !validate_user_password(&user.password) {
message_resources.push(MessageResourceDto::new_from_error_message(ERROR_INVALID_PASSWORD)); message_resources.push(MessageResourceDto::new_from_error_message(ERROR_INVALID_PASSWORD));
} }
} }