BLOG-100 User retrieval functionality in authentication module #102

Merged
squid merged 1 commits from BLOG-100_get_current_logged_in_user into main 2025-08-01 18:42:22 +08:00
9 changed files with 95 additions and 6 deletions

View File

@ -11,6 +11,7 @@ use crate::{
use_case::{ use_case::{
exchange_auth_code_use_case::ExchangeAuthCodeUseCase, exchange_auth_code_use_case::ExchangeAuthCodeUseCase,
get_auth_url_use_case::{AuthUrl, GetAuthUrlUseCase}, get_auth_url_use_case::{AuthUrl, GetAuthUrlUseCase},
get_user_use_case::GetUserUseCase,
}, },
}, },
}; };
@ -25,21 +26,26 @@ pub trait AuthController: Send + Sync {
expected_state: &str, expected_state: &str,
expected_nonce: &str, expected_nonce: &str,
) -> Result<UserResponseDto, AuthError>; ) -> Result<UserResponseDto, AuthError>;
async fn get_user(&self, user_id: i32) -> Result<UserResponseDto, AuthError>;
} }
pub struct AuthControllerImpl { pub struct AuthControllerImpl {
get_auth_url_use_case: Arc<dyn GetAuthUrlUseCase>, get_auth_url_use_case: Arc<dyn GetAuthUrlUseCase>,
exchange_auth_code_use_case: Arc<dyn ExchangeAuthCodeUseCase>, exchange_auth_code_use_case: Arc<dyn ExchangeAuthCodeUseCase>,
get_user_use_case: Arc<dyn GetUserUseCase>,
} }
impl AuthControllerImpl { impl AuthControllerImpl {
pub fn new( pub fn new(
get_auth_url_use_case: Arc<dyn GetAuthUrlUseCase>, get_auth_url_use_case: Arc<dyn GetAuthUrlUseCase>,
exchange_auth_code_use_case: Arc<dyn ExchangeAuthCodeUseCase>, exchange_auth_code_use_case: Arc<dyn ExchangeAuthCodeUseCase>,
get_user_use_case: Arc<dyn GetUserUseCase>,
) -> Self { ) -> Self {
Self { Self {
get_auth_url_use_case, get_auth_url_use_case,
exchange_auth_code_use_case, exchange_auth_code_use_case,
get_user_use_case,
} }
} }
} }
@ -63,4 +69,9 @@ impl AuthController for AuthControllerImpl {
result.map(|user| UserResponseDto::from(user)) result.map(|user| UserResponseDto::from(user))
} }
async fn get_user(&self, user_id: i32) -> Result<UserResponseDto, AuthError> {
let user = self.get_user_use_case.execute(user_id).await?;
Ok(UserResponseDto::from(user))
}
} }

View File

