From 890e32de4385c4b20db7e35115c2c27737994603 Mon Sep 17 00:00:00 2001 From: Franklin Date: Thu, 21 Sep 2023 14:06:27 -0400 Subject: [PATCH] Password login, get credentials --- src/dao/credential.rs | 6 +- src/dao/token.rs | 8 +- src/dao/user.rs | 10 +- src/resources/error_messages.rs | 5 + src/service/user.rs | 193 +++++++++++++++++++++---------- src/validation/user_validator.rs | 4 +- 6 files changed, 148 insertions(+), 78 deletions(-) diff --git a/src/dao/credential.rs b/src/dao/credential.rs index 5b913e8..84a0fb7 100644 --- a/src/dao/credential.rs +++ b/src/dao/credential.rs @@ -3,7 +3,7 @@ use crate::dto::credential::CredentialDto; use chrono::Utc; use sqlx::{Error, PgConnection, PgPool}; -pub async fn insert_credential( +pub(crate) async fn insert_credential( conn: &mut PgConnection, credential_dto: CredentialDto, user_id: &i32, @@ -21,14 +21,14 @@ pub async fn insert_credential( .await } -pub async fn fetch_user_credentials( +pub(crate) async fn fetch_user_credentials( conn: &mut PgConnection, user_id: &i32, ) -> Result, Error> { sqlx::query_as(r#"SELECT user_id, 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( +pub(crate) async fn get_credential( conn: &mut PgConnection, credential: String, ) -> Result, Error> { diff --git a/src/dao/token.rs b/src/dao/token.rs index c5459be..4f0c71f 100644 --- a/src/dao/token.rs +++ b/src/dao/token.rs @@ -2,14 +2,14 @@ use crate::domain::token::Token; use chrono::Utc; use sqlx::{Error, PgConnection, PgPool}; -pub async fn insert_token(conn: &mut PgConnection, token: Token) -> Result { +pub(crate) async fn insert_token(conn: &mut PgConnection, token: Token) -> Result { sqlx::query_as(r#"INSERT INTO token ( user_id, auth_token, refresh_token, time_created, last_updated) VALUES ($1, $2, $3, $4, $4) RETURNING *;"#) .bind(token.user_id).bind(token.auth_token).bind(token.refresh_token).bind(token.time_created) .fetch_one(conn).await } -pub async fn update_token( +pub(crate) async fn update_token( conn: &mut PgConnection, token_id: &i32, refresh_token: String, @@ -28,14 +28,14 @@ pub async fn update_token( .await } -pub async fn remove_token(conn: &mut PgConnection, token_id: &i32) -> Result, Error> { +pub(crate) async fn remove_token(conn: &mut PgConnection, token_id: &i32) -> Result, Error> { sqlx::query_as(r#"DELETE FROM token WHERE id = $1 RETURNING *;"#) .bind(token_id) .fetch_optional(conn) .await } -pub async fn validate_user_token( +pub(crate) async fn validate_user_token( conn: &mut PgConnection, user_id: &i32, auth_token: String, diff --git a/src/dao/user.rs b/src/dao/user.rs index 76187a8..4889719 100644 --- a/src/dao/user.rs +++ b/src/dao/user.rs @@ -1,7 +1,7 @@ use crate::domain::user::User; -use sqlx::{PgConnection, PgPool, Postgres, Transaction}; +use sqlx::PgConnection; -pub async fn insert_user(conn: &mut PgConnection, user: User) -> Result { +pub(crate) async fn insert_user(conn: &mut PgConnection, user: User) -> Result { sqlx::query_as( r#" INSERT INTO "user" (name, password, salt, time_created, last_updated) @@ -16,7 +16,7 @@ pub async fn insert_user(conn: &mut PgConnection, user: User) -> Result Result, sqlx::Error> { +pub(crate) async fn get_user_with_id(conn: &mut PgConnection, user_id: &i32) -> Result, sqlx::Error> { sqlx::query_as( r#" SELECT * FROM "user" where id = $1; @@ -27,7 +27,7 @@ pub async fn get_user_with_id(conn: &mut PgConnection, user_id: &i32) -> Result< .await } -pub async fn update_user(conn: &mut PgConnection, user: User) -> Result { +pub(crate) async fn update_user(conn: &mut PgConnection, user: User) -> Result { sqlx::query_as( r#" UPDATE "user" SET @@ -44,7 +44,7 @@ pub async fn update_user(conn: &mut PgConnection, user: User) -> Result Result, sqlx::Error> { +pub(crate) async fn delete_user(conn: &mut PgConnection, user_id: &i32) -> Result, sqlx::Error> { sqlx::query_as( r#" DELETE FROM "user" where id = $1 RETURNING *; diff --git a/src/resources/error_messages.rs b/src/resources/error_messages.rs index 4a38e13..a4f2a1f 100644 --- a/src/resources/error_messages.rs +++ b/src/resources/error_messages.rs @@ -66,6 +66,11 @@ pub const ERROR_TOO_MANY_CREDENTIALS: (&str, &str) = ( "Only up to 3 credentials are allowed. One of each type.", ); +pub const ERROR_CREDENTIAL_DOES_NOT_EXIST: (&str, &str) = ( + "ERROR.CREDENTIAL_DOES_NOT_EXIST", + "Credential specified does not exist.", +); + pub const ERROR_TOKEN_NOT_CREATED: (&str, &str) = ( "ERROR.TOKEN_NOT_CREATED", "Token futures were joined but not created correctly.", diff --git a/src/service/user.rs b/src/service/user.rs index 66ec942..13d2592 100644 --- a/src/service/user.rs +++ b/src/service/user.rs @@ -1,17 +1,17 @@ use chrono::Utc; -use log::{debug, error, log}; -use sqlx::{Error, Postgres, Transaction}; -use sqlx::pool::PoolConnection; -use crate::dao::credential::{get_credential, insert_credential}; +use log::{debug, error}; +use sqlx::{PgConnection, Postgres, Transaction}; +use crate::dao::credential::{fetch_user_credentials, get_credential, insert_credential}; use crate::dao::token::{insert_token, validate_user_token}; use crate::dao::user::{get_user_with_id, insert_user}; +use crate::domain::credential::Credential; use crate::domain::token::Token; use crate::domain::user::User; use crate::dto::token::AuthenticateUserDto; -use crate::dto::users::UserRegisterPayload; -use crate::resources::error_messages::{ERROR_EXPIRED_TOKEN, ERROR_INCORRECT_TOKEN, ERROR_TOKEN_NOT_CREATED, ERROR_TOO_MANY_CREDENTIALS, ERROR_USER_ALREADY_EXISTS, ERROR_USER_DOES_NOT_EXIST, ErrorResource}; +use crate::dto::users::{UserLoginPayload, UserRegisterPayload}; +use crate::resources::error_messages::{ERROR_CREDENTIAL_DOES_NOT_EXIST, ERROR_EXPIRED_TOKEN, ERROR_INCORRECT_TOKEN, ERROR_TOKEN_NOT_CREATED, ERROR_TOO_MANY_CREDENTIALS, ERROR_USER_ALREADY_EXISTS, ERROR_USER_DOES_NOT_EXIST, ErrorResource}; use crate::resources::expirations::AUTH_TOKEN_EXPIRATION_TIME_MILLIS; -use crate::utils::hasher::{generate_multiple_random_token_with_rng, hash_password}; +use crate::utils::hasher::{generate_multiple_random_token_with_rng, hash_password, hash_password_with_existing_salt}; use crate::validation::user_validator::validate_user_for_creation; pub async fn register_user<'a>(transaction: &mut Transaction<'a, Postgres>, user: UserRegisterPayload) -> Result>> { @@ -39,7 +39,7 @@ pub async fn register_user<'a>(transaction: &mut Transaction<'a, Postgres>, user } } Err(e) => { - error!("1{}", e); + error!("{}", e); error_resources.push(("ERROR.DATABASE_ERROR", "")); } }; @@ -67,7 +67,7 @@ pub async fn register_user<'a>(transaction: &mut Transaction<'a, Postgres>, user persisted_user = user; }, Err(e) => { - error!("2{}", e); + error!("{}", e); error_resources.push(("ERROR.DATABASE_ERROR", "")); return Err(error_resources); }}; @@ -77,68 +77,22 @@ pub async fn register_user<'a>(transaction: &mut Transaction<'a, Postgres>, user match insert_credential(transaction, credential, &persisted_user.id).await { Ok(_) => {} Err(e) => { - error!("3{}", e); + error!("{}", e); error_resources.push(("ERROR.DATABASE_ERROR", "")); return Err(error_resources); } }; } - // Create token and send it back. - let tokens: Vec = match generate_multiple_random_token_with_rng(2).await { - Ok(tokens) => tokens, - Err(e) => { - error!("4{}", e); - error_resources.push(("ERROR.JOIN_ERROR", "")); - return Err(error_resources); - } - }; - let token_to_insert = - Token { - id: 0, - user_id: persisted_user.id, - auth_token: match tokens.get(0) { - None => { - error!("Tokens were not created.", ); - error_resources.push(ERROR_TOKEN_NOT_CREATED); - return Err(error_resources); - } - Some(token) => token.clone() - }, - refresh_token: match tokens.get(1) { - None => { - error!("Tokens were not created.", ); - error_resources.push(ERROR_TOKEN_NOT_CREATED); - return Err(error_resources); - } - Some(token) => token.clone() - }, - time_created: now, - last_updated: now, - }; - // Insert token in DB - match insert_token(transaction, token_to_insert).await { - Ok(persisted_token) => { - Ok(persisted_token) - }, - Err(e) => { - error!("{}", e); - error_resources.push(("ERROR.DATABASE_ERROR", "")); - Err(error_resources) - } + if let Some(persisted_token) = create_token_for_user(transaction, persisted_user.id, &mut error_resources).await { + Ok(persisted_token) + } else { + Err(error_resources) } } -pub async fn authenticate_user(db_conn: &sqlx::PgPool, user: AuthenticateUserDto) -> Result> { +pub async fn authenticate_user<'a>(conn: &mut PgConnection, user: AuthenticateUserDto) -> Result>> { let mut error_resources = Vec::new(); - let mut conn = match db_conn.acquire().await { - Ok(conn) => conn, - Err(error) => { - error!("{:?}", error); - error_resources.push(("ERROR.DATABASE_ERROR", "")); - return Err(error_resources); - } - }; - let persisted_user = match get_user_with_id(&mut conn, &user.id).await { + let persisted_user = match get_user_with_id(conn, &user.id).await { Ok(persisted_user_opt) => match persisted_user_opt { None => { error_resources.push(ERROR_USER_DOES_NOT_EXIST); @@ -153,7 +107,7 @@ pub async fn authenticate_user(db_conn: &sqlx::PgPool, user: AuthenticateUserDto } }; - match validate_user_token(&mut conn, &user.id, user.auth_token).await { + match validate_user_token(conn, &user.id, user.auth_token).await { Ok(persisted_token_opt) => match persisted_token_opt { None => { error_resources.push(ERROR_INCORRECT_TOKEN); @@ -180,8 +134,119 @@ pub async fn authenticate_user(db_conn: &sqlx::PgPool, user: AuthenticateUserDto } } +/// pub async fn refresh_auth_token() {} +/// pub async fn reset_password() {} -pub async fn password_login() {} \ No newline at end of file +/// ## This resets a user's password without any validations! +/// Don't expose this to any public endpoint!! +pub async fn force_reset_password() {} + +/// +pub async fn password_login<'a>(transaction: &mut Transaction<'a, Postgres>, user: UserLoginPayload) -> Result>>{ + let mut error_resources = Vec::new(); + let persisted_user_credential = match get_credential(transaction, user.credential).await { + Ok(credential_opt) => match credential_opt { + None => { + error!("Credential not found for password login."); + error_resources.push(ERROR_CREDENTIAL_DOES_NOT_EXIST); + return Err(error_resources); + }, + Some(persisted_credential) => persisted_credential + }, + Err(e) => { + error!("{}", e); + error_resources.push(("ERROR.DATABASE_ERROR", "")); + return Err(error_resources); + } + }; + let persisted_user = match get_user_with_id(transaction, &persisted_user_credential.user_id).await { + Ok(persisted_user_opt) => match persisted_user_opt { + None => { + error!("Serious error. User doesn't exist but credentials pointing to the user do."); + error_resources.push(("ERROR.DATABASE_ERROR", "Critical. User doesn't exist but credentials pointing to the user do.")); + return Err(error_resources); + }, + Some(persisted_user) => { + persisted_user + } + }, + Err(e) => { + error!("{}", e); + error_resources.push(("ERROR.DATABASE_ERROR", "")); + return Err(error_resources);} + }; + let hashed_password = hash_password_with_existing_salt(&user.password, &persisted_user.salt); + if hashed_password.hash == persisted_user.password { + return if let Some(persisted_token) = create_token_for_user(transaction, persisted_user.id, &mut error_resources).await { + Ok(persisted_token) + } else { + Err(error_resources) + } + } + + Err(error_resources) +} + +/// +pub async fn get_user_credentials<'a>(transaction: &mut Transaction<'a, Postgres>, user: AuthenticateUserDto) -> Result, Vec>> { + let mut error_resources = Vec::new(); + let persisted_user = authenticate_user(transaction, user).await?; + match fetch_user_credentials(transaction, &persisted_user.id).await { + Ok(persisted_credentials) => Ok(persisted_credentials), + Err(e) => { + error!("{}", e); + error_resources.push(("ERROR.DATABASE_ERROR", "")); + Err(error_resources) + } + } +} + +async fn create_token_for_user<'a>(transaction: &mut Transaction<'a, Postgres>, user_id: i32, error_resources: &mut Vec>) -> Option { + // Create token and send it back. + let tokens: Vec = match generate_multiple_random_token_with_rng(2).await { + Ok(tokens) => tokens, + Err(e) => { + error!("{}", e); + error_resources.push(("ERROR.JOIN_ERROR", "")); + return None; + } + }; + let token_to_insert = + Token { + id: 0, + user_id, + auth_token: match tokens.get(0) { + None => { + error!("Tokens were not created.", ); + error_resources.push(ERROR_TOKEN_NOT_CREATED); + return None; + } + Some(token) => token.clone() + }, + refresh_token: match tokens.get(1) { + None => { + error!("Tokens were not created.", ); + error_resources.push(ERROR_TOKEN_NOT_CREATED); + return None; + } + Some(token) => token.clone() + }, + time_created: Utc::now(), + last_updated: Utc::now(), + }; + + // Insert token in DB + match insert_token(transaction, token_to_insert).await { + Ok(persisted_token) => { + Some(persisted_token) + }, + Err(e) => { + error!("{}", e); + error_resources.push(("ERROR.DATABASE_ERROR", "")); + None + } + } +} \ No newline at end of file diff --git a/src/validation/user_validator.rs b/src/validation/user_validator.rs index c6d3907..da8225d 100644 --- a/src/validation/user_validator.rs +++ b/src/validation/user_validator.rs @@ -33,7 +33,7 @@ fn validate_user_password(password: &String) -> bool { password.len() >= MIN_PASSWORD_LENGTH.into() && password.len() <= MAX_PASSWORD_LENGTH.into() } -pub fn validate_user_for_creation( +pub(crate) fn validate_user_for_creation( user: &UserRegisterPayload, error_resources: &mut Vec, ) { @@ -64,7 +64,7 @@ pub fn validate_user_for_creation( error_resources.push(ERROR_INVALID_PASSWORD); } } -pub fn validate_user_for_password_authentication( +pub(crate) fn validate_user_for_password_authentication( user: &UserLoginPayload, error_resources: &mut Vec, ) {