From b5ab349b76fef19f0e81cb3e510c29f5a0a5d973 Mon Sep 17 00:00:00 2001 From: Daelon022 Date: Sat, 16 Mar 2024 20:49:55 +0200 Subject: [PATCH] Refactor code, add login for getting token --- src/actors/handlers.rs | 48 +++++++++++++++++- src/actors/messages.rs | 16 +++++- src/db/utils.rs | 70 +------------------------- src/errors.rs | 3 ++ src/main.rs | 4 +- src/models.rs | 25 ++++++++-- src/registration/mod.rs | 36 -------------- src/user_flow/auth0.rs | 100 ++++++++++++++++++++++++++++++++++++++ src/user_flow/consts.rs | 2 + src/user_flow/mod.rs | 3 ++ src/user_flow/requests.rs | 58 ++++++++++++++++++++++ 11 files changed, 251 insertions(+), 114 deletions(-) delete mode 100644 src/registration/mod.rs create mode 100644 src/user_flow/auth0.rs create mode 100644 src/user_flow/consts.rs create mode 100644 src/user_flow/mod.rs create mode 100644 src/user_flow/requests.rs diff --git a/src/actors/handlers.rs b/src/actors/handlers.rs index 72816b6..b26f668 100644 --- a/src/actors/handlers.rs +++ b/src/actors/handlers.rs @@ -1,5 +1,6 @@ use crate::actors::messages::{ - CreateUser, DeleteUser, UpdateActivateEmail, UpdateEmail, UpdatePassword, UpdateUsername, + CheckIfRegisteredUser, CheckUser, CreateUser, DeleteUser, UpdateActivateEmail, UpdateEmail, + UpdatePassword, UpdateUsername, }; use crate::db::postgres_db::DbService; use crate::db::tables::Users; @@ -144,3 +145,48 @@ impl Handler for DbService { AtomicResponse::new(Box::pin(query.into_actor(&db))) } } + +impl Handler for DbService { + type Result = AtomicResponse>; + + fn handle(&mut self, msg: CheckUser, _: &mut Self::Context) -> Self::Result { + let db = self.clone(); + let conn = async move { db.pool.get().await }; + let query = async move { + let user = crate::db::schema::users::table + .filter(crate::db::schema::users::id.eq(msg.id)) + .first::(&mut conn.await?) + .await?; + Ok(user.id == msg.id) + }; + log::info!("Checking user {}", msg.id); + + let db = self.clone(); + AtomicResponse::new(Box::pin(query.into_actor(&db))) + } +} + +impl Handler for DbService { + type Result = AtomicResponse>; + + fn handle(&mut self, msg: CheckIfRegisteredUser, _: &mut Self::Context) -> Self::Result { + let db = self.clone(); + let conn = async move { db.pool.get().await }; + let query = async move { + let user = crate::db::schema::users::table + .filter(crate::db::schema::users::username.eq(msg.username)) + .filter(crate::db::schema::users::email.eq(msg.email)) + .first::(&mut conn.await?) + .await; + + match user { + Ok(_) => Ok(true), + Err(_) => Ok(false), + } + }; + log::info!("Checking if user is registered"); + + let db = self.clone(); + AtomicResponse::new(Box::pin(query.into_actor(&db))) + } +} diff --git a/src/actors/messages.rs b/src/actors/messages.rs index 859ae9f..3f921a6 100644 --- a/src/actors/messages.rs +++ b/src/actors/messages.rs @@ -1,7 +1,8 @@ use actix::Message; +use serde::Serialize; use uuid::Uuid; -#[derive(Message)] +#[derive(Message, Serialize, Clone)] #[rtype(result = "crate::errors::Result<()>")] pub(crate) struct CreateUser { pub id: Uuid, @@ -42,3 +43,16 @@ pub(crate) struct UpdateUsername { pub user_id: Uuid, pub username: String, } + +#[derive(Message)] +#[rtype(result = "crate::errors::Result")] +pub(crate) struct CheckUser { + pub id: Uuid, +} + +#[derive(Message)] +#[rtype(result = "crate::errors::Result")] +pub(crate) struct CheckIfRegisteredUser { + pub username: String, + pub email: String, +} diff --git a/src/db/utils.rs b/src/db/utils.rs index 73677e6..962dcd9 100644 --- a/src/db/utils.rs +++ b/src/db/utils.rs @@ -1,12 +1,7 @@ -use crate::consts::{ACCESS_TOKEN, APPLICATION_JSON, AUDIENCE, CONTENT_TYPE, GRANT_TYPE_PASS}; -use crate::errors::{Error, Result}; -use crate::models::{ConnectToAuth0, UserData, UserFlow}; +use crate::errors::Result; use diesel_async::pooled_connection::deadpool::Pool; use diesel_async::pooled_connection::AsyncDieselConnectionManager; use diesel_async::AsyncPgConnection; -use reqwest::{Client, Method}; -use serde_json::Value; -use uuid::Uuid; pub type DatabasePool = Pool; @@ -16,66 +11,3 @@ pub async fn create_connection_pool(database_url: String) -> Result Result<()> { - let client = Client::new(); - let url = dotenv::var("CLIENT").unwrap_or_else(|_| "localhost:8080".to_string()); - - let body = generate_body_for_auth0(user, user_id); - - client - .request(Method::POST, &url) - .header(CONTENT_TYPE, APPLICATION_JSON) - .json(&body) - .send() - .await?; - Ok(()) -} - -pub async fn get_jwt_user_token(user: UserData) -> Result { - let client = Client::new(); - let url = dotenv::var("CLIENT").unwrap_or_else(|_| "localhost:8080".to_string()); - - let body = generate_body_for_auth0(user, Uuid::new_v4()); - - let response = client - .request(Method::POST, &url) - .header(CONTENT_TYPE, APPLICATION_JSON) - .json(&body) - .send() - .await?; - - let response: Result = response.json().await.map_err(Error::from); - match response { - Ok(value) => { - log::info!("Value: {:?}", value); - let token = value[ACCESS_TOKEN].as_str().ok_or(Error::InvalidToken)?; - Ok(token.to_string()) - } - Err(e) => { - log::error!("Error: {}", e); - Err(Error::InvalidToken) - } - } -} - -fn generate_body_for_auth0(user: UserData, user_id: Uuid) -> UserFlow { - let client_id = dotenv::var("CLIENT_ID").unwrap_or_else(|_| "admin".to_string()); - let client_secret = dotenv::var("CLIENT_SECRET").unwrap_or_else(|_| "admin".to_string()); - let connection = dotenv::var("CONNECTION") - .unwrap_or_else(|_| "Username-Password-Authentication".to_string()); - - UserFlow { - connect: ConnectToAuth0 { - client_id, - client_secret, - audience: AUDIENCE.to_string(), - grant_type: GRANT_TYPE_PASS.to_string(), - user_id: user_id.to_string(), - connection, - }, - username: user.username, - password: user.password, - email: user.email, - } -} diff --git a/src/errors.rs b/src/errors.rs index 1500d18..84986df 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -41,6 +41,9 @@ pub enum Error { #[error("Cannot get token from Auth0")] InvalidToken, + + #[error(transparent)] + SerdeJsonError(#[from] serde_json::Error), } pub type Result = std::result::Result; diff --git a/src/main.rs b/src/main.rs index 4749275..25e2001 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,12 +3,12 @@ pub mod consts; pub mod db; pub mod errors; pub mod models; -pub mod registration; +pub mod user_flow; pub mod utils; use crate::db::postgres_db::DbService; use crate::errors::Result; -use crate::registration::{check_token, login, register}; +use crate::user_flow::requests::{check_token, login, register}; use crate::utils::init_logging; use actix::Actor; use actix_web::web::Data; diff --git a/src/models.rs b/src/models.rs index 776dcd3..e80a662 100644 --- a/src/models.rs +++ b/src/models.rs @@ -1,4 +1,5 @@ use serde::{Deserialize, Serialize}; +use uuid::Uuid; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct UserData { @@ -8,20 +9,34 @@ pub struct UserData { } #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct UserFlow { - #[serde(flatten)] - pub connect: ConnectToAuth0, +pub struct RegisteredUserData { + pub id: Uuid, pub username: String, - pub password: String, pub email: String, + pub password: String, } #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ConnectToAuth0 { +pub struct ConnectToAuth0 { pub client_id: String, pub client_secret: String, pub audience: String, pub grant_type: String, pub user_id: String, pub connection: String, + #[serde(flatten)] + pub extra: T, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct RegistrationFlow { + pub username: String, + pub password: String, + pub email: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct LoginFlow { + pub username: String, + pub password: String, } diff --git a/src/registration/mod.rs b/src/registration/mod.rs deleted file mode 100644 index 03688fd..0000000 --- a/src/registration/mod.rs +++ /dev/null @@ -1,36 +0,0 @@ -use crate::actors::messages::CreateUser; -use crate::db::postgres_db::DbService; -use crate::db::utils::{get_jwt_user_token, register_user}; -use crate::errors::Result; -use crate::models::UserData; -use actix::Addr; -use actix_web::web::{Data, Json}; -use actix_web::HttpResponse; -use uuid::Uuid; - -pub async fn register(user: Json, db: Data>) -> Result { - let user_id = Uuid::new_v4(); - register_user(user.clone(), user_id).await?; - - let token = get_jwt_user_token(user.clone()).await?; - - db.send(CreateUser { - id: user_id, - username: user.username.clone(), - password: user.password.clone(), - email: user.email.clone(), - }) - .await??; - log::info!("Getting request for register!"); - Ok(HttpResponse::Ok().body(token)) -} - -pub async fn login(_db: Data) -> Result { - log::info!("Getting request for login!"); - Ok(HttpResponse::Ok().finish()) -} - -pub async fn check_token(_db: Data) -> Result { - log::info!("Getting request for checking token!"); - Ok(HttpResponse::Ok().finish()) -} diff --git a/src/user_flow/auth0.rs b/src/user_flow/auth0.rs new file mode 100644 index 0000000..d557162 --- /dev/null +++ b/src/user_flow/auth0.rs @@ -0,0 +1,100 @@ +use crate::consts::{ACCESS_TOKEN, APPLICATION_JSON, AUDIENCE, CONTENT_TYPE, GRANT_TYPE_PASS}; +use crate::errors::Error; +use crate::models::{ConnectToAuth0, LoginFlow, RegisteredUserData, RegistrationFlow, UserData}; +use crate::user_flow::consts::{LOGIN_URL, REGISTRATION_URL}; +use actix_web::http::Method; +use reqwest::Client; +use serde_json::Value; +use uuid::Uuid; + +pub async fn register_user(user: UserData, user_id: Uuid) -> crate::errors::Result<()> { + let client = Client::new(); + let client_url = dotenv::var("CLIENT").unwrap_or_else(|_| "localhost:8080".to_string()); + + let url = format!("{}{}", client_url, REGISTRATION_URL); + + let body = generate_body_for_registration(user_id, user.username, user.password, user.email); + + client + .request(Method::POST, &url) + .header(CONTENT_TYPE, APPLICATION_JSON) + .json(&body) + .send() + .await?; + Ok(()) +} + +pub async fn get_jwt_user_token(user: RegisteredUserData) -> crate::errors::Result { + let client = Client::new(); + let client_url = dotenv::var("CLIENT").unwrap_or_else(|_| "localhost:8080".to_string()); + + let url = format!("{}{}", client_url, LOGIN_URL); + + let body = generate_body_for_login(user.id, user.username, user.password).await; + + let response = client + .request(Method::POST, &url) + .header(CONTENT_TYPE, APPLICATION_JSON) + .json(&body) + .send() + .await?; + + let response: crate::errors::Result = response.json().await.map_err(Error::from); + match response { + Ok(value) => { + let token = value[ACCESS_TOKEN].as_str().ok_or(Error::InvalidToken)?; + Ok(token.to_string()) + } + Err(e) => { + log::error!("Error: {}", e); + Err(Error::InvalidToken) + } + } +} + +fn generate_body_for_registration( + user_id: Uuid, + username: String, + password: String, + email: String, +) -> ConnectToAuth0 { + let client_id = dotenv::var("CLIENT_ID").unwrap_or_else(|_| "admin".to_string()); + let client_secret = dotenv::var("CLIENT_SECRET").unwrap_or_else(|_| "admin".to_string()); + let connection = dotenv::var("CONNECTION") + .unwrap_or_else(|_| "Username-Password-Authentication".to_string()); + + ConnectToAuth0 { + client_id, + client_secret, + audience: AUDIENCE.to_string(), + grant_type: GRANT_TYPE_PASS.to_string(), + user_id: user_id.to_string(), + connection, + extra: RegistrationFlow { + username, + password, + email, + }, + } +} + +pub async fn generate_body_for_login( + user_id: Uuid, + username: String, + password: String, +) -> ConnectToAuth0 { + let client_id = dotenv::var("CLIENT_ID").unwrap_or_else(|_| "admin".to_string()); + let client_secret = dotenv::var("CLIENT_SECRET").unwrap_or_else(|_| "admin".to_string()); + let connection = dotenv::var("CONNECTION") + .unwrap_or_else(|_| "Username-Password-Authentication".to_string()); + + ConnectToAuth0 { + client_id, + client_secret, + audience: AUDIENCE.to_string(), + grant_type: GRANT_TYPE_PASS.to_string(), + user_id: user_id.to_string(), + connection, + extra: LoginFlow { username, password }, + } +} diff --git a/src/user_flow/consts.rs b/src/user_flow/consts.rs new file mode 100644 index 0000000..1f1212e --- /dev/null +++ b/src/user_flow/consts.rs @@ -0,0 +1,2 @@ +pub const REGISTRATION_URL: &str = "/dbconnections/signup"; +pub const LOGIN_URL: &str = "/oauth/token"; diff --git a/src/user_flow/mod.rs b/src/user_flow/mod.rs new file mode 100644 index 0000000..0c9b5f1 --- /dev/null +++ b/src/user_flow/mod.rs @@ -0,0 +1,3 @@ +pub mod auth0; +pub mod consts; +pub mod requests; diff --git a/src/user_flow/requests.rs b/src/user_flow/requests.rs new file mode 100644 index 0000000..039d0e4 --- /dev/null +++ b/src/user_flow/requests.rs @@ -0,0 +1,58 @@ +use crate::actors::messages::{CheckIfRegisteredUser, CheckUser, CreateUser}; +use crate::db::postgres_db::DbService; +use crate::models::{RegisteredUserData, UserData}; +use crate::user_flow::auth0::{get_jwt_user_token, register_user}; +use actix::Addr; +use actix_web::web::{Data, Json}; +use actix_web::HttpResponse; +use uuid::Uuid; + +pub async fn register( + user: Json, + db: Data>, +) -> crate::errors::Result { + let user_id = Uuid::new_v4(); + register_user(user.clone(), user_id).await?; + + let if_user = CheckIfRegisteredUser { + username: user.username.clone(), + email: user.email.clone(), + }; + + if db.send(if_user).await?? { + return Ok(HttpResponse::BadRequest().finish()); + } + + let user = CreateUser { + id: user_id, + username: user.username.clone(), + password: user.password.clone(), + email: user.email.clone(), + }; + + db.send(user.clone()).await??; + + log::info!("Getting request for register!"); + Ok(HttpResponse::Ok().body(serde_json::to_string(&user)?)) +} + +pub async fn login( + db: Data>, + user: Json, +) -> crate::errors::Result { + let if_user = CheckUser { id: user.id }; + + if db.send(if_user).await?? { + log::info!("Getting request for login!"); + let token = get_jwt_user_token(user.0.clone()).await?; + let json = serde_json::json!({ "user": user, "token": token }); + Ok(HttpResponse::Ok().body(json.to_string())) + } else { + Ok(HttpResponse::BadRequest().finish()) + } +} + +pub async fn check_token(_db: Data) -> crate::errors::Result { + log::info!("Getting request for checking token!"); + Ok(HttpResponse::Ok().finish()) +}