Skip to content

Commit

Permalink
Create auth-service, add actors, add getting token from auth0
Browse files Browse the repository at this point in the history
  • Loading branch information
Daelon022 committed Mar 10, 2024
1 parent e6ca32b commit 0b8c907
Show file tree
Hide file tree
Showing 16 changed files with 534 additions and 0 deletions.
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,9 @@ Cargo.lock
# MSVC Windows builds of rustc generate these, which store debugging information
*.pdb
.idea
.env


# Added by cargo

/target
30 changes: 30 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
[package]
name = "auth-service"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
actix = "0.13.3"
serde = { version = "1.0.197", features = ["derive"] }
serde_json = "1.0.114"
actix-web = "4.0.0-beta.8"
diesel = { version = "=2.1.4", features = [
"extras",
"uuid",
"numeric",
"chrono",
] }
diesel-derive-enum = { version = "2.0.0-rc.0", features = ["postgres"] }
diesel-async = { version = "0.4.1", features = ["postgres", "deadpool"] }
tokio = "1.36.0"
thiserror = "1.0.57"
uuid = { version = "1.7.0", features = ["v4"] }
chrono = "0.4.35"
reqwest = { version = "0.11.25", features = ["json"] }
log = "0.4.21"
anyhow = "1.0.80"
fern = "0.6.2"
colored = "2.1.0"
dotenv = "0.15.0"
1 change: 1 addition & 0 deletions migrations/2024-03-09-212028_create_user_table/down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DROP TABLE users;
9 changes: 9 additions & 0 deletions migrations/2024-03-09-212028_create_user_table/up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
CREATE TABLE users (
id UUID PRIMARY KEY,
username VARCHAR(255),
email VARCHAR(255) NOT NULL,
password VARCHAR(255) NOT NULL,
is_email_activate BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP
);
146 changes: 146 additions & 0 deletions src/actors/handlers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
use crate::actors::messages::{
CreateUser, DeleteUser, UpdateActivateEmail, UpdateEmail, UpdatePassword, UpdateUsername,
};
use crate::db::postgres_db::DbService;
use crate::db::tables::Users;
use actix::{AtomicResponse, Handler, WrapFuture};
use diesel::{ExpressionMethods, QueryDsl};
use diesel_async::RunQueryDsl;

impl Handler<CreateUser> for DbService {
type Result = AtomicResponse<Self, crate::errors::Result<()>>;

fn handle(&mut self, msg: CreateUser, _: &mut Self::Context) -> Self::Result {
let db = self.clone();
let conn = async move { db.pool.get().await };
let result = async move {
let user = Users {
id: msg.id,
username: msg.username,
email: msg.email,
password: msg.password,
is_email_activate: false,
created_at: chrono::Utc::now(),
updated_at: None,
};

let _ = diesel::insert_into(crate::db::schema::users::table)
.values(user)
.execute(&mut conn.await?)
.await?;
Ok(())
};
log::info!("Creating user {}", msg.id);

let db = self.clone();

AtomicResponse::new(Box::pin(result.into_actor(&db)))
}
}

impl Handler<UpdateActivateEmail> for DbService {
type Result = AtomicResponse<Self, crate::errors::Result<()>>;

fn handle(&mut self, msg: UpdateActivateEmail, _: &mut Self::Context) -> Self::Result {
let db = self.clone();
let conn = async move { db.pool.get().await };

let query = async move {
let _ = diesel::update(crate::db::schema::users::table)
.filter(crate::db::schema::users::id.eq(msg.user_id))
.set(crate::db::schema::users::is_email_activate.eq(true))
.execute(&mut conn.await?)
.await?;
Ok(())
};
log::info!("Updating user is_activate_email {}", msg.user_id);

let db = self.clone();
AtomicResponse::new(Box::pin(query.into_actor(&db)))
}
}

impl Handler<DeleteUser> for DbService {
type Result = AtomicResponse<Self, crate::errors::Result<()>>;

fn handle(&mut self, msg: DeleteUser, _: &mut Self::Context) -> Self::Result {
let db = self.clone();
let conn = async move { db.pool.get().await };
let query = async move {
let _ = diesel::delete(
crate::db::schema::users::table
.filter(crate::db::schema::users::id.eq(msg.user_id)),
)
.execute(&mut conn.await?)
.await?;
Ok(())
};
log::info!("Deleting user {}", msg.user_id);

let db = self.clone();
AtomicResponse::new(Box::pin(query.into_actor(&db)))
}
}

impl Handler<UpdatePassword> for DbService {
type Result = AtomicResponse<Self, crate::errors::Result<()>>;

fn handle(&mut self, msg: UpdatePassword, _: &mut Self::Context) -> Self::Result {
let db = self.clone();
let conn = async move { db.pool.get().await };
let query = async move {
let _ = diesel::update(crate::db::schema::users::table)
.filter(crate::db::schema::users::id.eq(msg.user_id))
.set(crate::db::schema::users::password.eq(msg.password))
.execute(&mut conn.await?)
.await?;
Ok(())
};
log::info!("Updating user password {}", msg.user_id);

let db = self.clone();
AtomicResponse::new(Box::pin(query.into_actor(&db)))
}
}

impl Handler<UpdateEmail> for DbService {
type Result = AtomicResponse<Self, crate::errors::Result<()>>;

fn handle(&mut self, msg: UpdateEmail, _: &mut Self::Context) -> Self::Result {
let db = self.clone();
let conn = async move { db.pool.get().await };
let query = async move {
let _ = diesel::update(crate::db::schema::users::table)
.filter(crate::db::schema::users::id.eq(msg.user_id))
.set(crate::db::schema::users::email.eq(msg.email))
.execute(&mut conn.await?)
.await?;
Ok(())
};
log::info!("Updating user email {}", msg.user_id);

let db = self.clone();
AtomicResponse::new(Box::pin(query.into_actor(&db)))
}
}

impl Handler<UpdateUsername> for DbService {
type Result = AtomicResponse<Self, crate::errors::Result<()>>;

fn handle(&mut self, msg: UpdateUsername, _: &mut Self::Context) -> Self::Result {
let db = self.clone();
let conn = async move { db.pool.get().await };
let query = async move {
let _ = diesel::update(crate::db::schema::users::table)
.filter(crate::db::schema::users::id.eq(msg.user_id))
.set(crate::db::schema::users::username.eq(msg.username))
.execute(&mut conn.await?)
.await?;
Ok(())
};
log::info!("Updating user username {}", msg.user_id);

let db = self.clone();
AtomicResponse::new(Box::pin(query.into_actor(&db)))
}
}
44 changes: 44 additions & 0 deletions src/actors/messages.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use actix::Message;
use uuid::Uuid;

#[derive(Message)]
#[rtype(result = "crate::errors::Result<()>")]
pub(crate) struct CreateUser {
pub id: Uuid,
pub username: String,
pub password: String,
pub email: String,
}

#[derive(Message)]
#[rtype(result = "crate::errors::Result<()>")]
pub(crate) struct UpdateActivateEmail {
pub user_id: Uuid,
}

#[derive(Message)]
#[rtype(result = "crate::errors::Result<()>")]
pub(crate) struct DeleteUser {
pub user_id: Uuid,
}

#[derive(Message)]
#[rtype(result = "crate::errors::Result<()>")]
pub(crate) struct UpdatePassword {
pub user_id: Uuid,
pub password: String,
}

#[derive(Message)]
#[rtype(result = "crate::errors::Result<()>")]
pub(crate) struct UpdateEmail {
pub user_id: Uuid,
pub email: String,
}

#[derive(Message)]
#[rtype(result = "crate::errors::Result<()>")]
pub(crate) struct UpdateUsername {
pub user_id: Uuid,
pub username: String,
}
2 changes: 2 additions & 0 deletions src/actors/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod handlers;
pub mod messages;
7 changes: 7 additions & 0 deletions src/db/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
pub mod postgres_db;

pub mod tables;

pub mod schema;

pub mod utils;
17 changes: 17 additions & 0 deletions src/db/postgres_db.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use crate::db::utils::DatabasePool;
use actix::Actor;

#[derive(Clone)]
pub struct DbService {
pub(crate) pool: DatabasePool,
}

impl DbService {
pub fn new(pool: DatabasePool) -> Self {
Self { pool }
}
}

impl Actor for DbService {
type Context = actix::Context<Self>;
}
9 changes: 9 additions & 0 deletions src/db/schema.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
diesel::table!(users {
id -> Uuid,
username -> Varchar,
email -> Varchar,
password -> Varchar,
is_email_activate -> Bool,
created_at -> Timestamptz,
updated_at -> Nullable<Timestamptz>
});
16 changes: 16 additions & 0 deletions src/db/tables.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
use crate::db::schema::users;
use chrono::{DateTime, Utc};
use diesel::{Identifiable, Insertable, Queryable};
use uuid::Uuid;

#[derive(Debug, Clone, Queryable, Insertable, Identifiable)]
#[diesel(table_name = users)]
pub struct Users {
pub id: Uuid,
pub username: String,
pub email: String,
pub password: String,
pub is_email_activate: bool,
pub created_at: DateTime<Utc>,
pub updated_at: Option<DateTime<Utc>>,
}
40 changes: 40 additions & 0 deletions src/db/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
use crate::errors::{Error, 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;

pub type DatabasePool = Pool<AsyncPgConnection>;

pub async fn create_connection_pool(database_url: String) -> Result<DatabasePool> {
let manager = AsyncDieselConnectionManager::new(database_url);
let pool = Pool::builder(manager).build()?;

Ok(pool)
}

pub async fn get_jwt_token() -> Result<String> {
let client = Client::new();
let url = dotenv::var("CLIENT").unwrap_or_else(|_| "localhost:8080".to_string());
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 request = client
.request(Method::POST, &url)
.header("Content-Type", "application/json")
.json(&serde_json::json!({
"client_id": client_id,
"client_secret": client_secret,
"audience":"https://someexample.com",
"grant_type":"client_credentials"
}))
.send()
.await?;
let response = request.json::<Value>().await?;
log::info!("Response: {:?}", response);
let token = match response["access_token"].as_str() {
Some(token) => token,
None => return Err(Error::InvalidInput("Invalid token".to_string())),
};
Ok(token.to_string())
}
Loading

0 comments on commit 0b8c907

Please sign in to comment.