From 4b59574392289cfa0ee44b1c41e0b893693411e4 Mon Sep 17 00:00:00 2001 From: Zack Fu Zi Xiang Date: Fri, 13 Sep 2024 21:02:09 +0800 Subject: [PATCH] chore: add specific error code for wrong invitee --- libs/app-error/src/lib.rs | 5 +++++ libs/database/src/workspace.rs | 21 +++++++++++++++++++++ src/api/workspace.rs | 2 ++ src/biz/workspace/ops.rs | 19 ++++++++++++++++++- tests/workspace/invitation_crud.rs | 13 +++++++++++++ 5 files changed, 59 insertions(+), 1 deletion(-) diff --git a/libs/app-error/src/lib.rs b/libs/app-error/src/lib.rs index 69f1f397..5f911fda 100644 --- a/libs/app-error/src/lib.rs +++ b/libs/app-error/src/lib.rs @@ -142,6 +142,9 @@ pub enum AppError { #[error("{0}")] InvalidFolderView(String), + + #[error("{0}")] + NotInviteeOfWorkspaceInvitation(String), } impl AppError { @@ -208,6 +211,7 @@ impl AppError { AppError::InvalidContentType(_) => ErrorCode::InvalidContentType, AppError::InvalidPublishedOutline(_) => ErrorCode::InvalidPublishedOutline, AppError::InvalidFolderView(_) => ErrorCode::InvalidFolderView, + AppError::NotInviteeOfWorkspaceInvitation(_) => ErrorCode::NotInviteeOfWorkspaceInvitation, } } } @@ -328,6 +332,7 @@ pub enum ErrorCode { AppleRevokeTokenError = 1038, InvalidPublishedOutline = 1039, InvalidFolderView = 1040, + NotInviteeOfWorkspaceInvitation = 1041, } impl ErrorCode { diff --git a/libs/database/src/workspace.rs b/libs/database/src/workspace.rs index 354ac035..2c90b108 100644 --- a/libs/database/src/workspace.rs +++ b/libs/database/src/workspace.rs @@ -1376,3 +1376,24 @@ pub async fn delete_reaction_from_comment<'a, E: Executor<'a, Database = Postgre Ok(()) } + +pub async fn select_user_is_invitee_for_workspace_invitation( + pg_pool: &PgPool, + invitee_uuid: &Uuid, + invite_id: &Uuid, +) -> Result { + let res = sqlx::query_scalar!( + r#" + SELECT EXISTS( + SELECT 1 + FROM af_workspace_invitation + WHERE id = $1 AND invitee_email = (SELECT email FROM af_user WHERE uuid = $2) + ) + "#, + invite_id, + invitee_uuid, + ) + .fetch_one(pg_pool) + .await?; + res.map_or(Ok(false), Ok) +} diff --git a/src/api/workspace.rs b/src/api/workspace.rs index a4bd91ce..281a4056 100644 --- a/src/api/workspace.rs +++ b/src/api/workspace.rs @@ -340,12 +340,14 @@ async fn post_accept_workspace_invite_handler( ) -> Result> { let _is_new = verify_token(&auth.token, state.as_ref()).await?; let user_uuid = auth.uuid()?; + let user_uid = state.user_cache.get_user_uid(&user_uuid).await?; let invite_id = invite_id.into_inner(); // TODO(zack): insert a workspace member in the af_workspace_member by calling workspace::ops::add_workspace_members. // Currently, when the server get restarted, the policy in access control will be lost. workspace::ops::accept_workspace_invite( &state.pg_pool, &state.workspace_access_control, + user_uid, &user_uuid, &invite_id, ) diff --git a/src/biz/workspace/ops.rs b/src/biz/workspace/ops.rs index 5a4d8162..b82f7cf6 100644 --- a/src/biz/workspace/ops.rs +++ b/src/biz/workspace/ops.rs @@ -302,12 +302,21 @@ pub async fn open_workspace( pub async fn accept_workspace_invite( pg_pool: &PgPool, workspace_access_control: &impl WorkspaceAccessControl, + user_uid: i64, user_uuid: &Uuid, invite_id: &Uuid, ) -> Result<(), AppError> { let mut txn = pg_pool.begin().await?; - update_workspace_invitation_set_status_accepted(&mut txn, user_uuid, invite_id).await?; let inv = get_invitation_by_id(&mut txn, invite_id).await?; + if let Some(invitee_uid) = inv.invitee_uid { + if invitee_uid != user_uid { + return Err(AppError::NotInviteeOfWorkspaceInvitation(format!( + "User with uid {} is not the invitee for invite_id {}", + user_uid, invite_id + ))); + } + } + update_workspace_invitation_set_status_accepted(&mut txn, user_uuid, invite_id).await?; let invited_uid = inv .invitee_uid .ok_or_else(|| AppError::Internal(anyhow::anyhow!("Invitee uid is missing for {:?}", inv)))?; @@ -469,6 +478,14 @@ pub async fn get_workspace_invitations_for_user( user_uuid: &Uuid, invite_id: &Uuid, ) -> Result { + let user_is_invitee = + select_user_is_invitee_for_workspace_invitation(pg_pool, user_uuid, invite_id).await?; + if !user_is_invitee { + return Err(AppError::NotInviteeOfWorkspaceInvitation(format!( + "User with uuid {} is not the invitee for invite_id {}", + user_uuid, invite_id + ))); + } let invitation = select_workspace_invitation_for_user(pg_pool, user_uuid, invite_id).await?; Ok(invitation) } diff --git a/tests/workspace/invitation_crud.rs b/tests/workspace/invitation_crud.rs index 1a59f2f8..b258c47c 100644 --- a/tests/workspace/invitation_crud.rs +++ b/tests/workspace/invitation_crud.rs @@ -1,3 +1,4 @@ +use app_error::ErrorCode; use client_api_test::generate_unique_registered_user_client; use database_entity::dto::{AFRole, AFWorkspaceInvitationStatus}; use shared_entity::dto::workspace_dto::{QueryWorkspaceParam, WorkspaceMemberInvitation}; @@ -61,6 +62,18 @@ async fn invite_workspace_crud() { assert_eq!(invitation.status, AFWorkspaceInvitationStatus::Pending); assert_eq!(invitation.member_count.unwrap_or(0), 1); + let (charlie_client, _charlie) = generate_unique_registered_user_client().await; + let err = charlie_client + .get_workspace_invitation(&invite_id) + .await + .unwrap_err(); + assert_eq!(err.code, ErrorCode::NotInviteeOfWorkspaceInvitation); + let err = charlie_client + .accept_workspace_invitation(&invite_id) + .await + .unwrap_err(); + assert_eq!(err.code, ErrorCode::NotInviteeOfWorkspaceInvitation); + bob_client .accept_workspace_invitation(&invite_id) .await