From 03d8054cc17fe44da8b3007383bb1fd41ea1375b Mon Sep 17 00:00:00 2001 From: SquidSpirit Date: Wed, 6 Aug 2025 21:57:22 +0800 Subject: [PATCH] BLOG-119 feat: enhance post handling with user authorization checks and update method signatures --- .../src/adapter/delivery/post_controller.rs | 32 +++++++++++++++---- .../post/src/application/error/post_error.rs | 2 ++ .../use_case/get_full_post_use_case.rs | 12 +++++-- .../src/framework/web/create_post_handler.rs | 6 ++-- .../framework/web/get_post_by_id_handler.rs | 7 +++- .../src/framework/web/update_label_handler.rs | 1 + .../src/framework/web/update_post_handler.rs | 7 ++-- 7 files changed, 52 insertions(+), 15 deletions(-) diff --git a/backend/feature/post/src/adapter/delivery/post_controller.rs b/backend/feature/post/src/adapter/delivery/post_controller.rs index 736d765..31cddba 100644 --- a/backend/feature/post/src/adapter/delivery/post_controller.rs +++ b/backend/feature/post/src/adapter/delivery/post_controller.rs @@ -33,14 +33,23 @@ pub trait PostController: Send + Sync { query: PostQueryDto, ) -> Result, PostError>; - async fn get_post_by_id(&self, id: i32) -> Result; + async fn get_post_by_id( + &self, + id: i32, + user_id: Option, + ) -> Result; - async fn create_post(&self, post: CreatePostRequestDto) -> Result; + async fn create_post( + &self, + post: CreatePostRequestDto, + user_id: i32, + ) -> Result; async fn update_post( &self, id: i32, post: UpdatePostRequestDto, + user_id: i32, ) -> Result; async fn create_label( @@ -110,8 +119,12 @@ impl PostController for PostControllerImpl { }) } - async fn get_post_by_id(&self, id: i32) -> Result { - let result = self.get_full_post_use_case.execute(id).await; + async fn get_post_by_id( + &self, + id: i32, + user_id: Option, + ) -> Result { + let result = self.get_full_post_use_case.execute(id, user_id).await; result.map(PostResponseDto::from) } @@ -154,7 +167,11 @@ impl PostController for PostControllerImpl { }) } - async fn create_post(&self, post: CreatePostRequestDto) -> Result { + async fn create_post( + &self, + post: CreatePostRequestDto, + user_id: i32, + ) -> Result { let label_ids = post.label_ids.clone(); let post_entity = post.into_entity(); @@ -163,13 +180,14 @@ impl PostController for PostControllerImpl { .execute(post_entity, &label_ids) .await?; - self.get_post_by_id(id).await + self.get_post_by_id(id, Some(user_id)).await } async fn update_post( &self, id: i32, post: UpdatePostRequestDto, + user_id: i32, ) -> Result { let label_ids = post.label_ids.clone(); let post_entity = post.into_entity(id); @@ -178,6 +196,6 @@ impl PostController for PostControllerImpl { .execute(post_entity, &label_ids) .await?; - self.get_post_by_id(id).await + self.get_post_by_id(id, Some(user_id)).await } } diff --git a/backend/feature/post/src/application/error/post_error.rs b/backend/feature/post/src/application/error/post_error.rs index f917ee4..58ce49a 100644 --- a/backend/feature/post/src/application/error/post_error.rs +++ b/backend/feature/post/src/application/error/post_error.rs @@ -3,6 +3,7 @@ use std::fmt::Display; #[derive(Debug)] pub enum PostError { NotFound, + Unauthorized, Unexpected(anyhow::Error), } @@ -10,6 +11,7 @@ impl Display for PostError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { PostError::NotFound => write!(f, "Post not found"), + PostError::Unauthorized => write!(f, "Unauthorized access to post"), PostError::Unexpected(e) => write!(f, "Unexpected error: {}", e), } } diff --git a/backend/feature/post/src/application/use_case/get_full_post_use_case.rs b/backend/feature/post/src/application/use_case/get_full_post_use_case.rs index 9fe345f..1a161a6 100644 --- a/backend/feature/post/src/application/use_case/get_full_post_use_case.rs +++ b/backend/feature/post/src/application/use_case/get_full_post_use_case.rs @@ -9,7 +9,7 @@ use crate::{ #[async_trait] pub trait GetFullPostUseCase: Send + Sync { - async fn execute(&self, id: i32) -> Result; + async fn execute(&self, id: i32, user_id: Option) -> Result; } pub struct GetFullPostUseCaseImpl { @@ -24,7 +24,13 @@ impl GetFullPostUseCaseImpl { #[async_trait] impl GetFullPostUseCase for GetFullPostUseCaseImpl { - async fn execute(&self, id: i32) -> Result { - self.post_repository.get_post_by_id(id).await + async fn execute(&self, id: i32, user_id: Option) -> Result { + let post = self.post_repository.get_post_by_id(id).await?; + + if post.info.published_time.is_none() && user_id.is_none() { + return Err(PostError::Unauthorized); + } + + Ok(post) } } diff --git a/backend/feature/post/src/framework/web/create_post_handler.rs b/backend/feature/post/src/framework/web/create_post_handler.rs index a5c84c5..8de8df3 100644 --- a/backend/feature/post/src/framework/web/create_post_handler.rs +++ b/backend/feature/post/src/framework/web/create_post_handler.rs @@ -26,9 +26,11 @@ use crate::{ pub async fn create_post_handler( post_controller: web::Data, post_dto: web::Json, - _: UserId, + user_id: UserId, ) -> impl Responder { - let result = post_controller.create_post(post_dto.into_inner()).await; + let result = post_controller + .create_post(post_dto.into_inner(), user_id.get()) + .await; match result { Ok(post) => HttpResponse::Created().json(post), diff --git a/backend/feature/post/src/framework/web/get_post_by_id_handler.rs b/backend/feature/post/src/framework/web/get_post_by_id_handler.rs index 2057c5f..fd4a378 100644 --- a/backend/feature/post/src/framework/web/get_post_by_id_handler.rs +++ b/backend/feature/post/src/framework/web/get_post_by_id_handler.rs @@ -1,4 +1,5 @@ use actix_web::{HttpResponse, Responder, web}; +use auth::framework::web::auth_middleware::UserId; use sentry::integrations::anyhow::capture_anyhow; use crate::{ @@ -19,14 +20,18 @@ use crate::{ pub async fn get_post_by_id_handler( post_controller: web::Data, path: web::Path, + user_id: Option, ) -> impl Responder { let id = path.into_inner(); - let result = post_controller.get_post_by_id(id).await; + let result = post_controller + .get_post_by_id(id, user_id.map(|id| id.get())) + .await; match result { Ok(post) => HttpResponse::Ok().json(post), Err(e) => match e { PostError::NotFound => HttpResponse::NotFound().finish(), + PostError::Unauthorized => HttpResponse::Unauthorized().finish(), PostError::Unexpected(e) => { capture_anyhow(&e); HttpResponse::InternalServerError().finish() diff --git a/backend/feature/post/src/framework/web/update_label_handler.rs b/backend/feature/post/src/framework/web/update_label_handler.rs index 5c2b32f..434903a 100644 --- a/backend/feature/post/src/framework/web/update_label_handler.rs +++ b/backend/feature/post/src/framework/web/update_label_handler.rs @@ -38,6 +38,7 @@ pub async fn update_label_handler( Ok(label) => HttpResponse::Ok().json(label), Err(e) => match e { PostError::NotFound => HttpResponse::NotFound().finish(), + PostError::Unauthorized => HttpResponse::Unauthorized().finish(), PostError::Unexpected(e) => { capture_anyhow(&e); HttpResponse::InternalServerError().finish() diff --git a/backend/feature/post/src/framework/web/update_post_handler.rs b/backend/feature/post/src/framework/web/update_post_handler.rs index 167b005..d3a16e2 100644 --- a/backend/feature/post/src/framework/web/update_post_handler.rs +++ b/backend/feature/post/src/framework/web/update_post_handler.rs @@ -27,15 +27,18 @@ pub async fn update_post_handler( post_controller: web::Data, path: web::Path, post_dto: web::Json, - _: UserId, + user_id: UserId, ) -> impl Responder { let id = path.into_inner(); - let result = post_controller.update_post(id, post_dto.into_inner()).await; + let result = post_controller + .update_post(id, post_dto.into_inner(), user_id.get()) + .await; match result { Ok(post) => HttpResponse::Ok().json(post), Err(e) => match e { PostError::NotFound => HttpResponse::NotFound().finish(), + PostError::Unauthorized => HttpResponse::Unauthorized().finish(), PostError::Unexpected(e) => { capture_anyhow(&e); HttpResponse::InternalServerError().finish()