Skip to content

Commit

Permalink
Refactor code, add login for getting token
Browse files Browse the repository at this point in the history
Daelon022 committed Mar 16, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent d39acff commit b5ab349
Showing 11 changed files with 251 additions and 114 deletions.
48 changes: 47 additions & 1 deletion src/actors/handlers.rs
Original file line number Diff line number Diff line change
@@ -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<UpdateUsername> for DbService {
AtomicResponse::new(Box::pin(query.into_actor(&db)))
}
}

impl Handler<CheckUser> for DbService {
type Result = AtomicResponse<Self, crate::errors::Result<bool>>;

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::<Users>(&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<CheckIfRegisteredUser> for DbService {
type Result = AtomicResponse<Self, crate::errors::Result<bool>>;

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::<Users>(&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)))
}
}
16 changes: 15 additions & 1 deletion src/actors/messages.rs
Original file line number Diff line number Diff line change
@@ -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<bool>")]
pub(crate) struct CheckUser {
pub id: Uuid,
}

#[derive(Message)]
#[rtype(result = "crate::errors::Result<bool>")]
pub(crate) struct CheckIfRegisteredUser {
pub username: String,
pub email: String,
}
70 changes: 1 addition & 69 deletions src/db/utils.rs
Original file line number Diff line number Diff line change
@@ -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<AsyncPgConnection>;

@@ -16,66 +11,3 @@ pub async fn create_connection_pool(database_url: String) -> Result<DatabasePool

Ok(pool)
}

pub async fn register_user(user: UserData, user_id: Uuid) -> 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<String> {
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<Value> = 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,
}
}
3 changes: 3 additions & 0 deletions src/errors.rs
Original file line number Diff line number Diff line change
@@ -41,6 +41,9 @@ pub enum Error {

#[error("Cannot get token from Auth0")]
InvalidToken,

#[error(transparent)]
SerdeJsonError(#[from] serde_json::Error),
}

pub type Result<T> = std::result::Result<T, Error>;
4 changes: 2 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -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;
25 changes: 20 additions & 5 deletions src/models.rs
Original file line number Diff line number Diff line change
@@ -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<T: Serialize> {
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,
}
36 changes: 0 additions & 36 deletions src/registration/mod.rs

This file was deleted.

100 changes: 100 additions & 0 deletions src/user_flow/auth0.rs
Original file line number Diff line number Diff line change
@@ -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<String> {
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<Value> = 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<RegistrationFlow> {
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<LoginFlow> {
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 },
}
}
2 changes: 2 additions & 0 deletions src/user_flow/consts.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub const REGISTRATION_URL: &str = "/dbconnections/signup";
pub const LOGIN_URL: &str = "/oauth/token";
3 changes: 3 additions & 0 deletions src/user_flow/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub mod auth0;
pub mod consts;
pub mod requests;
58 changes: 58 additions & 0 deletions src/user_flow/requests.rs
Original file line number Diff line number Diff line change
@@ -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<UserData>,
db: Data<Addr<DbService>>,
) -> crate::errors::Result<HttpResponse> {
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<Addr<DbService>>,
user: Json<RegisteredUserData>,
) -> crate::errors::Result<HttpResponse> {
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<DbService>) -> crate::errors::Result<HttpResponse> {
log::info!("Getting request for checking token!");
Ok(HttpResponse::Ok().finish())
}

0 comments on commit b5ab349

Please sign in to comment.