diff --git a/libs/client-api-test-util/src/test_client.rs b/libs/client-api-test-util/src/test_client.rs index 90fd95f4..258179a4 100644 --- a/libs/client-api-test-util/src/test_client.rs +++ b/libs/client-api-test-util/src/test_client.rs @@ -15,9 +15,9 @@ use collab_entity::CollabType; use collab_folder::Folder; use database_entity::dto::{ AFAccessLevel, AFRole, AFSnapshotMeta, AFSnapshotMetas, AFUserWorkspaceInfo, AFWorkspace, - AFWorkspaceMember, BatchQueryCollabResult, CollabParams, CreateCollabParams, - InsertCollabMemberParams, QueryCollab, QueryCollabParams, QuerySnapshotParams, SnapshotData, - UpdateCollabMemberParams, + AFWorkspaceInvitationStatus, AFWorkspaceMember, BatchQueryCollabResult, CollabParams, + CreateCollabParams, InsertCollabMemberParams, QueryCollab, QueryCollabParams, + QuerySnapshotParams, SnapshotData, UpdateCollabMemberParams, }; use mime::Mime; use serde_json::Value; @@ -121,7 +121,7 @@ impl TestClient { role: AFRole, ) { self - .try_add_workspace_member(workspace_id, other_client, role) + .invite_and_accepted_workspace_member(workspace_id, other_client, role) .await .unwrap(); } @@ -174,13 +174,19 @@ impl TestClient { .await } - pub async fn try_add_workspace_member( + pub async fn invite_and_accepted_workspace_member( &self, workspace_id: &str, other_client: &TestClient, role: AFRole, ) -> Result<(), AppResponseError> { let email = other_client.email().await; + + // self + // .api_client + // .add_workspace_members(workspace_id, vec![CreateWorkspaceMember { email, role }]) + // .await + self .api_client .invite_workspace_members( @@ -189,17 +195,21 @@ impl TestClient { ) .await?; - todo!() - // let invis = other_client.api_client.list_workspace_invitations().await?; - // let invis = invis - // .iter() - // .filter(|inv| inv.email == email) - // .collect::>(); + let invitations = other_client + .api_client + .list_workspace_invitations(Some(AFWorkspaceInvitationStatus::Pending)) + .await + .unwrap(); - // self - // .api_client - // .add_workspace_members(workspace_id, vec![CreateWorkspaceMember { email, role }]) - // .await + let target_invitation = invitations + .iter() + .find(|inv| inv.workspace_id.to_string().as_str() == workspace_id) + .unwrap(); + + other_client + .api_client + .accept_workspace_invitation(&target_invitation.invite_id.to_string().as_str()) + .await } pub async fn try_remove_workspace_member( diff --git a/libs/database/src/workspace.rs b/libs/database/src/workspace.rs index 6d819efa..1af6e715 100644 --- a/libs/database/src/workspace.rs +++ b/libs/database/src/workspace.rs @@ -271,12 +271,9 @@ pub async fn insert_workspace_invitation( pub async fn update_workspace_invitation_set_invited( txn: &mut Transaction<'_, sqlx::Postgres>, - user_uuid: &Uuid, + invitee_uuid: &Uuid, invite_id: &Uuid, ) -> Result<(), AppError> { - println!("--------- user_uuid: {:?}", user_uuid); - println!("--------- invite_id: {:?}", invite_id); - let res = sqlx::query_scalar!( r#" UPDATE public.af_workspace_invitation @@ -284,7 +281,7 @@ pub async fn update_workspace_invitation_set_invited( WHERE invitee = (SELECT uid FROM public.af_user WHERE uuid = $1) AND id = $2 "#, - user_uuid, + invitee_uuid, invite_id, ) .execute(txn.deref_mut()) diff --git a/migrations/20240302000000_workspace_invitation.sql b/migrations/20240302000000_workspace_invitation.sql index 01545b37..998cee13 100644 --- a/migrations/20240302000000_workspace_invitation.sql +++ b/migrations/20240302000000_workspace_invitation.sql @@ -34,9 +34,23 @@ CREATE OR REPLACE FUNCTION add_to_af_workspace_member() RETURNS TRIGGER AS $$ BEGIN IF NEW.status = 1 THEN + -- workspace permission INSERT INTO af_workspace_member (workspace_id, uid, role_id) VALUES (NEW.workspace_id, NEW.invitee, NEW.role_id) ON CONFLICT (workspace_id, uid) DO NOTHING; + + -- collab permission + INSERT INTO af_collab_member (uid, oid, permission_id) + VALUES ( + NEW.invitee, + NEW.workspace_id, + (SELECT permission_id + FROM public.af_role_permissions + WHERE public.af_role_permissions.role_id = NEW.role_id) + ) + ON CONFLICT (uid, oid) + DO UPDATE + SET permission_id = excluded.permission_id; END IF; RETURN NEW; END; diff --git a/src/api/workspace.rs b/src/api/workspace.rs index a21d7932..92ed1c0a 100644 --- a/src/api/workspace.rs +++ b/src/api/workspace.rs @@ -72,7 +72,7 @@ pub fn workspace_scope() -> Scope { .service( web::resource("/{workspace_id}/member") .route(web::get().to(get_workspace_members_handler)) - .route(web::post().to(create_workspace_members_handler)) // deprecated + .route(web::post().to(create_workspace_members_handler)) // deprecated, use invite flow instead .route(web::put().to(update_workspace_member_handler)) .route(web::delete().to(remove_workspace_member_handler)), ) diff --git a/src/biz/workspace/access_control.rs b/src/biz/workspace/access_control.rs index 1fc16594..aff2dfb0 100644 --- a/src/biz/workspace/access_control.rs +++ b/src/biz/workspace/access_control.rs @@ -120,8 +120,10 @@ where ) -> Result<(), AppError> { if self.should_skip(&method, path) { trace!("Skip access control for the request"); + println!("------- Skip access control for the request"); return Ok(()); } + println!("----- Check access control for the request"); // For some specific resources, we require a specific role to access them instead of the action. // For example, Both AFRole::Owner and AFRole::Member have the write permission to the workspace, @@ -147,6 +149,8 @@ where if result { Ok(()) } else { + println!("------------------------------ Not enough permissions"); + Err(AppError::NotEnoughPermissions { user: uid.to_string(), action: format!( diff --git a/src/biz/workspace/ops.rs b/src/biz/workspace/ops.rs index 6ac08c53..ca388389 100644 --- a/src/biz/workspace/ops.rs +++ b/src/biz/workspace/ops.rs @@ -202,11 +202,6 @@ pub async fn list_workspace_invitations_for_user( /// - Determines the access level based on the member's role. /// - If the member exists (based on their email), inserts them into the workspace and updates their collaboration access level. /// 3. Commits the database transaction. -/// -/// # Returns -/// - A `Result` containing a `HashMap` where the key is the user ID (`uid`) and the value is the role (`AFRole`) assigned to the user in the workspace. -/// If there's an error during the operation, an `AppError` is returned. -/// #[instrument(level = "debug", skip_all, err)] pub async fn add_workspace_members( pg_pool: &PgPool, diff --git a/src/middleware/access_control_mw.rs b/src/middleware/access_control_mw.rs index 36b5cc9c..40e53724 100644 --- a/src/middleware/access_control_mw.rs +++ b/src/middleware/access_control_mw.rs @@ -168,6 +168,7 @@ where Box::pin(async move { // If the workspace_id or collab_object_id is not present, skip the access control if workspace_id.is_none() && object_id.is_none() { + println!("-------- Skip access control for the request"); return fut.await; } diff --git a/tests/workspace/invitation_crud.rs b/tests/workspace/invitation_crud.rs index f8007ad5..217e9a59 100644 --- a/tests/workspace/invitation_crud.rs +++ b/tests/workspace/invitation_crud.rs @@ -4,7 +4,7 @@ use shared_entity::dto::workspace_dto::WorkspaceMemberInvitation; #[tokio::test] async fn invite_workspace_crud() { - let (alice_client, alice) = generate_unique_registered_user_client().await; + let (alice_client, _alice) = generate_unique_registered_user_client().await; let alice_workspace_id = alice_client .get_workspaces() .await diff --git a/tests/workspace/member_crud.rs b/tests/workspace/member_crud.rs index e9e3894f..d2c74713 100644 --- a/tests/workspace/member_crud.rs +++ b/tests/workspace/member_crud.rs @@ -49,7 +49,7 @@ async fn add_workspace_members_not_enough_permission() { // client 2 add client 3 to client 1's workspace but permission denied let error = member_1 - .try_add_workspace_member(&workspace_id, &member_2, AFRole::Member) + .invite_and_accepted_workspace_member(&workspace_id, &member_2, AFRole::Member) .await .unwrap_err(); assert_eq!(error.code, ErrorCode::NotEnoughPermissions);