From c31586573b3d2228dae396f14776b05dd10395e9 Mon Sep 17 00:00:00 2001 From: Franklin Date: Wed, 20 Sep 2023 23:31:16 -0400 Subject: [PATCH] Starting to see services being made --- Cargo.lock | 1 + Cargo.toml | 3 +- src/dao/credential.rs | 7 ++++ src/resources/error_messages.rs | 7 +++- src/service/user.rs | 73 ++++++++++++++++++++++++++++++++- src/utils/hasher.rs | 13 ++++-- 6 files changed, 97 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9ceefb3..e986ee6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1609,6 +1609,7 @@ dependencies = [ "chrono", "data-encoding", "futures-util", + "log", "ring", "serde", "sqlx", diff --git a/Cargo.toml b/Cargo.toml index 62c7bab..5b55a19 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,4 +16,5 @@ sqlx = { version = "0.7", features = [ "runtime-tokio", "tls-rustls", "postgres" chrono = { version = "0.4", features = [ "serde" ] } ring = "0.16.20" data-encoding = "2.3.2" -futures-util = "0.3" \ No newline at end of file +futures-util = "0.3" +log = "0.4.19" \ No newline at end of file diff --git a/src/dao/credential.rs b/src/dao/credential.rs index bfee323..f4e0516 100644 --- a/src/dao/credential.rs +++ b/src/dao/credential.rs @@ -32,3 +32,10 @@ pub async fn fetch_user_credentials( ) -> Result, Error> { sqlx::query_as(r#"SELECT user_id, credential_type as "credential_type: _", credential, validated, time_created, last_updated FROM credential WHERE user_id = $1 "#).bind(user_id).fetch_all(conn).await } + +pub async fn get_credential( + conn: &PgPool, + credential: String, +) -> Result, Error> { + sqlx::query_as(r#"SELECT user_id, credential_type as "credential_type: _", credential, validated, time_created, last_updated FROM credential WHERE credential = $1"#).bind(credential).fetch_optional(conn).await +} diff --git a/src/resources/error_messages.rs b/src/resources/error_messages.rs index 54a8c7a..da260a8 100644 --- a/src/resources/error_messages.rs +++ b/src/resources/error_messages.rs @@ -26,7 +26,7 @@ pub const ERROR_INVALID_PASSWORD: (&str, &str) = ( pub const ERROR_USER_ALREADY_EXISTS: (&str, &str) = ( "ERROR.USER_ALREADY_EXISTS", - "A user with that email already exists.", + "A user with that credential already exists.", ); pub const ERROR_USER_DOES_NOT_EXIST: (&str, &str) = ( @@ -60,3 +60,8 @@ pub const ERROR_CREATING_TOKEN: (&str, &str) = ( "ERROR.CREATING_TOKEN", "The server had an error creating the auth tokens.", ); + +pub const ERROR_TOO_MANY_CREDENTIALS: (&str, &str) = ( + "ERROR.TOO_MANY_CREDENTIALS", + "Only up to 3 credentials are allowed. One of each type.", +); diff --git a/src/service/user.rs b/src/service/user.rs index f1c6558..a7d8dd3 100644 --- a/src/service/user.rs +++ b/src/service/user.rs @@ -1,5 +1,76 @@ +use std::error::Error; +use log::{error, log}; +use crate::dao::credential::get_credential; +use crate::domain::credential::Credential; use crate::dto::users::UserRegisterPayload; +use crate::resources::error_messages::{ERROR_TOO_MANY_CREDENTIALS, ERROR_USER_ALREADY_EXISTS, ErrorResource}; +use crate::validation::user_validator::validate_user_for_creation; -pub async fn register_user(db_conn: &sqlx::PgPool, user: UserRegisterPayload) -> Result<(), ()> { +pub async fn register_user(db_conn: &sqlx::PgPool, user: UserRegisterPayload) -> Result<(), Vec> { + let mut error_resources: Vec = Vec::new(); + // Validate user + validate_user_for_creation(&user, &mut error_resources); + // Find if user exists + if user.credentials.len() > 3 { + error_resources.push(ERROR_TOO_MANY_CREDENTIALS); + + } + for credential_dto in user.credentials.iter() { + match get_credential( + &db_conn, + credential_dto.credential.clone(), + ) + .await + { + Ok(credential_opt) => { + match credential_opt { + None => {} + Some(_) => { + error_resources.push( + ERROR_USER_ALREADY_EXISTS); + } + } + } + Err(e) => { + error!("{}", e); + error_resources.push(("ERROR.DATABASE_ERROR", "")); + } + }; + } + // If validation gave any errors blow up and send them back to the client + if error_resources.len() > 0 { + return Err(error_resources); + } + + /* TODO: + // Get salt and hashed password from hashing function then give the results to the user + let hash_result = hasher::hash_password(&user_to_insert.password); + user_to_insert.password = hash_result.hash; + user_to_insert.salt = hash_result.salt; + + // Insert user in DB + match insert_user(&db_conn, &user_to_insert).await{ + Ok(resultrs) => { + user_to_insert.id = resultrs.last_insert_id() as u32; + }, + Err(error) => { + println!("Error while inserting user in database from create_user method. Log: {}", error); + return HttpResponse::InternalServerError().finish(); + }}; + + // Create token and send it back. + let tokens: Vec = hasher::generate_multiple_random_token_with_rng(2).await.expect("Error creating multiple random tokens."); + let mut token_to_insert = + Token::new(user_to_insert.id, + tokens.get(0).expect("Error. Token doesn't exist in list.").to_string(), + tokens.get(1).expect("Error. Token doesn't exist in list.").to_string() + ); + + // Insert token in DB + match insert_token(&db_conn, &token_to_insert).await{ + Ok(resultrs) => {token_to_insert.id = resultrs.last_insert_id() as u32}, + Err(_e) => {return HttpResponse::InternalServerError().finish()} + } +*/ Ok(()) } diff --git a/src/utils/hasher.rs b/src/utils/hasher.rs index 5cadd82..1ff3b56 100644 --- a/src/utils/hasher.rs +++ b/src/utils/hasher.rs @@ -9,7 +9,9 @@ use tokio::task::JoinError; const SALT_ROUNDS: u32 = 1000; -pub async fn generate_multiple_random_token_with_rng(amount: u8) -> Result, JoinError> { +pub(crate) 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(); @@ -20,7 +22,7 @@ pub async fn generate_multiple_random_token_with_rng(amount: u8) -> Result BASE64.encode(&token_arr), //TODO: Remove this panic, make your own error and fix this + Ok(()) => BASE64.encode(&token_arr), Err(_e) => { panic!("Failed to generate random token for some reason.") } @@ -42,7 +44,10 @@ pub async fn generate_multiple_random_token_with_rng(amount: u8) -> Result HashResult { +pub(crate) fn hash_password_with_existing_salt( + password: &String, + input_salt: &String, +) -> HashResult { // Get output length from a sha512 hash const CREDENTIAL_LEN: usize = digest::SHA512_OUTPUT_LEN; let n_iter = NonZeroU32::new(SALT_ROUNDS).unwrap(); @@ -65,7 +70,7 @@ pub fn hash_password_with_existing_salt(password: &String, input_salt: &String) HashResult::new(BASE64.encode(&salt), BASE64.encode(&pbkdf2_hash)) } -pub fn hash_password(password: &String) -> HashResult { +pub(crate) fn hash_password(password: &String) -> HashResult { // Get output length from a sha512 hash const CREDENTIAL_LEN: usize = digest::SHA512_OUTPUT_LEN; let n_iter = NonZeroU32::new(SALT_ROUNDS).unwrap();