Skip to content

Commit

Permalink
chore: simplify collab level access control
Browse files Browse the repository at this point in the history
  • Loading branch information
khorshuheng committed Nov 20, 2024
1 parent c421f03 commit 7c6243a
Show file tree
Hide file tree
Showing 7 changed files with 91 additions and 160 deletions.
85 changes: 54 additions & 31 deletions libs/access-control/src/casbin/collab.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use tracing::instrument;
use crate::{
act::{Action, ActionVariant},
collab::{CollabAccessControl, RealtimeAccessControl},
entity::{ObjectType, SubjectType},
entity::ObjectType,
};

use super::access::AccessControl;
Expand All @@ -28,16 +28,25 @@ impl CollabAccessControl for CollabAccessControlImpl {
&self,
workspace_id: &str,
uid: &i64,
oid: &str,
_oid: &str,
action: Action,
) -> 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
.access_control
.enforce(
workspace_id,
uid,
ObjectType::Collab(oid),
ActionVariant::FromAction(&action),
ObjectType::Workspace(workspace_id),
ActionVariant::FromAction(&workspace_action),
)
.await
}
Expand All @@ -46,45 +55,44 @@ impl CollabAccessControl for CollabAccessControlImpl {
&self,
workspace_id: &str,
uid: &i64,
oid: &str,
_oid: &str,
access_level: AFAccessLevel,
) -> 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
.access_control
.enforce(
workspace_id,
uid,
ObjectType::Collab(oid),
ActionVariant::FromAccessLevel(&access_level),
ObjectType::Workspace(workspace_id),
ActionVariant::FromAction(&workspace_action),
)
.await
}

#[instrument(level = "info", skip_all)]
async fn update_access_level_policy(
&self,
uid: &i64,
oid: &str,
level: AFAccessLevel,
_uid: &i64,
_oid: &str,
_level: AFAccessLevel,
) -> Result<(), AppError> {
self
.access_control
.update_policy(
SubjectType::User(*uid),
ObjectType::Collab(oid),
ActionVariant::FromAccessLevel(&level),
)
.await?;

// TODO: allow non workspace member to read a collab.
Ok(())
}

#[instrument(level = "info", skip_all)]
async fn remove_access_level(&self, uid: &i64, oid: &str) -> Result<(), AppError> {
self
.access_control
.remove_policy(&SubjectType::User(*uid), &ObjectType::Collab(oid))
.await?;
async fn remove_access_level(&self, _uid: &i64, _oid: &str) -> Result<(), AppError> {
// TODO: allow non workspace member to read a collab.
Ok(())
}
}
Expand All @@ -103,20 +111,35 @@ impl RealtimeCollabAccessControlImpl {
&self,
workspace_id: &str,
uid: &i64,
oid: &str,
_oid: &str,
required_action: Action,
) -> 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
.enforce(
workspace_id,
uid,
ObjectType::Collab(oid),
ActionVariant::FromAction(&required_action),
ObjectType::Workspace(workspace_id),
ActionVariant::FromAction(&workspace_action),
)
.await?;

Ok(true)
.await;
match enforcement_result {
Ok(_) => Ok(true),
Err(AppError::NotEnoughPermissions {
user: _user,
workspace_id: _workspace_id,
}) => Ok(false),
Err(e) => Err(e),
}
}
}

Expand Down
47 changes: 3 additions & 44 deletions libs/client-api-test/src/test_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,9 @@ use client_api::entity::{
};
use client_api::ws::{WSClient, WSClientConfig};
use database_entity::dto::{
AFAccessLevel, AFRole, AFSnapshotMeta, AFSnapshotMetas, AFUserProfile, AFUserWorkspaceInfo,
AFWorkspace, AFWorkspaceInvitationStatus, AFWorkspaceMember, BatchQueryCollabResult,
CollabParams, CreateCollabParams, InsertCollabMemberParams, QueryCollab, QueryCollabParams,
QuerySnapshotParams, SnapshotData, UpdateCollabMemberParams,
AFRole, AFSnapshotMeta, AFSnapshotMetas, AFUserProfile, AFUserWorkspaceInfo, AFWorkspace,
AFWorkspaceInvitationStatus, AFWorkspaceMember, BatchQueryCollabResult, CollabParams,
CreateCollabParams, QueryCollab, QueryCollabParams, QuerySnapshotParams, SnapshotData,
};
use shared_entity::dto::workspace_dto::{
BlobMetadata, CollabResponse, PublishedDuplicate, WorkspaceMemberChangeset,
Expand Down Expand Up @@ -441,46 +440,6 @@ impl TestClient {
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> {
self
.wait_object_sync_complete_with_secs(object_id, 60)
Expand Down
10 changes: 1 addition & 9 deletions tests/collab/awareness_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use collab_entity::CollabType;
use tokio::time::sleep;

use client_api_test::TestClient;
use database_entity::dto::{AFAccessLevel, AFRole};
use database_entity::dto::AFRole;

#[tokio::test]
async fn viewing_document_editing_users_test() {
Expand All @@ -27,14 +27,6 @@ async fn viewing_document_editing_users_test() {
assert_eq!(clients.len(), 1);
assert_eq!(clients[0], owner_uid);

owner
.add_collab_member(
&workspace_id,
&object_id,
&guest,
AFAccessLevel::ReadAndWrite,
)
.await;
guest
.open_collab(&workspace_id, &object_id, collab_type)
.await;
Expand Down
12 changes: 4 additions & 8 deletions tests/collab/missing_update_test.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use std::time::Duration;

use client_api::entity::AFRole;
use collab_entity::CollabType;
use serde_json::{json, Value};
use tokio::time::sleep;

use client_api_test::{assert_client_collab_include_value, TestClient};
use database_entity::dto::AFAccessLevel;

#[tokio::test]
async fn client_apply_update_find_missing_update_test() {
Expand Down Expand Up @@ -56,13 +56,9 @@ async fn make_clients() -> (TestClient, TestClient, String, Value) {
.create_and_edit_collab(&workspace_id, collab_type.clone())
.await;
client_1
.add_collab_member(
&workspace_id,
&object_id,
&client_2,
AFAccessLevel::ReadAndWrite,
)
.await;
.invite_and_accepted_workspace_member(&workspace_id, &client_2, AFRole::Member)
.await
.unwrap();

// after client 2 finish init sync and then disable receive message
client_2
Expand Down
15 changes: 6 additions & 9 deletions tests/collab/multi_devices_edit.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
use std::time::Duration;

use client_api::entity::AFRole;
use collab_entity::CollabType;
use serde_json::json;
use sqlx::types::uuid;
use tokio::time::sleep;
use tracing::trace;

use client_api_test::*;
use database_entity::dto::{AFAccessLevel, QueryCollabParams};
use database_entity::dto::QueryCollabParams;

#[tokio::test]
async fn sync_collab_content_after_reconnect_test() {
Expand Down Expand Up @@ -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())
.await;

// add client 2 as a member of the collab
// add client 2 as a member of the workspace
client_1
.add_collab_member(
&workspace_id,
&object_id,
&client_2,
AFAccessLevel::ReadAndWrite,
)
.await;
.invite_and_accepted_workspace_member(&workspace_id, &client_2, AFRole::Member)
.await
.unwrap();
client_1.disconnect().await;

client_2
Expand Down
Loading

0 comments on commit 7c6243a

Please sign in to comment.