From 964e655e1cb44f6feeba2411414937afdbb6274a Mon Sep 17 00:00:00 2001 From: franklinblanco Date: Mon, 27 Jun 2022 17:54:51 -0400 Subject: [PATCH] Create user endpoint perfected and finished --- Cargo.toml | 3 ++- migrations/20220627132425_userup.sql.sql | 4 ++-- migrations/20220627132445_tokenup.sql.sql | 4 ++-- sql/schema/migrations/tokenup.sql | 4 ++-- sql/schema/migrations/userup.sql | 2 +- src/dao/mod.rs | 3 ++- src/dao/token_dao.rs | 7 ++++++ src/do/mod.rs | 3 ++- src/do/token.rs | 24 +++++++++++++++++++ src/routes/main_router.rs | 7 +++--- src/routes/user_routes.rs | 24 +++++++++++++++---- src/util/hasher.rs | 28 ++++++++++++++++++++++- src/validation/user_validator.rs | 6 ++--- 13 files changed, 96 insertions(+), 23 deletions(-) create mode 100644 src/dao/token_dao.rs create mode 100644 src/do/token.rs diff --git a/Cargo.toml b/Cargo.toml index 4dd5098..5cc5887 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,4 +13,5 @@ sqlx = { version = "0.5", features = [ "runtime-tokio-rustls", "mysql", "chrono" actix-web = "4" chrono = { version = "0.4", features = [ "serde" ] } ring = "0.16.20" -data-encoding = "2.3.2" \ No newline at end of file +data-encoding = "2.3.2" +futures-util = "0.3" \ No newline at end of file diff --git a/migrations/20220627132425_userup.sql.sql b/migrations/20220627132425_userup.sql.sql index a826771..ca5748e 100644 --- a/migrations/20220627132425_userup.sql.sql +++ b/migrations/20220627132425_userup.sql.sql @@ -4,6 +4,6 @@ CREATE TABLE IF NOT EXISTS user ( last_updated TIMESTAMP NOT NULL, email VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, - password VARCHAR(255) NOT NULL, - salt VARCHAR(255) NOT NULL + password TEXT NOT NULL, + salt TEXT NOT NULL ) \ No newline at end of file diff --git a/migrations/20220627132445_tokenup.sql.sql b/migrations/20220627132445_tokenup.sql.sql index 0f3c492..48578aa 100644 --- a/migrations/20220627132445_tokenup.sql.sql +++ b/migrations/20220627132445_tokenup.sql.sql @@ -3,6 +3,6 @@ CREATE TABLE IF NOT EXISTS token ( user_id INT NOT NULL, time_created TIMESTAMP NOT NULL, last_updated TIMESTAMP NOT NULL, - auth_token VARCHAR(255) NOT NULL, - refresh_token VARCHAR(255) NOT NULL + auth_token TEXT NOT NULL, + refresh_token TEXT NOT NULL ) \ No newline at end of file diff --git a/sql/schema/migrations/tokenup.sql b/sql/schema/migrations/tokenup.sql index 0f3c492..48578aa 100644 --- a/sql/schema/migrations/tokenup.sql +++ b/sql/schema/migrations/tokenup.sql @@ -3,6 +3,6 @@ CREATE TABLE IF NOT EXISTS token ( user_id INT NOT NULL, time_created TIMESTAMP NOT NULL, last_updated TIMESTAMP NOT NULL, - auth_token VARCHAR(255) NOT NULL, - refresh_token VARCHAR(255) NOT NULL + auth_token TEXT NOT NULL, + refresh_token TEXT NOT NULL ) \ No newline at end of file diff --git a/sql/schema/migrations/userup.sql b/sql/schema/migrations/userup.sql index a826771..a6e16f2 100644 --- a/sql/schema/migrations/userup.sql +++ b/sql/schema/migrations/userup.sql @@ -4,6 +4,6 @@ CREATE TABLE IF NOT EXISTS user ( last_updated TIMESTAMP NOT NULL, email VARCHAR(255) NOT NULL, name VARCHAR(255) NOT NULL, - password VARCHAR(255) NOT NULL, + password TEXT NOT NULL, salt VARCHAR(255) NOT NULL ) \ No newline at end of file diff --git a/src/dao/mod.rs b/src/dao/mod.rs index e96fb14..8a2ba42 100644 --- a/src/dao/mod.rs +++ b/src/dao/mod.rs @@ -1,2 +1,3 @@ pub mod main_dao; -pub mod user_dao; \ No newline at end of file +pub mod user_dao; +pub mod token_dao; \ No newline at end of file diff --git a/src/dao/token_dao.rs b/src/dao/token_dao.rs new file mode 100644 index 0000000..9e8f611 --- /dev/null +++ b/src/dao/token_dao.rs @@ -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 { + sqlx::query_file!("sql/schema/token/insert.sql", token.user_id, token.auth_token, token.refresh_token).execute(conn).await +} \ No newline at end of file diff --git a/src/do/mod.rs b/src/do/mod.rs index 8494aaf..b6de466 100644 --- a/src/do/mod.rs +++ b/src/do/mod.rs @@ -1,2 +1,3 @@ pub mod shared_state; -pub mod user; \ No newline at end of file +pub mod user; +pub mod token; \ No newline at end of file diff --git a/src/do/token.rs b/src/do/token.rs new file mode 100644 index 0000000..b043e66 --- /dev/null +++ b/src/do/token.rs @@ -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, + pub last_updated: Option, + 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 + } + } +} \ No newline at end of file diff --git a/src/routes/main_router.rs b/src/routes/main_router.rs index cbd1776..9a3be21 100644 --- a/src/routes/main_router.rs +++ b/src/routes/main_router.rs @@ -1,11 +1,10 @@ use std::{sync::Mutex}; use actix_web::{HttpServer, App, web}; -use crate::r#do::shared_state::SharedStateObj; - +use crate::{r#do::shared_state::SharedStateObj}; use super::user_routes; // 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."); } @@ -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 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 } \ No newline at end of file diff --git a/src/routes/user_routes.rs b/src/routes/user_routes.rs index 49c97fb..bda5d57 100644 --- a/src/routes/user_routes.rs +++ b/src/routes/user_routes.rs @@ -1,9 +1,9 @@ use std::sync::Mutex; 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}")] pub async fn get_user_from_db(id: Path, db_conn: Data>) -> HttpResponse { @@ -38,13 +38,27 @@ pub async fn create_user(incoming_user: web::Json, db_conn: // Try to insert user in DB 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) => { println!("Error while inserting user in database from create_user method. Log: {}", error); return HttpResponse::InternalServerError().json(web::Json(())) }}; - // TODO: Create token and send it back. + + // Create token and send it back. + let tokens: Vec = 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! - HttpResponse::Ok().body(()) + HttpResponse::Ok().json(web::Json(token_to_insert)) } \ No newline at end of file diff --git a/src/util/hasher.rs b/src/util/hasher.rs index 9158f54..a1cfc13 100644 --- a/src/util/hasher.rs +++ b/src/util/hasher.rs @@ -1,9 +1,35 @@ -use std::{num::NonZeroU32}; +use std::num::NonZeroU32; use data_encoding::HEXUPPER; use ring::{digest, rand::{SecureRandom, SystemRandom}, pbkdf2, error::Unspecified}; +use tokio::task::JoinError; use crate::dto::hash_dtos::HashResult; +pub async fn generate_multiple_random_token_with_rng(amount: u8) -> Result, 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 = 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{ // Get output length from a sha512 hash diff --git a/src/validation/user_validator.rs b/src/validation/user_validator.rs index 15bbd98..e4dbe38 100644 --- a/src/validation/user_validator.rs +++ b/src/validation/user_validator.rs @@ -19,13 +19,13 @@ fn validate_user_password(password: &String) -> bool { } // User dto SHOULD die here. pub fn validate_user_for_creation(user: UserForCreationDto, message_resources: &mut Vec){ - if validate_user_email(&user.email) { + if !validate_user_email(&user.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)); } - if validate_user_password(&user.password) { + if !validate_user_password(&user.password) { message_resources.push(MessageResourceDto::new_from_error_message(ERROR_INVALID_PASSWORD)); } } \ No newline at end of file