diff --git a/backend/feature/post/src/adapter/delivery/post_controller.rs b/backend/feature/post/src/adapter/delivery/post_controller.rs index 736d765..ca2ef5b 100644 --- a/backend/feature/post/src/adapter/delivery/post_controller.rs +++ b/backend/feature/post/src/adapter/delivery/post_controller.rs @@ -31,16 +31,26 @@ pub trait PostController: Send + Sync { async fn get_all_post_info( &self, query: PostQueryDto, + user_id: Option, ) -> 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( @@ -94,10 +104,11 @@ impl PostController for PostControllerImpl { async fn get_all_post_info( &self, query: PostQueryDto, + user_id: Option, ) -> Result, PostError> { let result = self .get_all_post_info_use_case - .execute(query.is_published_only.unwrap_or(true)) + .execute(query.is_published_only.unwrap_or(true), user_id) .await; result.map(|post_info_list| { @@ -110,8 +121,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 +169,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 +182,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 +198,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_all_post_info_use_case.rs b/backend/feature/post/src/application/use_case/get_all_post_info_use_case.rs index f2895d5..3a7eefa 100644 --- a/backend/feature/post/src/application/use_case/get_all_post_info_use_case.rs +++ b/backend/feature/post/src/application/use_case/get_all_post_info_use_case.rs @@ -9,7 +9,11 @@ use crate::{ #[async_trait] pub trait GetAllPostInfoUseCase: Send + Sync { - async fn execute(&self, is_published_only: bool) -> Result, PostError>; + async fn execute( + &self, + is_published_only: bool, + user_id: Option, + ) -> Result, PostError>; } pub struct GetAllPostInfoUseCaseImpl { @@ -24,7 +28,15 @@ impl GetAllPostInfoUseCaseImpl { #[async_trait] impl GetAllPostInfoUseCase for GetAllPostInfoUseCaseImpl { - async fn execute(&self, is_published_only: bool) -> Result, PostError> { - self.post_repository.get_all_post_info(is_published_only).await + async fn execute( + &self, + is_published_only: bool, + user_id: Option, + ) -> Result, PostError> { + let is_published_only = is_published_only && user_id.is_some(); + + self.post_repository + .get_all_post_info(is_published_only) + .await } } 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_all_post_info_handler.rs b/backend/feature/post/src/framework/web/get_all_post_info_handler.rs index 2f3dd19..3d4ee41 100644 --- a/backend/feature/post/src/framework/web/get_all_post_info_handler.rs +++ b/backend/feature/post/src/framework/web/get_all_post_info_handler.rs @@ -1,5 +1,6 @@ use actix_web::{HttpResponse, Responder, web}; use anyhow::anyhow; +use auth::framework::web::auth_middleware::UserId; use sentry::integrations::anyhow::capture_anyhow; use crate::{ @@ -15,6 +16,7 @@ use crate::{ path = "/post", tag = "post", summary = "Get all post information", + description = "`is_published_only` query is only available for authenticated users.", params( PostQueryDto ), @@ -25,8 +27,11 @@ use crate::{ pub async fn get_all_post_info_handler( post_controller: web::Data, query: web::Query, + user_id: Option, ) -> impl Responder { - let result = post_controller.get_all_post_info(query.into_inner()).await; + let result = post_controller + .get_all_post_info(query.into_inner(), user_id.map(|id| id.get())) + .await; match result { Ok(post_info_list) => HttpResponse::Ok().json(post_info_list), 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..16ca48c 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::{ @@ -11,6 +12,7 @@ use crate::{ path = "/post/{id}", tag = "post", summary = "Get post by ID", + description = "Only authenticated users can access unpublished posts.", responses ( (status = 200, body = PostResponseDto), (status = 404, description = "Post not found") @@ -19,14 +21,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()