Error refactor
This commit is contained in:
parent
06d5b6ef9d
commit
a15c4d807f
@ -3,7 +3,7 @@
|
|||||||
<option name="myName" value="Project Default" />
|
<option name="myName" value="Project Default" />
|
||||||
<inspection_tool class="DuplicatedCode" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
<inspection_tool class="DuplicatedCode" enabled="true" level="WEAK WARNING" enabled_by_default="true">
|
||||||
<Languages>
|
<Languages>
|
||||||
<language minSize="83" name="Rust" />
|
<language minSize="86" name="Rust" />
|
||||||
</Languages>
|
</Languages>
|
||||||
</inspection_tool>
|
</inspection_tool>
|
||||||
<inspection_tool class="SqlDialectInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
<inspection_tool class="SqlDialectInspection" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||||
|
16
Cargo.lock
generated
16
Cargo.lock
generated
@ -50,6 +50,12 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anyhow"
|
||||||
|
version = "1.0.75"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "atoi"
|
name = "atoi"
|
||||||
version = "2.0.0"
|
version = "2.0.0"
|
||||||
@ -1427,18 +1433,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.44"
|
version = "1.0.48"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90"
|
checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "1.0.44"
|
version = "1.0.48"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96"
|
checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@ -1606,6 +1612,7 @@ dependencies = [
|
|||||||
name = "user-lib"
|
name = "user-lib"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
"chrono",
|
"chrono",
|
||||||
"data-encoding",
|
"data-encoding",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
@ -1613,6 +1620,7 @@ dependencies = [
|
|||||||
"ring",
|
"ring",
|
||||||
"serde",
|
"serde",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -18,3 +18,5 @@ ring = "0.16.20"
|
|||||||
data-encoding = "2.3.2"
|
data-encoding = "2.3.2"
|
||||||
futures-util = "0.3"
|
futures-util = "0.3"
|
||||||
log = "0.4.19"
|
log = "0.4.19"
|
||||||
|
thiserror = "1.0.48"
|
||||||
|
anyhow = { version = "1.0" }
|
@ -1,13 +1,39 @@
|
|||||||
use std::fmt::Display;
|
use thiserror::Error;
|
||||||
|
|
||||||
/// Used to return a simple error from FromStr implementations
|
#[derive(Error, Debug)]
|
||||||
#[derive(Debug)]
|
pub enum Error {
|
||||||
pub struct FromStrError;
|
/// Used to return a simple error from FromStr implementations.
|
||||||
|
#[error("Error parsing string into value")]
|
||||||
impl Display for FromStrError {
|
FromStrError,
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
/// Every error that is returned from a DAO operation.
|
||||||
write!(f, "Error parsing string into value. FromStrError.")
|
#[error("Error from the Database: {0}")]
|
||||||
}
|
DatabaseError(#[from] sqlx::Error),
|
||||||
|
/// A vec of ValidationErrors
|
||||||
|
#[error("Validation Errors: {0:?}")]
|
||||||
|
ValidationErrors(Vec<ValidationError>),
|
||||||
|
/// Something already exists. That something should be {0}
|
||||||
|
/// Example: "User" "Credential"
|
||||||
|
#[error("Error {0} Already exists.")]
|
||||||
|
AlreadyExistsError(String),
|
||||||
|
/// Example: "User with id X"
|
||||||
|
#[error("{0} Not found.")]
|
||||||
|
NotFoundError(String),
|
||||||
|
/// Used to specify authentication error.
|
||||||
|
/// Example: Password incorrect for user
|
||||||
|
#[error("Credential supplied is incorrect. {0}")]
|
||||||
|
IncorrectCredentialError(String),
|
||||||
|
#[error("Too many credentials supplied, maximum is 3.")]
|
||||||
|
TooManyCredentialsError,
|
||||||
|
/// Used for anything else.
|
||||||
|
#[error("Unexpected Error: {0}")]
|
||||||
|
UnexpectedError(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::error::Error for FromStrError {}
|
/// Any string validation error such as Phone number validation or email, etc...
|
||||||
|
/// Reason should be a Key for internationalization
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
#[error("Error validating `{what}`. Reason: {reason}")]
|
||||||
|
pub struct ValidationError {
|
||||||
|
pub what: String,
|
||||||
|
pub reason: String,
|
||||||
|
}
|
@ -7,10 +7,12 @@ use sqlx::{
|
|||||||
Postgres,
|
Postgres,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::domain::{credential::CredentialType, error::FromStrError};
|
use crate::domain::{credential::CredentialType};
|
||||||
|
use crate::domain::error::Error;
|
||||||
|
use crate::domain::error::Error::FromStrError;
|
||||||
|
|
||||||
impl FromStr for CredentialType {
|
impl FromStr for CredentialType {
|
||||||
type Err = FromStrError;
|
type Err = Error;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
match s {
|
match s {
|
||||||
|
@ -19,36 +19,28 @@ use crate::validation::user_validator::validate_user_for_creation;
|
|||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use log::{debug, error};
|
use log::{debug, error};
|
||||||
use sqlx::{PgConnection, Postgres, Transaction};
|
use sqlx::{PgConnection, Postgres, Transaction};
|
||||||
|
use crate::domain::error::{Error, ValidationError};
|
||||||
|
use crate::domain::error::Error::{AlreadyExistsError, IncorrectCredentialError, NotFoundError, TooManyCredentialsError, UnexpectedError};
|
||||||
|
|
||||||
pub async fn register_user<'a>(
|
pub async fn register_user<'a>(
|
||||||
transaction: &mut PgConnection,
|
transaction: &mut PgConnection,
|
||||||
user: UserRegisterPayload,
|
user: UserRegisterPayload,
|
||||||
) -> Result<Token, Vec<ErrorResource<'a>>> {
|
) -> Result<Token, Error> {
|
||||||
let mut error_resources: Vec<ErrorResource> = Vec::new();
|
let mut validation_errors: Vec<ValidationError> = Vec::new();
|
||||||
// Validate user
|
// Validate user
|
||||||
validate_user_for_creation(&user, &mut error_resources);
|
validate_user_for_creation(&user, &mut validation_errors);
|
||||||
// Find if user exists
|
// Find if user exists
|
||||||
if user.credentials.len() > 3 {
|
if user.credentials.len() > 3 {
|
||||||
error_resources.push(ERROR_TOO_MANY_CREDENTIALS);
|
return Err(TooManyCredentialsError);
|
||||||
}
|
}
|
||||||
for credential_dto in user.credentials.iter() {
|
for credential_dto in user.credentials.iter() {
|
||||||
match get_credential(transaction, credential_dto.credential.clone()).await {
|
match get_credential(transaction, credential_dto.credential.clone()).await? {
|
||||||
Ok(credential_opt) => match credential_opt {
|
|
||||||
None => {}
|
None => {}
|
||||||
Some(_) => {
|
Some(_) => {
|
||||||
error_resources.push(ERROR_USER_ALREADY_EXISTS);
|
return Err(AlreadyExistsError(String::from("Credential")));
|
||||||
}
|
|
||||||
},
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
// Get salt and hashed password from hashing function then give the results to the user
|
// Get salt and hashed password from hashing function then give the results to the user
|
||||||
let hash_result = hash_password(&user.password);
|
let hash_result = hash_password(&user.password);
|
||||||
let now = Utc::now();
|
let now = Utc::now();
|
||||||
@ -61,85 +53,42 @@ pub async fn register_user<'a>(
|
|||||||
last_updated: now,
|
last_updated: now,
|
||||||
};
|
};
|
||||||
|
|
||||||
let persisted_user;
|
let persisted_user= insert_user(transaction, user_to_insert).await?;
|
||||||
// Insert user in DB
|
|
||||||
match insert_user(transaction, user_to_insert).await {
|
|
||||||
Ok(user) => {
|
|
||||||
persisted_user = user;
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
error!("{}", e);
|
|
||||||
error_resources.push(("ERROR.DATABASE_ERROR", ""));
|
|
||||||
return Err(error_resources);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Insert Credentials
|
// Insert Credentials
|
||||||
for credential in user.credentials {
|
for credential in user.credentials {
|
||||||
match insert_credential(transaction, credential, &persisted_user.id).await {
|
insert_credential(transaction, credential, &persisted_user.id).await?;
|
||||||
Ok(_) => {}
|
|
||||||
Err(e) => {
|
|
||||||
error!("{}", e);
|
|
||||||
error_resources.push(("ERROR.DATABASE_ERROR", ""));
|
|
||||||
return Err(error_resources);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(persisted_token) =
|
|
||||||
create_token_for_user(transaction, persisted_user.id, &mut error_resources).await
|
create_token_for_user(transaction, persisted_user.id).await
|
||||||
{
|
|
||||||
Ok(persisted_token)
|
|
||||||
} else {
|
|
||||||
Err(error_resources)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
pub async fn authenticate_user<'a>(
|
pub async fn authenticate_user<'a>(
|
||||||
conn: &mut PgConnection,
|
conn: &mut PgConnection,
|
||||||
user: AuthenticateUserDto,
|
user: AuthenticateUserDto,
|
||||||
) -> Result<User, Vec<ErrorResource<'a>>> {
|
) -> Result<User, Error> {
|
||||||
let mut error_resources = Vec::new();
|
let persisted_user = match get_user_with_id(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 => {
|
None => {
|
||||||
error_resources.push(ERROR_USER_DOES_NOT_EXIST);
|
return Err(NotFoundError(format!("User with id: {}", user.id)));
|
||||||
return Err(error_resources);
|
|
||||||
}
|
}
|
||||||
Some(persisted_user) => persisted_user,
|
Some(persisted_user) => persisted_user,
|
||||||
},
|
|
||||||
Err(error) => {
|
|
||||||
error!("{:?}", error);
|
|
||||||
error_resources.push(("ERROR.DATABASE_ERROR", ""));
|
|
||||||
return Err(error_resources);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
match validate_user_token(conn, &user.id, user.auth_token).await {
|
match validate_user_token(conn, &user.id, user.auth_token.clone()).await? {
|
||||||
Ok(persisted_token_opt) => match persisted_token_opt {
|
|
||||||
None => {
|
None => {
|
||||||
error_resources.push(ERROR_INCORRECT_TOKEN);
|
Err(NotFoundError(format!("Auth Token {}", user.auth_token)))
|
||||||
Err(error_resources)
|
|
||||||
}
|
}
|
||||||
Some(persisted_token) => {
|
Some(persisted_token) => {
|
||||||
// Check if persisted_token expired
|
// Check if persisted_token expired
|
||||||
if Utc::now().timestamp_millis() - persisted_token.last_updated.timestamp_millis()
|
if Utc::now().timestamp_millis() - persisted_token.last_updated.timestamp_millis()
|
||||||
> AUTH_TOKEN_EXPIRATION_TIME_MILLIS
|
> AUTH_TOKEN_EXPIRATION_TIME_MILLIS
|
||||||
{
|
{
|
||||||
// Expired
|
Err(IncorrectCredentialError(String::from("Auth Token Expired. Use refresh token to get a new one.")))
|
||||||
debug!("Expired token: {:?}", persisted_token);
|
|
||||||
error_resources.push(ERROR_EXPIRED_TOKEN);
|
|
||||||
Err(error_resources)
|
|
||||||
} else {
|
} else {
|
||||||
// Not expired
|
// Not expired
|
||||||
Ok(persisted_user)
|
Ok(persisted_user)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
Err(error) => {
|
|
||||||
error!("{:?}", error);
|
|
||||||
error_resources.push(("ERROR.DATABASE_ERROR", ""));
|
|
||||||
Err(error_resources)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,76 +96,37 @@ pub async fn authenticate_user<'a>(
|
|||||||
pub async fn refresh_auth_token<'a>(
|
pub async fn refresh_auth_token<'a>(
|
||||||
conn: &mut PgConnection,
|
conn: &mut PgConnection,
|
||||||
user: RefreshAuthTokenForUserDto,
|
user: RefreshAuthTokenForUserDto,
|
||||||
) -> Result<Token, Vec<ErrorResource>> {
|
) -> Result<Token, Error> {
|
||||||
let mut error_resources = Vec::new();
|
let _persisted_user = match get_user_with_id(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 => {
|
None => {
|
||||||
error_resources.push(ERROR_USER_DOES_NOT_EXIST);
|
return Err(NotFoundError(format!("User with id: {}", user.id)));
|
||||||
return Err(error_resources);
|
|
||||||
}
|
}
|
||||||
Some(persisted_user) => persisted_user,
|
Some(persisted_user) => persisted_user,
|
||||||
},
|
|
||||||
Err(error) => {
|
|
||||||
error!("{:?}", error);
|
|
||||||
error_resources.push(("ERROR.DATABASE_ERROR", ""));
|
|
||||||
return Err(error_resources);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut tokens: Vec<String> = match generate_multiple_random_token_with_rng(2).await {
|
let mut tokens: Vec<String> = generate_multiple_random_token_with_rng(2).await;
|
||||||
Ok(tokens) => tokens,
|
|
||||||
Err(e) => {
|
|
||||||
error!("{}", e);
|
|
||||||
error_resources.push(("ERROR.JOIN_ERROR", ""));
|
|
||||||
return Err(error_resources);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if tokens.len() > 0 {
|
if tokens.len() > 0 {
|
||||||
let new_auth_token = tokens.remove(0);
|
let new_auth_token = tokens.remove(0);
|
||||||
return match update_token(conn, user.refresh_token, new_auth_token).await {
|
Ok(update_token(conn, user.refresh_token, new_auth_token).await?)
|
||||||
Ok(persisted_token) => Ok(persisted_token),
|
} else {
|
||||||
Err(e) => {
|
Err(UnexpectedError(String::from("No tokens were created.")))
|
||||||
error!("{:?}", e);
|
|
||||||
error_resources.push(("ERROR.DATABASE_ERROR", ""));
|
|
||||||
Err(error_resources)
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(error_resources)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// reset a user's password by validating the user's own password.
|
/// reset a user's password by validating the user's own password.
|
||||||
pub async fn reset_password(
|
pub async fn reset_password(
|
||||||
conn: &mut PgConnection,
|
conn: &mut PgConnection,
|
||||||
user: UserResetPasswordPayload,
|
user: UserResetPasswordPayload,
|
||||||
) -> Result<User, Vec<ErrorResource>> {
|
) -> Result<User, Error> {
|
||||||
let mut error_resources: Vec<ErrorResource> = Vec::new();
|
|
||||||
|
|
||||||
let password_matches = match validate_user_password(conn, &user.id, user.password).await {
|
let password_matches = validate_user_password(conn, &user.id, user.password).await?;
|
||||||
Ok(matches) => matches,
|
|
||||||
Err(e) => {
|
|
||||||
error!("{:?}", e);
|
|
||||||
error_resources.push(e);
|
|
||||||
return Err(error_resources);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(persisted_user) = password_matches {
|
if let Some(persisted_user) = password_matches {
|
||||||
// Change pass
|
// Change pass
|
||||||
match change_password(conn, persisted_user, &user.new_password).await {
|
Ok(change_password(conn, persisted_user, &user.new_password).await?)
|
||||||
Ok(user_changed) => Ok(user_changed),
|
|
||||||
Err(e) => {
|
|
||||||
error!("{:?}", e);
|
|
||||||
error_resources.push(e);
|
|
||||||
Err(error_resources)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
error_resources.push(ERROR_PASSWORD_INCORRECT);
|
Err(IncorrectCredentialError(String::from("Password incorrect.")))
|
||||||
Err(error_resources)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -226,24 +136,13 @@ pub async fn force_reset_password<'a>(
|
|||||||
conn: &mut PgConnection,
|
conn: &mut PgConnection,
|
||||||
user_id: &i32,
|
user_id: &i32,
|
||||||
new_password: String,
|
new_password: String,
|
||||||
) -> Result<User, ErrorResource<'a>> {
|
) -> Result<User, Error> {
|
||||||
let persisted_user = match get_user_with_id(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 => {
|
None => {
|
||||||
error!("Serious error. User doesn't exist but credentials pointing to the user do.");
|
error!("Serious error. User doesn't exist but credentials pointing to the user do.");
|
||||||
return Err((
|
return Err(Error::NotFoundError(format!("User with id: {user_id}")));
|
||||||
"ERROR.DATABASE_ERROR",
|
|
||||||
"Critical. User doesn't exist but credentials pointing to the user do.",
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
Some(persisted_user) => persisted_user,
|
Some(persisted_user) => persisted_user,
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
error!("{}", e);
|
|
||||||
return Err(("ERROR.DATABASE_ERROR", ""));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
Ok(change_password(conn, persisted_user, &new_password).await?)
|
Ok(change_password(conn, persisted_user, &new_password).await?)
|
||||||
}
|
}
|
||||||
@ -252,97 +151,55 @@ pub async fn force_reset_password<'a>(
|
|||||||
pub async fn password_login<'a>(
|
pub async fn password_login<'a>(
|
||||||
conn: &mut Transaction<'a, Postgres>,
|
conn: &mut Transaction<'a, Postgres>,
|
||||||
user: UserLoginPayload,
|
user: UserLoginPayload,
|
||||||
) -> Result<Token, Vec<ErrorResource<'a>>> {
|
) -> Result<Token, Error> {
|
||||||
let mut error_resources = Vec::new();
|
let persisted_user_credential = match get_credential(conn, user.credential.clone()).await? {
|
||||||
let persisted_user_credential = match get_credential(conn, user.credential).await {
|
|
||||||
Ok(credential_opt) => match credential_opt {
|
|
||||||
None => {
|
None => {
|
||||||
error!("Credential not found for password login.");
|
return Err(NotFoundError(format!("Credential {}", user.credential)))
|
||||||
error_resources.push(ERROR_CREDENTIAL_DOES_NOT_EXIST);
|
|
||||||
return Err(error_resources);
|
|
||||||
}
|
}
|
||||||
Some(persisted_credential) => persisted_credential,
|
Some(persisted_credential) => persisted_credential,
|
||||||
},
|
|
||||||
Err(e) => {
|
|
||||||
error!("{}", e);
|
|
||||||
error_resources.push(("ERROR.DATABASE_ERROR", ""));
|
|
||||||
return Err(error_resources);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
let persisted_user_opt =
|
let persisted_user_opt =
|
||||||
match validate_user_password(conn, &persisted_user_credential.user_id, user.password).await
|
validate_user_password(conn, &persisted_user_credential.user_id, user.password).await?;
|
||||||
{
|
|
||||||
Ok(matches) => matches,
|
|
||||||
Err(e) => {
|
|
||||||
error!("{:?}", e);
|
|
||||||
error_resources.push(e);
|
|
||||||
return Err(error_resources);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
if let Some(_) = persisted_user_opt {
|
if let Some(_) = persisted_user_opt {
|
||||||
return if let Some(persisted_token) = create_token_for_user(
|
Ok(create_token_for_user(
|
||||||
conn,
|
conn,
|
||||||
persisted_user_credential.user_id,
|
persisted_user_credential.user_id,
|
||||||
&mut error_resources,
|
|
||||||
)
|
)
|
||||||
.await
|
.await?)
|
||||||
{
|
|
||||||
Ok(persisted_token)
|
|
||||||
} else {
|
} else {
|
||||||
Err(error_resources)
|
Err(NotFoundError(format!("User with id: {}", persisted_user_credential.user_id)))
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(error_resources)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
pub async fn get_user_credentials<'a>(
|
pub async fn get_user_credentials<'a>(
|
||||||
transaction: &mut Transaction<'a, Postgres>,
|
transaction: &mut Transaction<'a, Postgres>,
|
||||||
user: AuthenticateUserDto,
|
user: AuthenticateUserDto,
|
||||||
) -> Result<Vec<Credential>, Vec<ErrorResource<'a>>> {
|
) -> Result<Vec<Credential>, Error> {
|
||||||
let mut error_resources = Vec::new();
|
|
||||||
let persisted_user = authenticate_user(transaction, user).await?;
|
let persisted_user = authenticate_user(transaction, user).await?;
|
||||||
match fetch_user_credentials(transaction, &persisted_user.id).await {
|
Ok(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>(
|
async fn create_token_for_user<'a>(
|
||||||
transaction: &mut PgConnection,
|
transaction: &mut PgConnection,
|
||||||
user_id: i32,
|
user_id: i32,
|
||||||
error_resources: &mut Vec<ErrorResource<'a>>,
|
) -> Result<Token, Error> {
|
||||||
) -> Option<Token> {
|
|
||||||
// Create token and send it back.
|
// Create token and send it back.
|
||||||
let tokens: Vec<String> = match generate_multiple_random_token_with_rng(2).await {
|
let tokens: Vec<String> = 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 {
|
let token_to_insert = Token {
|
||||||
id: 0,
|
id: 0,
|
||||||
user_id,
|
user_id,
|
||||||
auth_token: match tokens.get(0) {
|
auth_token: match tokens.get(0) {
|
||||||
None => {
|
None => {
|
||||||
error!("Tokens were not created.",);
|
error!("Tokens were not created.");
|
||||||
error_resources.push(ERROR_TOKEN_NOT_CREATED);
|
return Err(Error::UnexpectedError(String::from("Tokens were not created.")));
|
||||||
return None;
|
|
||||||
}
|
}
|
||||||
Some(token) => token.clone(),
|
Some(token) => token.clone(),
|
||||||
},
|
},
|
||||||
refresh_token: match tokens.get(1) {
|
refresh_token: match tokens.get(1) {
|
||||||
None => {
|
None => {
|
||||||
error!("Tokens were not created.",);
|
error!("Tokens were not created.");
|
||||||
error_resources.push(ERROR_TOKEN_NOT_CREATED);
|
return Err(Error::UnexpectedError(String::from("Tokens were not created.")));
|
||||||
return None;
|
|
||||||
}
|
}
|
||||||
Some(token) => token.clone(),
|
Some(token) => token.clone(),
|
||||||
},
|
},
|
||||||
@ -351,39 +208,22 @@ async fn create_token_for_user<'a>(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Insert token in DB
|
// Insert token in DB
|
||||||
match insert_token(transaction, token_to_insert).await {
|
Ok(insert_token(transaction, token_to_insert).await?)
|
||||||
Ok(persisted_token) => Some(persisted_token),
|
|
||||||
Err(e) => {
|
|
||||||
error!("{}", e);
|
|
||||||
error_resources.push(("ERROR.DATABASE_ERROR", ""));
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn validate_user_password<'a>(
|
async fn validate_user_password<'a>(
|
||||||
conn: &mut PgConnection,
|
conn: &mut PgConnection,
|
||||||
user_id: &i32,
|
user_id: &i32,
|
||||||
password: String,
|
password: String,
|
||||||
) -> Result<Option<User>, ErrorResource<'a>> {
|
) -> Result<Option<User>, Error> {
|
||||||
let persisted_user = match get_user_with_id(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 => {
|
None => {
|
||||||
error!("Serious error. User doesn't exist but credentials pointing to the user do.");
|
error!("Serious error. User doesn't exist but credentials pointing to the user do.");
|
||||||
return Err((
|
return Err(Error::NotFoundError(format!("User with id: {user_id}")));
|
||||||
"ERROR.DATABASE_ERROR",
|
|
||||||
"Critical. User doesn't exist but credentials pointing to the user do.",
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
Some(persisted_user) => persisted_user,
|
Some(persisted_user) => persisted_user,
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
error!("{}", e);
|
|
||||||
return Err(("ERROR.DATABASE_ERROR", ""));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let hashed_password = hash_password_with_existing_salt(&password, &persisted_user.salt);
|
let hashed_password = hash_password_with_existing_salt(&password, &persisted_user.salt);
|
||||||
if hashed_password.hash == persisted_user.password {
|
if hashed_password.hash == persisted_user.password {
|
||||||
Ok(Some(persisted_user))
|
Ok(Some(persisted_user))
|
||||||
@ -396,15 +236,9 @@ async fn change_password<'a>(
|
|||||||
conn: &mut PgConnection,
|
conn: &mut PgConnection,
|
||||||
mut persisted_user: User,
|
mut persisted_user: User,
|
||||||
new_password: &String,
|
new_password: &String,
|
||||||
) -> Result<User, ErrorResource<'a>> {
|
) -> Result<User, Error> {
|
||||||
let hash_result = hash_password(&new_password);
|
let hash_result = hash_password(&new_password);
|
||||||
persisted_user.password = hash_result.hash;
|
persisted_user.password = hash_result.hash;
|
||||||
persisted_user.salt = hash_result.salt;
|
persisted_user.salt = hash_result.salt;
|
||||||
match update_user(conn, persisted_user).await {
|
Ok(update_user(conn, persisted_user).await?)
|
||||||
Ok(user) => Ok(user),
|
|
||||||
Err(error) => {
|
|
||||||
error!("{}", error);
|
|
||||||
return Err(("ERROR.DATABASE_ERROR", ""));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -5,13 +5,12 @@ use ring::{
|
|||||||
rand::{SecureRandom, SystemRandom},
|
rand::{SecureRandom, SystemRandom},
|
||||||
};
|
};
|
||||||
use std::num::NonZeroU32;
|
use std::num::NonZeroU32;
|
||||||
use tokio::task::JoinError;
|
|
||||||
|
|
||||||
const SALT_ROUNDS: u32 = 1000;
|
const SALT_ROUNDS: u32 = 1000;
|
||||||
|
|
||||||
pub(crate) async fn generate_multiple_random_token_with_rng(
|
pub(crate) async fn generate_multiple_random_token_with_rng(
|
||||||
amount: u8,
|
amount: u8,
|
||||||
) -> Result<Vec<String>, JoinError> {
|
) -> Vec<String> {
|
||||||
// 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();
|
||||||
|
|
||||||
@ -41,7 +40,7 @@ pub(crate) async fn generate_multiple_random_token_with_rng(
|
|||||||
.rev()
|
.rev()
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
Ok(all_tokens_solved)
|
all_tokens_solved
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn hash_password_with_existing_salt(
|
pub(crate) fn hash_password_with_existing_salt(
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
use crate::domain::credential::CredentialType;
|
use crate::domain::credential::CredentialType;
|
||||||
|
use crate::domain::error::ValidationError;
|
||||||
use crate::dto::users::{UserLoginPayload, UserRegisterPayload};
|
use crate::dto::users::{UserLoginPayload, UserRegisterPayload};
|
||||||
use crate::resources::error_messages::{ErrorResource, ERROR_INVALID_USERNAME};
|
use crate::resources::error_messages::ERROR_INVALID_USERNAME;
|
||||||
use crate::resources::{
|
use crate::resources::{
|
||||||
error_messages::{
|
error_messages::{
|
||||||
ERROR_INVALID_EMAIL, ERROR_INVALID_NAME, ERROR_INVALID_PASSWORD, ERROR_INVALID_PHONE_NUMBER,
|
ERROR_INVALID_EMAIL, ERROR_INVALID_PASSWORD, ERROR_INVALID_PHONE_NUMBER,
|
||||||
},
|
},
|
||||||
variable_lengths::{
|
variable_lengths::{
|
||||||
MAX_EMAIL_LENGTH, MAX_NAME_LENGTH, MAX_PASSWORD_LENGTH, MIN_EMAIL_LENGTH, MIN_NAME_LENGTH,
|
MAX_EMAIL_LENGTH, MAX_NAME_LENGTH, MAX_PASSWORD_LENGTH, MIN_EMAIL_LENGTH, MIN_NAME_LENGTH,
|
||||||
@ -35,7 +36,7 @@ fn validate_user_password(password: &String) -> bool {
|
|||||||
|
|
||||||
pub(crate) fn validate_user_for_creation(
|
pub(crate) fn validate_user_for_creation(
|
||||||
user: &UserRegisterPayload,
|
user: &UserRegisterPayload,
|
||||||
error_resources: &mut Vec<ErrorResource>,
|
error_resources: &mut Vec<ValidationError>,
|
||||||
) {
|
) {
|
||||||
for credential_dto in user.credentials.iter() {
|
for credential_dto in user.credentials.iter() {
|
||||||
validate_credential(
|
validate_credential(
|
||||||
@ -46,41 +47,41 @@ pub(crate) fn validate_user_for_creation(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !validate_user_name(&user.name) {
|
if !validate_user_name(&user.name) {
|
||||||
error_resources.push(ERROR_INVALID_NAME);
|
error_resources.push(ValidationError { what: String::from("UserName"), reason: ERROR_INVALID_USERNAME.0.to_string() });
|
||||||
}
|
}
|
||||||
if !validate_user_password(&user.password) {
|
if !validate_user_password(&user.password) {
|
||||||
error_resources.push(ERROR_INVALID_PASSWORD);
|
error_resources.push(ValidationError { what: String::from("Password"), reason: ERROR_INVALID_PASSWORD.0.to_string() });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub(crate) fn validate_user_for_password_authentication(
|
pub(crate) fn validate_user_for_password_authentication(
|
||||||
user: &UserLoginPayload,
|
user: &UserLoginPayload,
|
||||||
error_resources: &mut Vec<ErrorResource>,
|
error_resources: &mut Vec<ValidationError>,
|
||||||
) {
|
) {
|
||||||
validate_credential(error_resources, &user.credential, &user.credential_type);
|
validate_credential(error_resources, &user.credential, &user.credential_type);
|
||||||
if !validate_user_password(&user.password) {
|
if !validate_user_password(&user.password) {
|
||||||
error_resources.push(ERROR_INVALID_PASSWORD);
|
error_resources.push(ValidationError { what: String::from("Password"), reason: ERROR_INVALID_PASSWORD.0.to_string() });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn validate_credential(
|
fn validate_credential(
|
||||||
error_resources: &mut Vec<ErrorResource>,
|
error_resources: &mut Vec<ValidationError>,
|
||||||
credential: &String,
|
credential: &String,
|
||||||
credential_type: &CredentialType,
|
credential_type: &CredentialType,
|
||||||
) {
|
) {
|
||||||
match credential_type {
|
match credential_type {
|
||||||
CredentialType::Email => {
|
CredentialType::Email => {
|
||||||
if !validate_user_email(credential) {
|
if !validate_user_email(credential) {
|
||||||
error_resources.push(ERROR_INVALID_EMAIL);
|
error_resources.push(ValidationError { what: String::from("Email"), reason: ERROR_INVALID_EMAIL.0.to_string() });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CredentialType::PhoneNumber => {
|
CredentialType::PhoneNumber => {
|
||||||
if !validate_user_phone_number(credential) {
|
if !validate_user_phone_number(credential) {
|
||||||
error_resources.push(ERROR_INVALID_PHONE_NUMBER);
|
error_resources.push(ValidationError { what: String::from("Phone Number"), reason: ERROR_INVALID_PHONE_NUMBER.0.to_string() });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CredentialType::Username => {
|
CredentialType::Username => {
|
||||||
if !validate_user_username(credential) {
|
if !validate_user_username(credential) {
|
||||||
error_resources.push(ERROR_INVALID_USERNAME);
|
error_resources.push(ValidationError { what: String::from("UserName"), reason: ERROR_INVALID_USERNAME.0.to_string() });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user