Fixed user service actix backend
This commit is contained in:
parent
3de2319fd7
commit
5b86b21734
|
@ -19,4 +19,4 @@ futures-util = "0.3"
|
|||
[build-dependencies]
|
||||
dotenv = "0.15.0"
|
||||
sqlx = { version = "0.6.0", features = [ "runtime-tokio-rustls", "mysql", "chrono" ] }
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
tokio = { version = "1", features = ["full"] }
|
|
@ -0,0 +1 @@
|
|||
-- Add migration script here
|
|
@ -3,23 +3,11 @@ use std::collections::HashMap;
|
|||
use sqlx::{MySqlPool};
|
||||
|
||||
pub async fn start_database_connection(env_vars: &HashMap<String, String>) -> Result<MySqlPool, sqlx::Error>{
|
||||
let db_user = match env_vars.get("DB_USER") {
|
||||
let db_url = match env_vars.get("DATABASE_URL") {
|
||||
Some(str) => str,
|
||||
None => panic!("DB_USER env var not found")
|
||||
None => panic!("DATABASE_URL env var not found")
|
||||
};
|
||||
let db_pass = match env_vars.get("DB_PASS") {
|
||||
Some(str) => str,
|
||||
None => panic!("DB_PASS env var not found")
|
||||
};
|
||||
let db_host = match env_vars.get("DB_HOST") {
|
||||
Some(str) => str,
|
||||
None => panic!("DB_HOST env var not found")
|
||||
};
|
||||
let db_database_name = match env_vars.get("DB_DATABASE_NAME") {
|
||||
Some(str) => str,
|
||||
None => panic!("DB_DATABASE_NAME env var not found")
|
||||
};
|
||||
let formatted_db_url = &format!("mysql://{db_user}:{db_pass}@{db_host}/{db_database_name}");
|
||||
let formatted_db_url = &db_url;
|
||||
sqlx::MySqlPool::connect(&formatted_db_url).await
|
||||
}
|
||||
pub async fn run_all_migrations(conn: &MySqlPool){
|
||||
|
|
|
@ -10,6 +10,6 @@ pub async fn insert_user(conn: &MySqlPool, user_to_insert: &User) -> Result<MySq
|
|||
pub async fn find_user_by_email(conn: &MySqlPool, email: &String, app: &String) -> Result<User, sqlx::Error>{
|
||||
sqlx::query_file_as!(User, "sql/schema/user/find_with_email.sql", email, app).fetch_one(conn).await
|
||||
}
|
||||
pub async fn _find_user_by_id(conn: &MySqlPool, id: &i32) -> Result<User, sqlx::Error> {
|
||||
pub async fn find_user_by_id(conn: &MySqlPool, id: &i32) -> Result<User, sqlx::Error> {
|
||||
sqlx::query_file_as!(User, "sql/schema/user/find_with_id.sql", id).fetch_one(conn).await
|
||||
}
|
|
@ -6,9 +6,12 @@ pub const REFRESH_TOKEN_EXPIRATION_TIME_IN_DAYS: i32 = 20;
|
|||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Token {
|
||||
#[serde(skip_serializing)]
|
||||
pub id: i32,
|
||||
pub user_id: i32,
|
||||
#[serde(skip_serializing)]
|
||||
pub time_created: Option<NaiveDateTime>,
|
||||
#[serde(skip_serializing)]
|
||||
pub last_updated: Option<NaiveDateTime>,
|
||||
pub auth_token: String,
|
||||
pub refresh_token: String
|
||||
|
|
|
@ -6,12 +6,16 @@ use crate::dto::user_dtos::UserForCreationDto;
|
|||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct User{
|
||||
pub id: i32,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub time_created: Option<NaiveDateTime>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub last_updated: Option<NaiveDateTime>,
|
||||
pub app: String,
|
||||
pub email: String,
|
||||
pub name: String,
|
||||
#[serde(skip_serializing)]
|
||||
pub password: String,
|
||||
#[serde(skip_serializing)]
|
||||
pub salt: String
|
||||
}
|
||||
impl User {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
#[forbid(unsafe_code)]
|
||||
mod r#do; mod dao;
|
||||
mod routes; mod service;
|
||||
mod util; mod dto;
|
||||
|
|
|
@ -19,4 +19,6 @@ pub const ERROR_INCORRECT_TOKEN: (&str, &str) = ("ERROR.INCORRECT_TOKEN", "The t
|
|||
|
||||
pub const ERROR_MISSING_TOKEN: (&str, &str) = ("ERROR.MISSING_TOKEN", "No token supplied.");
|
||||
|
||||
pub const ERROR_EXPIRED_TOKEN: (&str, &str) = ("ERROR.EXPIRED_TOKEN", "The token you have supplied is expired.");
|
||||
pub const ERROR_EXPIRED_TOKEN: (&str, &str) = ("ERROR.EXPIRED_TOKEN", "The token you have supplied is expired.");
|
||||
|
||||
pub const ERROR_CREATING_TOKEN: (&str, &str) = ("ERROR.CREATING_TOKEN", "The server had an error creating the auth tokens.");
|
|
@ -27,7 +27,6 @@ pub async fn start_all_routes(after_startup_fn_call: &dyn Fn(), state: SharedSta
|
|||
None => {panic!("HOST_PORT env variable not found.");},
|
||||
};
|
||||
|
||||
|
||||
// Extract variables to be put into shared app state & clone them
|
||||
let db_conn_state = web::Data::new(Arc::new(state.db_conn));
|
||||
let env_vars_state = web::Data::new(Mutex::new(state.env_vars.clone()));
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
use core::panic;
|
||||
use std::{sync::{Arc}};
|
||||
|
||||
use actix_web::{web::{self, Data}, HttpResponse, post, patch, HttpRequest};
|
||||
use chrono::{Utc};
|
||||
use sqlx::{MySqlPool};
|
||||
|
||||
use crate::{r#do::user::User, dao::{user_dao::{insert_user, find_user_by_email}, token_dao::{insert_token, self, update_token_with_id}}, dto::{user_dtos::{UserForCreationDto, UserForLoginDto}, message_resources_dtos::MessageResourceDto}, validation::user_validator, util::hasher::{self, generate_multiple_random_token_with_rng}, r#do::token::Token, resources::error_messages::{ERROR_USER_ALREADY_EXISTS, ERROR_USER_DOES_NOT_EXIST, ERROR_PASSWORD_INCORRECT, ERROR_INVALID_TOKEN, ERROR_MISSING_TOKEN, ERROR_INCORRECT_TOKEN, ERROR_EXPIRED_TOKEN}, r#do::token::AUTH_TOKEN_EXPIRATION_TIME_IN_DAYS, r#do::token::REFRESH_TOKEN_EXPIRATION_TIME_IN_DAYS};
|
||||
use crate::{r#do::user::User, dao::{user_dao::{insert_user, find_user_by_email, find_user_by_id}, token_dao::{insert_token, self, update_token_with_id}}, dto::{user_dtos::{UserForCreationDto, UserForLoginDto}, message_resources_dtos::MessageResourceDto}, validation::user_validator, util::hasher::{self, generate_multiple_random_token_with_rng}, r#do::token::Token, resources::error_messages::{ERROR_USER_ALREADY_EXISTS, ERROR_USER_DOES_NOT_EXIST, ERROR_PASSWORD_INCORRECT, ERROR_INVALID_TOKEN, ERROR_MISSING_TOKEN, ERROR_INCORRECT_TOKEN, ERROR_EXPIRED_TOKEN, ERROR_CREATING_TOKEN}, r#do::token::AUTH_TOKEN_EXPIRATION_TIME_IN_DAYS, r#do::token::REFRESH_TOKEN_EXPIRATION_TIME_IN_DAYS};
|
||||
|
||||
|
||||
|
||||
#[post("/user")]
|
||||
pub async fn create_user(incoming_user: web::Json<UserForCreationDto>, db_conn: Data<Arc<MySqlPool>>) -> HttpResponse {
|
||||
|
@ -60,13 +61,15 @@ pub async fn create_user(incoming_user: web::Json<UserForCreationDto>, db_conn:
|
|||
// Insert token in DB
|
||||
match insert_token(&db_conn, &token_to_insert).await{
|
||||
Ok(resultrs) => {token_to_insert.id = resultrs.last_insert_id() as i32},
|
||||
Err(e) => {panic!("{e}")}
|
||||
Err(_e) => {return HttpResponse::InternalServerError().finish()}
|
||||
}
|
||||
|
||||
// All good? Send an OK!
|
||||
HttpResponse::Ok().json(web::Json(token_to_insert))
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[post("/user/auth/password")]
|
||||
pub async fn authenticate_user_with_password(incoming_user: web::Json<UserForLoginDto>, db_conn: Data<Arc<MySqlPool>>) -> HttpResponse {
|
||||
let mut message_resources: Vec<MessageResourceDto> = Vec::new();
|
||||
|
@ -81,7 +84,7 @@ pub async fn authenticate_user_with_password(incoming_user: web::Json<UserForLog
|
|||
// If user exists get it, if it doesn't blow up to the client
|
||||
let persisted_user = match find_user_by_email(&db_conn, &incoming_user_obj.email, &incoming_user_obj.app).await {
|
||||
Ok(rs) => {rs},
|
||||
Err(_e) => {
|
||||
Err(_) => {
|
||||
message_resources.push(MessageResourceDto::new_from_error_message(ERROR_USER_DOES_NOT_EXIST));
|
||||
return HttpResponse::NotFound().json(web::Json(message_resources));
|
||||
}};
|
||||
|
@ -97,21 +100,32 @@ pub async fn authenticate_user_with_password(incoming_user: web::Json<UserForLog
|
|||
}
|
||||
|
||||
// TODO: create new token & check for all the others that may have expired.
|
||||
let tokens = generate_multiple_random_token_with_rng(2).await.expect("Error creating multiple tokens.");
|
||||
let tokens = match generate_multiple_random_token_with_rng(2).await {
|
||||
Ok(generated_tokens) => generated_tokens,
|
||||
Err(_) => {
|
||||
message_resources.push(MessageResourceDto::new_from_error_message(ERROR_CREATING_TOKEN));
|
||||
return HttpResponse::InternalServerError().json(web::Json(message_resources))
|
||||
}
|
||||
};
|
||||
let mut token_to_insert = Token::new(persisted_user.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()
|
||||
);
|
||||
); //TODO: I don't like these panics
|
||||
|
||||
// Insert token in DB
|
||||
match insert_token(&db_conn, &token_to_insert).await{
|
||||
Ok(resultrs) => {token_to_insert.id = resultrs.last_insert_id() as i32},
|
||||
Err(e) => {panic!("{e}")}
|
||||
Err(_) => {
|
||||
message_resources.push(MessageResourceDto::new_from_error_message(ERROR_CREATING_TOKEN));
|
||||
return HttpResponse::InternalServerError().json(message_resources)
|
||||
}
|
||||
};
|
||||
|
||||
HttpResponse::Ok().json(token_to_insert)
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[post("/user/auth/token/{user_id}")]
|
||||
pub async fn authenticate_user_with_auth_token(request: HttpRequest, user_id: web::Path<i32>, db_conn: Data<Arc<MySqlPool>>) -> HttpResponse{
|
||||
let mut message_resources: Vec<MessageResourceDto> = Vec::new();
|
||||
|
@ -140,7 +154,12 @@ pub async fn authenticate_user_with_auth_token(request: HttpRequest, user_id: we
|
|||
let now = Utc::now().naive_utc();
|
||||
match token.auth_token == auth_token{
|
||||
true if token.last_updated.unwrap().signed_duration_since(now).num_days() <
|
||||
AUTH_TOKEN_EXPIRATION_TIME_IN_DAYS.into() => { return HttpResponse::Ok().finish(); },
|
||||
AUTH_TOKEN_EXPIRATION_TIME_IN_DAYS.into() => {
|
||||
match find_user_by_id(&db_conn, &user_id).await {
|
||||
Ok(persisted_user) => return HttpResponse::Ok().json(web::Json(persisted_user)),
|
||||
Err(_) => {return HttpResponse::NotFound().json(MessageResourceDto::new_from_error_message(ERROR_USER_DOES_NOT_EXIST))}
|
||||
};
|
||||
},
|
||||
true => {
|
||||
message_resources.push(MessageResourceDto::new_from_error_message(ERROR_EXPIRED_TOKEN));
|
||||
return HttpResponse::Unauthorized().json(web::Json(message_resources));
|
||||
|
@ -160,6 +179,8 @@ pub async fn authenticate_user_with_auth_token(request: HttpRequest, user_id: we
|
|||
}};
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[patch("/user/refresh/{user_id}")]
|
||||
pub async fn refresh_auth_token(request: HttpRequest, user_id: web::Path<i32>, db_conn: Data<Arc<MySqlPool>>) -> HttpResponse{
|
||||
let mut message_resources: Vec<MessageResourceDto> = Vec::new();
|
||||
|
@ -168,8 +189,7 @@ pub async fn refresh_auth_token(request: HttpRequest, user_id: web::Path<i32>, d
|
|||
Some(token) => {
|
||||
match token.to_str(){
|
||||
Ok(value) => {value},
|
||||
Err(e) => {
|
||||
println!("{}", e);
|
||||
Err(_) => {
|
||||
message_resources.push(MessageResourceDto::new_from_error_message(ERROR_INVALID_TOKEN));
|
||||
return HttpResponse::BadRequest().json(web::Json(message_resources));
|
||||
}}},
|
||||
|
|
|
@ -18,7 +18,7 @@ pub async fn generate_multiple_random_token_with_rng(amount: u8) -> Result<Vec<S
|
|||
let future_token = async move {
|
||||
let mut token_arr = [0u8; digest::SHA512_OUTPUT_LEN];
|
||||
match cloned_rng.fill(&mut token_arr){
|
||||
Ok(()) => {BASE64.encode(&token_arr)},
|
||||
Ok(()) => {BASE64.encode(&token_arr)}, //TODO: Remove this panic, make your own error and fix this
|
||||
Err(_e) => { panic!("Failed to generate random token for some reason.") }
|
||||
}};
|
||||
tokens.push(tokio::spawn(future_token));
|
||||
|
@ -68,7 +68,7 @@ pub fn hash_password(password: &String) -> HashResult{
|
|||
// Fill array with random-generated salt
|
||||
match rng.fill(&mut salt){
|
||||
Ok(()) => {},
|
||||
Err(_e) => {panic!("Failed to generate random salt for some reason.")}
|
||||
Err(_e) => {panic!("Failed to generate random salt for some reason.")} //TODO: Remove this panic, make your own error and fix this
|
||||
}
|
||||
|
||||
// Create empty 64-bit byte array for the hash + salt
|
||||
|
|
Loading…
Reference in New Issue