chore: simplify collab level access control (#1008)
This commit is contained in:
parent
e6dbc95641
commit
afeaeb7796
|
|
@ -6,7 +6,7 @@ use tracing::instrument;
|
||||||
use crate::{
|
use crate::{
|
||||||
act::{Action, ActionVariant},
|
act::{Action, ActionVariant},
|
||||||
collab::{CollabAccessControl, RealtimeAccessControl},
|
collab::{CollabAccessControl, RealtimeAccessControl},
|
||||||
entity::{ObjectType, SubjectType},
|
entity::ObjectType,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::access::AccessControl;
|
use super::access::AccessControl;
|
||||||
|
|
@ -28,16 +28,25 @@ impl CollabAccessControl for CollabAccessControlImpl {
|
||||||
&self,
|
&self,
|
||||||
workspace_id: &str,
|
workspace_id: &str,
|
||||||
uid: &i64,
|
uid: &i64,
|
||||||
oid: &str,
|
_oid: &str,
|
||||||
action: Action,
|
action: Action,
|
||||||
) -> Result<(), AppError> {
|
) -> Result<(), AppError> {
|
||||||
|
// TODO: allow non workspace member to read a collab.
|
||||||
|
|
||||||
|
// Anyone who can write to a workspace, can also delete a collab.
|
||||||
|
let workspace_action = match action {
|
||||||
|
Action::Read => Action::Read,
|
||||||
|
Action::Write => Action::Write,
|
||||||
|
Action::Delete => Action::Write,
|
||||||
|
};
|
||||||
|
|
||||||
self
|
self
|
||||||
.access_control
|
.access_control
|
||||||
.enforce(
|
.enforce(
|
||||||
workspace_id,
|
workspace_id,
|
||||||
uid,
|
uid,
|
||||||
ObjectType::Collab(oid),
|
ObjectType::Workspace(workspace_id),
|
||||||
ActionVariant::FromAction(&action),
|
ActionVariant::FromAction(&workspace_action),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
@ -46,16 +55,26 @@ impl CollabAccessControl for CollabAccessControlImpl {
|
||||||
&self,
|
&self,
|
||||||
workspace_id: &str,
|
workspace_id: &str,
|
||||||
uid: &i64,
|
uid: &i64,
|
||||||
oid: &str,
|
_oid: &str,
|
||||||
access_level: AFAccessLevel,
|
access_level: AFAccessLevel,
|
||||||
) -> Result<(), AppError> {
|
) -> Result<(), AppError> {
|
||||||
|
// TODO: allow non workspace member to read a collab.
|
||||||
|
|
||||||
|
// Anyone who can write to a workspace, also have full access to a collab.
|
||||||
|
let workspace_action = match access_level {
|
||||||
|
AFAccessLevel::ReadOnly => Action::Read,
|
||||||
|
AFAccessLevel::ReadAndComment => Action::Read,
|
||||||
|
AFAccessLevel::ReadAndWrite => Action::Write,
|
||||||
|
AFAccessLevel::FullAccess => Action::Write,
|
||||||
|
};
|
||||||
|
|
||||||
self
|
self
|
||||||
.access_control
|
.access_control
|
||||||
.enforce(
|
.enforce(
|
||||||
workspace_id,
|
workspace_id,
|
||||||
uid,
|
uid,
|
||||||
ObjectType::Collab(oid),
|
ObjectType::Workspace(workspace_id),
|
||||||
ActionVariant::FromAccessLevel(&access_level),
|
ActionVariant::FromAction(&workspace_action),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
@ -63,28 +82,17 @@ impl CollabAccessControl for CollabAccessControlImpl {
|
||||||
#[instrument(level = "info", skip_all)]
|
#[instrument(level = "info", skip_all)]
|
||||||
async fn update_access_level_policy(
|
async fn update_access_level_policy(
|
||||||
&self,
|
&self,
|
||||||
uid: &i64,
|
_uid: &i64,
|
||||||
oid: &str,
|
_oid: &str,
|
||||||
level: AFAccessLevel,
|
_level: AFAccessLevel,
|
||||||
) -> Result<(), AppError> {
|
) -> Result<(), AppError> {
|
||||||
self
|
// TODO: allow non workspace member to read a collab.
|
||||||
.access_control
|
|
||||||
.update_policy(
|
|
||||||
SubjectType::User(*uid),
|
|
||||||
ObjectType::Collab(oid),
|
|
||||||
ActionVariant::FromAccessLevel(&level),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(level = "info", skip_all)]
|
#[instrument(level = "info", skip_all)]
|
||||||
async fn remove_access_level(&self, uid: &i64, oid: &str) -> Result<(), AppError> {
|
async fn remove_access_level(&self, _uid: &i64, _oid: &str) -> Result<(), AppError> {
|
||||||
self
|
// TODO: allow non workspace member to read a collab.
|
||||||
.access_control
|
|
||||||
.remove_policy(&SubjectType::User(*uid), &ObjectType::Collab(oid))
|
|
||||||
.await?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -103,20 +111,35 @@ impl RealtimeCollabAccessControlImpl {
|
||||||
&self,
|
&self,
|
||||||
workspace_id: &str,
|
workspace_id: &str,
|
||||||
uid: &i64,
|
uid: &i64,
|
||||||
oid: &str,
|
_oid: &str,
|
||||||
required_action: Action,
|
required_action: Action,
|
||||||
) -> Result<bool, AppError> {
|
) -> Result<bool, AppError> {
|
||||||
self
|
// TODO: allow non workspace member to read a collab.
|
||||||
|
|
||||||
|
// Anyone who can write to a workspace, can also delete a collab.
|
||||||
|
let workspace_action = match required_action {
|
||||||
|
Action::Read => Action::Read,
|
||||||
|
Action::Write => Action::Write,
|
||||||
|
Action::Delete => Action::Write,
|
||||||
|
};
|
||||||
|
|
||||||
|
let enforcement_result = self
|
||||||
.access_control
|
.access_control
|
||||||
.enforce(
|
.enforce(
|
||||||
workspace_id,
|
workspace_id,
|
||||||
uid,
|
uid,
|
||||||
ObjectType::Collab(oid),
|
ObjectType::Workspace(workspace_id),
|
||||||
ActionVariant::FromAction(&required_action),
|
ActionVariant::FromAction(&workspace_action),
|
||||||
)
|
)
|
||||||
.await?;
|
.await;
|
||||||
|
match enforcement_result {
|
||||||
Ok(true)
|
Ok(_) => Ok(true),
|
||||||
|
Err(AppError::NotEnoughPermissions {
|
||||||
|
user: _user,
|
||||||
|
workspace_id: _workspace_id,
|
||||||
|
}) => Ok(false),
|
||||||
|
Err(e) => Err(e),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,10 +37,9 @@ use client_api::entity::{
|
||||||
};
|
};
|
||||||
use client_api::ws::{WSClient, WSClientConfig};
|
use client_api::ws::{WSClient, WSClientConfig};
|
||||||
use database_entity::dto::{
|
use database_entity::dto::{
|
||||||
AFAccessLevel, AFRole, AFSnapshotMeta, AFSnapshotMetas, AFUserProfile, AFUserWorkspaceInfo,
|
AFRole, AFSnapshotMeta, AFSnapshotMetas, AFUserProfile, AFUserWorkspaceInfo, AFWorkspace,
|
||||||
AFWorkspace, AFWorkspaceInvitationStatus, AFWorkspaceMember, BatchQueryCollabResult,
|
AFWorkspaceInvitationStatus, AFWorkspaceMember, BatchQueryCollabResult, CollabParams,
|
||||||
CollabParams, CreateCollabParams, InsertCollabMemberParams, QueryCollab, QueryCollabParams,
|
CreateCollabParams, QueryCollab, QueryCollabParams, QuerySnapshotParams, SnapshotData,
|
||||||
QuerySnapshotParams, SnapshotData, UpdateCollabMemberParams,
|
|
||||||
};
|
};
|
||||||
use shared_entity::dto::workspace_dto::{
|
use shared_entity::dto::workspace_dto::{
|
||||||
BlobMetadata, CollabResponse, PublishedDuplicate, WorkspaceMemberChangeset,
|
BlobMetadata, CollabResponse, PublishedDuplicate, WorkspaceMemberChangeset,
|
||||||
|
|
@ -441,46 +440,6 @@ impl TestClient {
|
||||||
self.api_client.get_workspace_member(params).await
|
self.api_client.get_workspace_member(params).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn add_collab_member(
|
|
||||||
&self,
|
|
||||||
workspace_id: &str,
|
|
||||||
object_id: &str,
|
|
||||||
other_client: &TestClient,
|
|
||||||
access_level: AFAccessLevel,
|
|
||||||
) {
|
|
||||||
let uid = other_client.uid().await;
|
|
||||||
self
|
|
||||||
.api_client
|
|
||||||
.add_collab_member(InsertCollabMemberParams {
|
|
||||||
uid,
|
|
||||||
workspace_id: workspace_id.to_string(),
|
|
||||||
object_id: object_id.to_string(),
|
|
||||||
access_level,
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn update_collab_member_access_level(
|
|
||||||
&self,
|
|
||||||
workspace_id: &str,
|
|
||||||
object_id: &str,
|
|
||||||
other_client: &TestClient,
|
|
||||||
access_level: AFAccessLevel,
|
|
||||||
) {
|
|
||||||
let uid = other_client.uid().await;
|
|
||||||
self
|
|
||||||
.api_client
|
|
||||||
.update_collab_member(UpdateCollabMemberParams {
|
|
||||||
uid,
|
|
||||||
workspace_id: workspace_id.to_string(),
|
|
||||||
object_id: object_id.to_string(),
|
|
||||||
access_level,
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn wait_object_sync_complete(&self, object_id: &str) -> Result<(), Error> {
|
pub async fn wait_object_sync_complete(&self, object_id: &str) -> Result<(), Error> {
|
||||||
self
|
self
|
||||||
.wait_object_sync_complete_with_secs(object_id, 60)
|
.wait_object_sync_complete_with_secs(object_id, 60)
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ use collab_entity::CollabType;
|
||||||
use tokio::time::sleep;
|
use tokio::time::sleep;
|
||||||
|
|
||||||
use client_api_test::TestClient;
|
use client_api_test::TestClient;
|
||||||
use database_entity::dto::{AFAccessLevel, AFRole};
|
use database_entity::dto::AFRole;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn viewing_document_editing_users_test() {
|
async fn viewing_document_editing_users_test() {
|
||||||
|
|
@ -27,14 +27,6 @@ async fn viewing_document_editing_users_test() {
|
||||||
assert_eq!(clients.len(), 1);
|
assert_eq!(clients.len(), 1);
|
||||||
assert_eq!(clients[0], owner_uid);
|
assert_eq!(clients[0], owner_uid);
|
||||||
|
|
||||||
owner
|
|
||||||
.add_collab_member(
|
|
||||||
&workspace_id,
|
|
||||||
&object_id,
|
|
||||||
&guest,
|
|
||||||
AFAccessLevel::ReadAndWrite,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
guest
|
guest
|
||||||
.open_collab(&workspace_id, &object_id, collab_type)
|
.open_collab(&workspace_id, &object_id, collab_type)
|
||||||
.await;
|
.await;
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use client_api::entity::AFRole;
|
||||||
use collab_entity::CollabType;
|
use collab_entity::CollabType;
|
||||||
use serde_json::{json, Value};
|
use serde_json::{json, Value};
|
||||||
use tokio::time::sleep;
|
use tokio::time::sleep;
|
||||||
|
|
||||||
use client_api_test::{assert_client_collab_include_value, TestClient};
|
use client_api_test::{assert_client_collab_include_value, TestClient};
|
||||||
use database_entity::dto::AFAccessLevel;
|
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn client_apply_update_find_missing_update_test() {
|
async fn client_apply_update_find_missing_update_test() {
|
||||||
|
|
@ -56,13 +56,9 @@ async fn make_clients() -> (TestClient, TestClient, String, Value) {
|
||||||
.create_and_edit_collab(&workspace_id, collab_type.clone())
|
.create_and_edit_collab(&workspace_id, collab_type.clone())
|
||||||
.await;
|
.await;
|
||||||
client_1
|
client_1
|
||||||
.add_collab_member(
|
.invite_and_accepted_workspace_member(&workspace_id, &client_2, AFRole::Member)
|
||||||
&workspace_id,
|
.await
|
||||||
&object_id,
|
.unwrap();
|
||||||
&client_2,
|
|
||||||
AFAccessLevel::ReadAndWrite,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
// after client 2 finish init sync and then disable receive message
|
// after client 2 finish init sync and then disable receive message
|
||||||
client_2
|
client_2
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use client_api::entity::AFRole;
|
||||||
use collab_entity::CollabType;
|
use collab_entity::CollabType;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use sqlx::types::uuid;
|
use sqlx::types::uuid;
|
||||||
|
|
@ -7,7 +8,7 @@ use tokio::time::sleep;
|
||||||
use tracing::trace;
|
use tracing::trace;
|
||||||
|
|
||||||
use client_api_test::*;
|
use client_api_test::*;
|
||||||
use database_entity::dto::{AFAccessLevel, QueryCollabParams};
|
use database_entity::dto::QueryCollabParams;
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn sync_collab_content_after_reconnect_test() {
|
async fn sync_collab_content_after_reconnect_test() {
|
||||||
|
|
@ -200,15 +201,11 @@ async fn edit_document_with_both_clients_offline_then_online_sync_test() {
|
||||||
.create_and_edit_collab(&workspace_id, collab_type.clone())
|
.create_and_edit_collab(&workspace_id, collab_type.clone())
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
// add client 2 as a member of the collab
|
// add client 2 as a member of the workspace
|
||||||
client_1
|
client_1
|
||||||
.add_collab_member(
|
.invite_and_accepted_workspace_member(&workspace_id, &client_2, AFRole::Member)
|
||||||
&workspace_id,
|
.await
|
||||||
&object_id,
|
.unwrap();
|
||||||
&client_2,
|
|
||||||
AFAccessLevel::ReadAndWrite,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
client_1.disconnect().await;
|
client_1.disconnect().await;
|
||||||
|
|
||||||
client_2
|
client_2
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ use client_api_test::{
|
||||||
assert_client_collab_include_value, assert_client_collab_within_secs, assert_server_collab,
|
assert_client_collab_include_value, assert_client_collab_within_secs, assert_server_collab,
|
||||||
TestClient,
|
TestClient,
|
||||||
};
|
};
|
||||||
use database_entity::dto::{AFAccessLevel, AFRole};
|
use database_entity::dto::AFRole;
|
||||||
|
|
||||||
use crate::collab::util::generate_random_string;
|
use crate::collab::util::generate_random_string;
|
||||||
|
|
||||||
|
|
@ -164,13 +164,9 @@ async fn edit_collab_with_readonly_permission_test() {
|
||||||
|
|
||||||
// Add client 2 as the member of the collab then the client 2 will receive the update.
|
// Add client 2 as the member of the collab then the client 2 will receive the update.
|
||||||
client_1
|
client_1
|
||||||
.add_collab_member(
|
.invite_and_accepted_workspace_member(&workspace_id, &client_2, AFRole::Guest)
|
||||||
&workspace_id,
|
.await
|
||||||
&object_id,
|
.unwrap();
|
||||||
&client_2,
|
|
||||||
AFAccessLevel::ReadOnly,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
client_2
|
client_2
|
||||||
.open_collab(&workspace_id, &object_id, collab_type.clone())
|
.open_collab(&workspace_id, &object_id, collab_type.clone())
|
||||||
|
|
@ -214,13 +210,9 @@ async fn edit_collab_with_read_and_write_permission_test() {
|
||||||
|
|
||||||
// Add client 2 as the member of the collab then the client 2 will receive the update.
|
// Add client 2 as the member of the collab then the client 2 will receive the update.
|
||||||
client_1
|
client_1
|
||||||
.add_collab_member(
|
.invite_and_accepted_workspace_member(&workspace_id, &client_2, AFRole::Member)
|
||||||
&workspace_id,
|
.await
|
||||||
&object_id,
|
.unwrap();
|
||||||
&client_2,
|
|
||||||
AFAccessLevel::ReadAndWrite,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
client_2
|
client_2
|
||||||
.open_collab(&workspace_id, &object_id, collab_type.clone())
|
.open_collab(&workspace_id, &object_id, collab_type.clone())
|
||||||
|
|
@ -265,13 +257,9 @@ async fn edit_collab_with_full_access_permission_test() {
|
||||||
|
|
||||||
// Add client 2 as the member of the collab then the client 2 will receive the update.
|
// Add client 2 as the member of the collab then the client 2 will receive the update.
|
||||||
client_1
|
client_1
|
||||||
.add_collab_member(
|
.invite_and_accepted_workspace_member(&workspace_id, &client_2, AFRole::Member)
|
||||||
&workspace_id,
|
.await
|
||||||
&object_id,
|
.unwrap();
|
||||||
&client_2,
|
|
||||||
AFAccessLevel::FullAccess,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
client_2
|
client_2
|
||||||
.open_collab(&workspace_id, &object_id, collab_type.clone())
|
.open_collab(&workspace_id, &object_id, collab_type.clone())
|
||||||
|
|
@ -314,13 +302,9 @@ async fn edit_collab_with_full_access_then_readonly_permission() {
|
||||||
|
|
||||||
// Add client 2 as the member of the collab then the client 2 will receive the update.
|
// Add client 2 as the member of the collab then the client 2 will receive the update.
|
||||||
client_1
|
client_1
|
||||||
.add_collab_member(
|
.invite_and_accepted_workspace_member(&workspace_id, &client_2, AFRole::Member)
|
||||||
&workspace_id,
|
.await
|
||||||
&object_id,
|
.unwrap();
|
||||||
&client_2,
|
|
||||||
AFAccessLevel::FullAccess,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
// client 2 edit the collab and then the server will broadcast the update
|
// client 2 edit the collab and then the server will broadcast the update
|
||||||
{
|
{
|
||||||
|
|
@ -340,13 +324,9 @@ async fn edit_collab_with_full_access_then_readonly_permission() {
|
||||||
// updates generated by client 2
|
// updates generated by client 2
|
||||||
{
|
{
|
||||||
client_1
|
client_1
|
||||||
.update_collab_member_access_level(
|
.try_update_workspace_member(&workspace_id, &client_2, AFRole::Guest)
|
||||||
&workspace_id,
|
.await
|
||||||
&object_id,
|
.unwrap();
|
||||||
&client_2,
|
|
||||||
AFAccessLevel::ReadOnly,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
client_2
|
client_2
|
||||||
.insert_into(&object_id, "subtitle", "Writing Rust, fun")
|
.insert_into(&object_id, "subtitle", "Writing Rust, fun")
|
||||||
.await;
|
.await;
|
||||||
|
|
@ -404,14 +384,6 @@ async fn multiple_user_with_read_and_write_permission_edit_same_collab_test() {
|
||||||
.invite_and_accepted_workspace_member(&workspace_id, &new_member, AFRole::Member)
|
.invite_and_accepted_workspace_member(&workspace_id, &new_member, AFRole::Member)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
owner
|
|
||||||
.add_collab_member(
|
|
||||||
&workspace_id,
|
|
||||||
&object_id,
|
|
||||||
&new_member,
|
|
||||||
AFAccessLevel::ReadAndWrite,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
new_member
|
new_member
|
||||||
.open_collab(&workspace_id, &object_id, collab_type.clone())
|
.open_collab(&workspace_id, &object_id, collab_type.clone())
|
||||||
|
|
@ -490,13 +462,9 @@ async fn multiple_user_with_read_only_permission_edit_same_collab_test() {
|
||||||
// sleep 2 secs to make sure it do not trigger register user too fast in gotrue
|
// sleep 2 secs to make sure it do not trigger register user too fast in gotrue
|
||||||
sleep(Duration::from_secs(i % 2)).await;
|
sleep(Duration::from_secs(i % 2)).await;
|
||||||
owner
|
owner
|
||||||
.add_collab_member(
|
.invite_and_accepted_workspace_member(&workspace_id, &new_user, AFRole::Guest)
|
||||||
&workspace_id,
|
.await
|
||||||
&object_id,
|
.unwrap();
|
||||||
&new_user,
|
|
||||||
AFAccessLevel::ReadOnly,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
new_user
|
new_user
|
||||||
.open_collab(&workspace_id, &object_id, collab_type.clone())
|
.open_collab(&workspace_id, &object_id, collab_type.clone())
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use assert_json_diff::assert_json_eq;
|
use assert_json_diff::assert_json_eq;
|
||||||
|
use client_api::entity::AFRole;
|
||||||
use collab::core::origin::CollabOrigin;
|
use collab::core::origin::CollabOrigin;
|
||||||
use collab_entity::CollabType;
|
use collab_entity::CollabType;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
@ -11,7 +12,6 @@ use uuid::Uuid;
|
||||||
|
|
||||||
use client_api_test::*;
|
use client_api_test::*;
|
||||||
use collab_rt_entity::{CollabMessage, RealtimeMessage, UpdateSync, MAXIMUM_REALTIME_MESSAGE_SIZE};
|
use collab_rt_entity::{CollabMessage, RealtimeMessage, UpdateSync, MAXIMUM_REALTIME_MESSAGE_SIZE};
|
||||||
use database_entity::dto::AFAccessLevel;
|
|
||||||
|
|
||||||
use crate::collab::util::{
|
use crate::collab::util::{
|
||||||
generate_random_bytes, generate_random_string, make_big_collab_doc_state,
|
generate_random_bytes, generate_random_string, make_big_collab_doc_state,
|
||||||
|
|
@ -325,13 +325,9 @@ async fn two_direction_peer_sync_test() {
|
||||||
// Before the client_2 want to edit the collab object, it needs to become a member of the collab
|
// Before the client_2 want to edit the collab object, it needs to become a member of the collab
|
||||||
// Otherwise, the server will reject the edit request
|
// Otherwise, the server will reject the edit request
|
||||||
client_1
|
client_1
|
||||||
.add_collab_member(
|
.invite_and_accepted_workspace_member(&workspace_id, &client_2, AFRole::Member)
|
||||||
&workspace_id,
|
.await
|
||||||
&object_id,
|
.unwrap();
|
||||||
&client_2,
|
|
||||||
AFAccessLevel::FullAccess,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
client_2
|
client_2
|
||||||
.open_collab(&workspace_id, &object_id, collab_type.clone())
|
.open_collab(&workspace_id, &object_id, collab_type.clone())
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue