Skip to content

Commit

Permalink
Get auth user
Browse files Browse the repository at this point in the history
  • Loading branch information
dev-davexoyinbo committed Aug 14, 2023
1 parent ae6857a commit b5bf10e
Show file tree
Hide file tree
Showing 9 changed files with 107 additions and 19 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ edition = "2021"
[dependencies]
actix-web = "4.3.1"
argon2 = "0.5.1"
chrono = "0.4.26"
chrono = { version = "0.4.26", features = ["serde"] }
config = "0.13.3"
derive_more = "0.99.17"
env_logger = "0.10.0"
Expand All @@ -17,6 +17,7 @@ handlebars = "4.3.7"
jsonwebtoken = "8.3.0"
log = "0.4.19"
serde = { version = "1.0.177", features = ["derive"] }
serde_json = "1.0.104"
sqlx = { version = "0.7.1", features = ["runtime-async-std-native-tls", "postgres", "chrono", "uuid"] }
tokio = { version = "1.30.0", features = ["full"] }

Expand Down
44 changes: 44 additions & 0 deletions src/auth/extractors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use std::{ops::Deref, pin::Pin};

use actix_web::{FromRequest, HttpMessage, error::ErrorUnauthorized};
use futures_util::Future;

use crate::models::dto_models::ResponseDTO;

use super::models::AuthenticationInfo;

pub struct Authenticated {
value: AuthenticationInfo,
}

impl Authenticated {
pub fn new(auth_info: AuthenticationInfo) -> Self {
Authenticated { value: auth_info }
}
}

impl Deref for Authenticated {
type Target = AuthenticationInfo;

fn deref(&self) -> &Self::Target {
&self.value
}
}

impl FromRequest for Authenticated {
type Error = actix_web::Error;
type Future = Pin<Box<dyn Future<Output = Result<Self, Self::Error>> + Send>>;

fn from_request(req: &actix_web::HttpRequest, _: &mut actix_web::dev::Payload) -> Self::Future {
let auth_info = req.extensions().get::<AuthenticationInfo>().cloned();

return Box::pin(async {
match auth_info {
Some(auth_info) => Ok(Authenticated::new(auth_info)),
None => {
Err(ErrorUnauthorized(ResponseDTO::new("Unauthorized").message("Authentication required for this resource")))
}
}
});
}
}
44 changes: 39 additions & 5 deletions src/auth/handlers.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,31 @@
use std::collections::HashMap;

use super::extractors::Authenticated;
use super::middlewares::require_auth_middleware::RequireAuthMiddlewareInitializer;
use super::models::JsonTokenClaims;
use crate::auth::dto::*;
use crate::auth::repositories::Repository;
use crate::models::dto_models::ResponseDTO;
use crate::models::user_models::FilteredUser;
use crate::states::db_state::DBState;
use crate::{auth::utils::JwtUtils, models::user_models::User};
use actix_web::{post, web, HttpResponse, Responder};
use actix_web::{get, post, web, HttpResponse, Responder};
use argon2::password_hash::rand_core::OsRng;
use argon2::password_hash::SaltString;
use argon2::{Argon2, PasswordHash, PasswordHasher, PasswordVerifier};
use chrono::{Duration, Utc};
use crate::auth::dto::*;

pub fn auth_config(cfg: &mut web::ServiceConfig) {
cfg.service(web::scope("api/auth").service(login).service(register));
cfg.service(
web::scope("api/auth")
.service(login)
.service(register)
.service(
web::scope("")
.wrap(RequireAuthMiddlewareInitializer)
.service(user_route),
),
);
}

#[post("login")]
Expand Down Expand Up @@ -64,7 +76,10 @@ pub async fn login(data: web::Json<LoginDTO>, db_state: web::Data<DBState>) -> i
} //end function login

