Auth with user token
This commit is contained in:
parent
a117b55ff4
commit
51d88246cd
@ -1,4 +1,4 @@
|
||||
CREATE TABLE IF NOT EXISTS user (
|
||||
CREATE TABLE IF NOT EXISTS "user" (
|
||||
id SERIAL PRIMARY KEY,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
password TEXT NOT NULL,
|
@ -1,4 +1,4 @@
|
||||
CREATE TABLE IF NOT EXISTS token (
|
||||
CREATE TABLE IF NOT EXISTS "token" (
|
||||
id SERIAL PRIMARY KEY,
|
||||
user_id INT NOT NULL,
|
||||
auth_token TEXT NOT NULL,
|
@ -1,4 +1,4 @@
|
||||
CREATE TABLE IF NOT EXISTS credential (
|
||||
CREATE TABLE IF NOT EXISTS "credential" (
|
||||
user_id INT NOT NULL,
|
||||
credential_type VARCHAR NOT NULL,
|
||||
credential VARCHAR NOT NULL,
|
@ -1,41 +1,36 @@
|
||||
use crate::domain::credential::Credential;
|
||||
use crate::dto::credential::CredentialDto;
|
||||
use chrono::Utc;
|
||||
use sqlx::{Error, PgPool};
|
||||
use sqlx::{Error, PgConnection, PgPool};
|
||||
|
||||
pub async fn insert_credentials(
|
||||
conn: &PgPool,
|
||||
credentials: Vec<CredentialDto>,
|
||||
pub async fn insert_credential(
|
||||
conn: &mut PgConnection,
|
||||
credential_dto: CredentialDto,
|
||||
user_id: &i32,
|
||||
) -> Result<Vec<Credential>, Error> {
|
||||
let insert_query_base = r#"INSERT INTO credential
|
||||
) -> Result<Credential, Error> {
|
||||
let insert_query_base = r#"INSERT INTO "credential"
|
||||
(user_id, credential_type, credential, validated, time_created, last_updated)
|
||||
VALUES ($1, $2, $3, $4, $5, $5) RETURNING user_id, credential_type as "credential_type: _", credential, validated, time_created, last_updated"#;
|
||||
let mut persisted_credentials = Vec::new();
|
||||
for credential_dto in credentials {
|
||||
let persisted_credential: Credential = sqlx::query_as(insert_query_base)
|
||||
VALUES ($1, $2, $3, $4, $5, $5) RETURNING user_id, credential_type, credential, validated, time_created, last_updated"#;
|
||||
sqlx::query_as(insert_query_base)
|
||||
.bind(user_id)
|
||||
.bind(credential_dto.credential_type)
|
||||
.bind(credential_dto.credential)
|
||||
.bind(false)
|
||||
.bind(Utc::now())
|
||||
.fetch_one(conn)
|
||||
.await?;
|
||||
persisted_credentials.push(persisted_credential);
|
||||
}
|
||||
Ok(persisted_credentials)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn fetch_user_credentials(
|
||||
conn: &PgPool,
|
||||
conn: &mut PgConnection,
|
||||
user_id: &i32,
|
||||
) -> Result<Vec<Credential>, 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
|
||||
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(
|
||||
conn: &PgPool,
|
||||
conn: &mut PgConnection,
|
||||
credential: String,
|
||||
) -> Result<Option<Credential>, 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
|
||||
sqlx::query_as(r#"SELECT user_id, credential_type, credential, validated, time_created, last_updated FROM "credential" WHERE credential = $1"#).bind(credential).fetch_optional(conn).await
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
use crate::domain::token::Token;
|
||||
use chrono::Utc;
|
||||
use sqlx::{Error, PgPool};
|
||||
use sqlx::{Error, PgConnection, PgPool};
|
||||
|
||||
pub async fn insert_token(conn: &PgPool, token: Token) -> Result<Token, Error> {
|
||||
pub async fn insert_token(conn: &mut PgConnection, token: Token) -> Result<Token, Error> {
|
||||
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)
|
||||
@ -10,7 +10,7 @@ pub async fn insert_token(conn: &PgPool, token: Token) -> Result<Token, Error> {
|
||||
}
|
||||
|
||||
pub async fn update_token(
|
||||
conn: &PgPool,
|
||||
conn: &mut PgConnection,
|
||||
token_id: &i32,
|
||||
refresh_token: String,
|
||||
new_auth_token: String,
|
||||
@ -28,7 +28,7 @@ pub async fn update_token(
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn remove_token(conn: &PgPool, token_id: &i32) -> Result<Option<Token>, Error> {
|
||||
pub async fn remove_token(conn: &mut PgConnection, token_id: &i32) -> Result<Option<Token>, Error> {
|
||||
sqlx::query_as(r#"DELETE FROM token WHERE id = $1 RETURNING *;"#)
|
||||
.bind(token_id)
|
||||
.fetch_optional(conn)
|
||||
@ -36,7 +36,7 @@ pub async fn remove_token(conn: &PgPool, token_id: &i32) -> Result<Option<Token>
|
||||
}
|
||||
|
||||
pub async fn validate_user_token(
|
||||
conn: &PgPool,
|
||||
conn: &mut PgConnection,
|
||||
user_id: &i32,
|
||||
auth_token: String,
|
||||
) -> Result<Option<Token>, Error> {
|
||||
|
@ -1,10 +1,10 @@
|
||||
use crate::domain::user::User;
|
||||
use sqlx::{query_as, query_as_with, PgPool};
|
||||
use sqlx::{PgConnection, PgPool, Postgres, Transaction};
|
||||
|
||||
pub async fn insert_user(conn: &PgPool, user: User) -> Result<User, sqlx::Error> {
|
||||
pub async fn insert_user(conn: &mut PgConnection, user: User) -> Result<User, sqlx::Error> {
|
||||
sqlx::query_as(
|
||||
r#"
|
||||
INSERT INTO user (name, password, salt, time_created, last_updated)
|
||||
INSERT INTO "user" (name, password, salt, time_created, last_updated)
|
||||
VALUES ($1, $2, $3, $4, $4) RETURNING *;
|
||||
"#,
|
||||
)
|
||||
@ -16,10 +16,10 @@ pub async fn insert_user(conn: &PgPool, user: User) -> Result<User, sqlx::Error>
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_user_with_id(conn: &PgPool, user_id: &i32) -> Result<Option<User>, sqlx::Error> {
|
||||
pub async fn get_user_with_id(conn: &mut PgConnection, user_id: &i32) -> Result<Option<User>, sqlx::Error> {
|
||||
sqlx::query_as(
|
||||
r#"
|
||||
SELECT * FROM user where id = $1;
|
||||
SELECT * FROM "user" where id = $1;
|
||||
"#,
|
||||
)
|
||||
.bind(user_id)
|
||||
@ -27,10 +27,10 @@ pub async fn get_user_with_id(conn: &PgPool, user_id: &i32) -> Result<Option<Use
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn update_user(conn: &PgPool, user: User) -> Result<User, sqlx::Error> {
|
||||
pub async fn update_user(conn: &mut PgConnection, user: User) -> Result<User, sqlx::Error> {
|
||||
sqlx::query_as(
|
||||
r#"
|
||||
UPDATE user SET
|
||||
UPDATE "user" SET
|
||||
name = $2, password = $3, salt = $4, last_updated = $5
|
||||
WHERE id = $1 RETURNING *;
|
||||
"#,
|
||||
@ -44,10 +44,10 @@ pub async fn update_user(conn: &PgPool, user: User) -> Result<User, sqlx::Error>
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn delete_user(conn: &PgPool, user_id: &i32) -> Result<Option<User>, sqlx::Error> {
|
||||
pub async fn delete_user(conn: &mut PgConnection, user_id: &i32) -> Result<Option<User>, sqlx::Error> {
|
||||
sqlx::query_as(
|
||||
r#"
|
||||
DELETE FROM user where id = $1 RETURNING *;
|
||||
DELETE FROM "user" where id = $1 RETURNING *;
|
||||
"#,
|
||||
)
|
||||
.bind(user_id)
|
||||
|
2
src/resources/expirations.rs
Normal file
2
src/resources/expirations.rs
Normal file
@ -0,0 +1,2 @@
|
||||
|
||||
pub const AUTH_TOKEN_EXPIRATION_TIME_MILLIS: i64 = 604800000; // 7 Days
|
@ -1,2 +1,3 @@
|
||||
pub mod error_messages;
|
||||
pub mod variable_lengths;
|
||||
pub mod expirations;
|
||||
|
@ -1,2 +1,2 @@
|
||||
mod token;
|
||||
mod user;
|
||||
pub mod token;
|
||||
pub mod user;
|
||||
|
@ -1,19 +1,20 @@
|
||||
use std::error::Error;
|
||||
use chrono::Utc;
|
||||
use log::{error, log};
|
||||
use tokio::task::JoinError;
|
||||
use crate::dao::credential::{get_credential, insert_credentials};
|
||||
use crate::dao::token::insert_token;
|
||||
use crate::dao::user::insert_user;
|
||||
use crate::domain::credential::Credential;
|
||||
use log::{debug, error, log};
|
||||
use sqlx::{Error, Postgres, Transaction};
|
||||
use sqlx::pool::PoolConnection;
|
||||
use crate::dao::credential::{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::token::Token;
|
||||
use crate::domain::user::User;
|
||||
use crate::dto::token::AuthenticateUserDto;
|
||||
use crate::dto::users::UserRegisterPayload;
|
||||
use crate::resources::error_messages::{ERROR_TOKEN_NOT_CREATED, ERROR_TOO_MANY_CREDENTIALS, ERROR_USER_ALREADY_EXISTS, ErrorResource};
|
||||
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::resources::expirations::AUTH_TOKEN_EXPIRATION_TIME_MILLIS;
|
||||
use crate::utils::hasher::{generate_multiple_random_token_with_rng, hash_password};
|
||||
use crate::validation::user_validator::validate_user_for_creation;
|
||||
|
||||
pub async fn register_user(db_conn: &sqlx::PgPool, user: UserRegisterPayload) -> Result<Token, Vec<ErrorResource>> {
|
||||
pub async fn register_user<'a>(transaction: &mut Transaction<'a, Postgres>, user: UserRegisterPayload) -> Result<Token, Vec<ErrorResource<'a>>> {
|
||||
let mut error_resources: Vec<ErrorResource> = Vec::new();
|
||||
// Validate user
|
||||
validate_user_for_creation(&user, &mut error_resources);
|
||||
@ -23,7 +24,7 @@ pub async fn register_user(db_conn: &sqlx::PgPool, user: UserRegisterPayload) ->
|
||||
}
|
||||
for credential_dto in user.credentials.iter() {
|
||||
match get_credential(
|
||||
&db_conn,
|
||||
transaction,
|
||||
credential_dto.credential.clone(),
|
||||
)
|
||||
.await
|
||||
@ -38,7 +39,7 @@ pub async fn register_user(db_conn: &sqlx::PgPool, user: UserRegisterPayload) ->
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
error!("{}", e);
|
||||
error!("1{}", e);
|
||||
error_resources.push(("ERROR.DATABASE_ERROR", ""));
|
||||
}
|
||||
};
|
||||
@ -60,33 +61,33 @@ pub async fn register_user(db_conn: &sqlx::PgPool, user: UserRegisterPayload) ->
|
||||
};
|
||||
|
||||
let persisted_user;
|
||||
|
||||
// Insert user in DB
|
||||
match insert_user(&db_conn, user_to_insert).await{
|
||||
match insert_user(transaction, user_to_insert).await{
|
||||
Ok(user) => {
|
||||
persisted_user = user;
|
||||
},
|
||||
Err(e) => {
|
||||
error!("{}", e);
|
||||
error!("2{}", e);
|
||||
error_resources.push(("ERROR.DATABASE_ERROR", ""));
|
||||
return Err(error_resources);
|
||||
}};
|
||||
|
||||
// Insert Credentials
|
||||
match insert_credentials(db_conn, user.credentials, &persisted_user.id).await {
|
||||
for credential in user.credentials {
|
||||
match insert_credential(transaction, credential, &persisted_user.id).await {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
error!("{}", e);
|
||||
error!("3{}", e);
|
||||
error_resources.push(("ERROR.DATABASE_ERROR", ""));
|
||||
return Err(error_resources);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
// Create token and send it back.
|
||||
let tokens: Vec<String> = match generate_multiple_random_token_with_rng(2).await {
|
||||
Ok(tokens) => tokens,
|
||||
Err(e) => {
|
||||
error!("{}", e);
|
||||
error!("4{}", e);
|
||||
error_resources.push(("ERROR.JOIN_ERROR", ""));
|
||||
return Err(error_resources);
|
||||
}
|
||||
@ -116,7 +117,7 @@ pub async fn register_user(db_conn: &sqlx::PgPool, user: UserRegisterPayload) ->
|
||||
};
|
||||
|
||||
// Insert token in DB
|
||||
match insert_token(&db_conn, token_to_insert).await {
|
||||
match insert_token(transaction, token_to_insert).await {
|
||||
Ok(persisted_token) => {
|
||||
Ok(persisted_token)
|
||||
},
|
||||
@ -127,3 +128,55 @@ pub async fn register_user(db_conn: &sqlx::PgPool, user: UserRegisterPayload) ->
|
||||
}
|
||||
}
|
||||
}
|
||||
pub async fn authenticate_user(db_conn: &sqlx::PgPool, user: AuthenticateUserDto) -> Result<User, Vec<ErrorResource>> {
|
||||
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 {
|
||||
Ok(persisted_user_opt) => match persisted_user_opt {
|
||||
None => {
|
||||
error_resources.push(ERROR_USER_DOES_NOT_EXIST);
|
||||
return Err(error_resources);
|
||||
},
|
||||
Some(persisted_user) => persisted_user
|
||||
},
|
||||
Err(error) => {
|
||||
error!("{:?}", error);
|
||||
error_resources.push(("ERROR.DATABASE_ERROR", ""));
|
||||
return Err(error_resources);
|
||||
}
|
||||
};
|
||||
|
||||
match validate_user_token(&mut conn, &user.id, user.auth_token).await {
|
||||
Ok(persisted_token_opt) => match persisted_token_opt {
|
||||
None => {
|
||||
error_resources.push(ERROR_INCORRECT_TOKEN);
|
||||
Err(error_resources)
|
||||
},
|
||||
Some(persisted_token) => {
|
||||
// Check if persisted_token expired
|
||||
if Utc::now().timestamp_millis() - persisted_token.last_updated.timestamp_millis() > AUTH_TOKEN_EXPIRATION_TIME_MILLIS {
|
||||
// Expired
|
||||
debug!("Expired token: {:?}", persisted_token);
|
||||
error_resources.push(ERROR_EXPIRED_TOKEN);
|
||||
Err(error_resources)
|
||||
} else {
|
||||
// Not expired
|
||||
Ok(persisted_user)
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(error) => {
|
||||
error!("{:?}", error);
|
||||
error_resources.push(("ERROR.DATABASE_ERROR", ""));
|
||||
Err(error_resources)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,14 +17,14 @@ fn validate_user_email(email: &String) -> bool {
|
||||
&& email.contains('@')
|
||||
&& email.contains('.')
|
||||
}
|
||||
fn validate_user_phone_number(email: &String) -> bool {
|
||||
email.len() >= CredentialType::get_max_length(&CredentialType::PhoneNumber)
|
||||
&& email.len() <= CredentialType::get_min_length(&CredentialType::PhoneNumber)
|
||||
fn validate_user_phone_number(phone_number: &String) -> bool {
|
||||
phone_number.len() <= CredentialType::get_max_length(&CredentialType::PhoneNumber)
|
||||
&& phone_number.len() >= CredentialType::get_min_length(&CredentialType::PhoneNumber)
|
||||
}
|
||||
|
||||
fn validate_user_username(username: &String) -> bool {
|
||||
username.len() >= CredentialType::get_max_length(&CredentialType::PhoneNumber)
|
||||
&& username.len() <= CredentialType::get_min_length(&CredentialType::PhoneNumber)
|
||||
username.len() >= CredentialType::get_max_length(&CredentialType::Username)
|
||||
&& username.len() <= CredentialType::get_min_length(&CredentialType::Username)
|
||||
}
|
||||
fn validate_user_name(name: &String) -> bool {
|
||||
name.len() >= MIN_NAME_LENGTH.into() && name.len() <= MAX_NAME_LENGTH.into()
|
||||
|
Loading…
Reference in New Issue
Block a user