@ -4,7 +4,8 @@ use async_trait::async_trait;
use crate::{ use crate::{
adapter::gateway::{ adapter::gateway::{
auth_oidc_service::AuthOidcService, user_db_service::UserDbService, user_db_mapper::UserMapper, auth_oidc_service::AuthOidcService, user_db_mapper::UserMapper,
user_db_service::UserDbService,
}, },
application::{ application::{
error::auth_error::AuthError, gateway::auth_repository::AuthRepository, error::auth_error::AuthError, gateway::auth_repository::AuthRepository,
@ -47,6 +48,13 @@ impl AuthRepository for AuthRepositoryImpl {
.map(|dto| dto.into_entity()) .map(|dto| dto.into_entity())
} }
async fn get_user_by_id(&self, user_id: i32) -> Result<User, AuthError> {
self.user_db_service
.get_user_by_id(user_id)
.await
.map(|mapper| mapper.into_entity())
}
async fn get_user_by_source_id( async fn get_user_by_source_id(
&self, &self,
issuer: &str, issuer: &str,

View File

@ -1,9 +1,13 @@
use async_trait::async_trait; use async_trait::async_trait;
use crate::{adapter::gateway::user_db_mapper::UserMapper, application::error::auth_error::AuthError}; use crate::{
adapter::gateway::user_db_mapper::UserMapper, application::error::auth_error::AuthError,
};
#[async_trait] #[async_trait]
pub trait UserDbService: Send + Sync { pub trait UserDbService: Send + Sync {
async fn get_user_by_id(&self, user_id: i32) -> Result<UserMapper, AuthError>;
async fn get_user_by_source_id( async fn get_user_by_source_id(
&self, &self,
issuer: &str, issuer: &str,

View File

@ -12,6 +12,8 @@ pub trait AuthRepository: Send + Sync {
async fn exchange_auth_code(&self, code: &str, expected_nonce: &str) async fn exchange_auth_code(&self, code: &str, expected_nonce: &str)
-> Result<User, AuthError>; -> Result<User, AuthError>;
async fn get_user_by_id(&self, user_id: i32) -> Result<User, AuthError>;
async fn get_user_by_source_id(&self, issuer: &str, source_id: &str) async fn get_user_by_source_id(&self, issuer: &str, source_id: &str)
-> Result<User, AuthError>; -> Result<User, AuthError>;

View File

@ -1,2 +1,3 @@
pub mod exchange_auth_code_use_case; pub mod exchange_auth_code_use_case;
pub mod get_auth_url_use_case; pub mod get_auth_url_use_case;
pub mod get_user_use_case;

View File

@ -0,0 +1,30 @@
use std::sync::Arc;
use async_trait::async_trait;
use crate::{
application::{error::auth_error::AuthError, gateway::auth_repository::AuthRepository},
domain::entity::user::User,
};
#[async_trait]
pub trait GetUserUseCase: Send + Sync {
async fn execute(&self, user_id: i32) -> Result<User, AuthError>;
}
pub struct GetUserUseCaseImpl {
auth_repository: Arc<dyn AuthRepository>,
}
impl GetUserUseCaseImpl {
pub fn new(auth_repository: Arc<dyn AuthRepository>) -> Self {
Self { auth_repository }
}
}
#[async_trait]
impl GetUserUseCase for GetUserUseCaseImpl {
async fn execute(&self, user_id: i32) -> Result<User, AuthError> {
self.auth_repository.get_user_by_id(user_id).await
}
}

View File

@ -2,7 +2,7 @@ use async_trait::async_trait;
use sqlx::{Pool, Postgres}; use sqlx::{Pool, Postgres};
use crate::{ use crate::{
adapter::gateway::{user_db_service::UserDbService, user_db_mapper::UserMapper}, adapter::gateway::{user_db_mapper::UserMapper, user_db_service::UserDbService},
application::error::auth_error::AuthError, application::error::auth_error::AuthError,
framework::db::user_record::UserRecord, framework::db::user_record::UserRecord,
}; };
@ -19,6 +19,26 @@ impl UserDbServiceImpl {
#[async_trait] #[async_trait]
impl UserDbService for UserDbServiceImpl { impl UserDbService for UserDbServiceImpl {
async fn get_user_by_id(&self, user_id: i32) -> Result<UserMapper, AuthError> {
let record = sqlx::query_as!(
UserRecord,
r#"
SELECT id, issuer, source_id, displayed_name, email
FROM "user"
WHERE id = $1
"#,
user_id
)
.fetch_optional(&self.db_pool)
.await
.map_err(|e| AuthError::DatabaseError(e.to_string()))?;
match record {
Some(record) => Ok(record.into_mapper()),
None => Err(AuthError::UserNotFound),
}
}
async fn get_user_by_source_id( async fn get_user_by_source_id(
&self, &self,
issuer: &str, issuer: &str,

View File

@ -101,6 +101,17 @@ async fn logout_handler(session: Session) -> impl Responder {
.finish() .finish()
} }
async fn get_logged_in_user_handler(user_id: UserId) -> impl Responder { async fn get_logged_in_user_handler(
HttpResponse::Ok().body(format!("Logged in user ID: {}", user_id.get())) auth_controller: web::Data<dyn AuthController>,
user_id: UserId,
) -> impl Responder {
let result = auth_controller.get_user(user_id.get()).await;
match result {
Ok(user) => HttpResponse::Ok().json(user),
Err(e) => {
log::error!("{e:?}");
HttpResponse::InternalServerError().finish()
}
}
} }

View File

@ -7,7 +7,7 @@ use auth::{
}, },
application::use_case::{ application::use_case::{
exchange_auth_code_use_case::ExchangeAuthCodeUseCaseImpl, exchange_auth_code_use_case::ExchangeAuthCodeUseCaseImpl,
get_auth_url_use_case::LoginUseCaseImpl, get_auth_url_use_case::LoginUseCaseImpl, get_user_use_case::GetUserUseCaseImpl,
}, },
framework::{ framework::{
db::user_db_service_impl::UserDbServiceImpl, db::user_db_service_impl::UserDbServiceImpl,
@ -68,9 +68,11 @@ impl Container {
let get_auth_url_use_case = Arc::new(LoginUseCaseImpl::new(auth_repository.clone())); let get_auth_url_use_case = Arc::new(LoginUseCaseImpl::new(auth_repository.clone()));
let exchange_auth_code_use_case = let exchange_auth_code_use_case =
Arc::new(ExchangeAuthCodeUseCaseImpl::new(auth_repository.clone())); Arc::new(ExchangeAuthCodeUseCaseImpl::new(auth_repository.clone()));
let get_user_use_case = Arc::new(GetUserUseCaseImpl::new(auth_repository.clone()));
let auth_controller = Arc::new(AuthControllerImpl::new( let auth_controller = Arc::new(AuthControllerImpl::new(
get_auth_url_use_case, get_auth_url_use_case,
exchange_auth_code_use_case, exchange_auth_code_use_case,
get_user_use_case,
)); ));
let post_db_service = Arc::new(PostDbServiceImpl::new(db_pool.clone())); let post_db_service = Arc::new(PostDbServiceImpl::new(db_pool.clone()));