#[post("register")]
pub async fn register(data: web::Json<RegisterDTO>, db_state: web::Data<DBState>) -> impl Responder {
pub async fn register(
data: web::Json<RegisterDTO>,
db_state: web::Data<DBState>,
) -> impl Responder {
let RegisterDTO {
email,
password,
Expand Down Expand Up @@ -97,4 +112,23 @@ pub async fn register(data: web::Json<RegisterDTO>, db_state: web::Data<DBState>
ResponseDTO::new(HashMap::from([("id", user.id)])).message("User created successfully"),
),
}
}
} //end function register

#[get("user")]
pub async fn user_route(auth: Authenticated, db_state: web::Data<DBState>) -> impl Responder {
let user: Option<User> = Repository::<User>::get_by_email(&db_state.pool, &auth.email).await;

match user {
Some(user) => {
let filtered_user: Result<FilteredUser, _> = user.try_into();
match filtered_user {
Ok(filtered_user) => HttpResponse::Ok()
.json(ResponseDTO::new(filtered_user).message("Authenticated user")),
Err(e) => HttpResponse::InternalServerError()
.json(ResponseDTO::new(e.to_string()).message("Unable to build user response")),
}
}
None => HttpResponse::NotFound()
.json(ResponseDTO::new("Not found").message("User record not found")),
}
} // end user function
8 changes: 0 additions & 8 deletions src/auth/middlewares/auth_middleware.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,14 +68,8 @@ where
.get(http::header::AUTHORIZATION)
.map(|h| {
let authorization_header = h.to_str().expect("Unable to convert to str");
log::error!(
"Authorization header({}): {}",
authorization_header.len(),
authorization_header
);
if authorization_header.len() > 7 {
let (_, token) = authorization_header.split_at(7);
log::error!("Authorization token: {}", token);
token.to_string()
} else {
"".to_string()
Expand All @@ -98,13 +92,11 @@ where
.await;

if let Ok(record) = record {
log::error!("Record: {:?}", record);
let auth_info = AuthenticationInfo {
id: record.id as u32,
email: record.email,
name: record.name,
};
log::error!("Auth info: {:?}", auth_info);
req.request()
.extensions_mut()
.insert::<AuthenticationInfo>(auth_info);
Expand Down
3 changes: 2 additions & 1 deletion src/auth/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ pub mod models;
pub mod handlers;
pub mod utils;
pub mod repositories;
pub mod middlewares;
pub mod middlewares;
pub mod extractors;
5 changes: 2 additions & 3 deletions src/handlers/mod.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
use actix_web::{Responder, HttpResponse};
use actix_web::{HttpResponse, Responder};

use crate::models::dto_models::ResponseDTO;

pub mod healthcheck;

pub async fn authenticated_route() -> impl Responder {
log::error!("Authenticated route");
return HttpResponse::Ok().json(ResponseDTO::new("This is the authenticated route"));
}
}
7 changes: 7 additions & 0 deletions src/models/dto_models.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use serde::{Deserialize, Serialize};
use core::fmt;
use std::ops::Deref;

#[derive(Debug, Deserialize, Serialize)]
Expand All @@ -7,6 +8,12 @@ pub struct ResponseDTO<T> {
data: T,
}

impl<T: serde::Serialize> fmt::Display for ResponseDTO<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", serde_json::to_string(&self).unwrap())
}
}

impl<T> ResponseDTO<T> {
pub fn new(data: T) -> Self {
return ResponseDTO {
Expand Down
10 changes: 9 additions & 1 deletion src/models/user_models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,19 @@ pub struct User {
pub updated_at: DateTime<Utc>,
}

#[derive(Debug)]
#[derive(Debug, serde::Serialize)]
pub struct FilteredUser {
pub id: u32,
pub name: String,
pub email: String,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}

impl TryFrom<User> for FilteredUser {
type Error = &'static str;

fn try_from(user: User) -> Result<Self, Self::Error> {
Ok(FilteredUser { id: user.id, name: user.name, email: user.email, created_at: user.created_at, updated_at: user.updated_at })
}
}

0 comments on commit b5bf10e

Please sign in to comment.