diff --git a/crates/collab/src/db/ids.rs b/crates/collab/src/db/ids.rs index 9bb766147f5b0b2084b76665262962179a62e6eb..2e2218c4d9da181719cf6404245a4d84d4285dab 100644 --- a/crates/collab/src/db/ids.rs +++ b/crates/collab/src/db/ids.rs @@ -140,6 +140,14 @@ impl ChannelRole { Guest | Banned => false, } } + + pub fn can_edit_projects(&self) -> bool { + use ChannelRole::*; + match self { + Admin | Member => true, + Guest | Banned => false, + } + } } impl From for ChannelRole { diff --git a/crates/collab/src/db/queries/projects.rs b/crates/collab/src/db/queries/projects.rs index 5b8d54f8d36a746b2c09a77cbf9e75b77b557543..ca59c851e738865c6ffc50c970490fa3e2a93d1f 100644 --- a/crates/collab/src/db/queries/projects.rs +++ b/crates/collab/src/db/queries/projects.rs @@ -777,6 +777,44 @@ impl Database { .await } + pub async fn host_for_mutating_project_request( + &self, + project_id: ProjectId, + connection_id: ConnectionId, + ) -> Result { + let room_id = self.room_id_for_project(project_id).await?; + self.room_transaction(room_id, |tx| async move { + let current_participant = room_participant::Entity::find() + .filter(room_participant::Column::RoomId.eq(room_id)) + .filter(room_participant::Column::AnsweringConnectionId.eq(connection_id.id)) + .one(&*tx) + .await? + .ok_or_else(|| anyhow!("no such room"))?; + + if !current_participant + .role + .unwrap_or(ChannelRole::Guest) + .can_edit_projects() + { + Err(anyhow!("not authorized to edit projects"))?; + } + + let host = project_collaborator::Entity::find() + .filter( + project_collaborator::Column::ProjectId + .eq(project_id) + .and(project_collaborator::Column::IsHost.eq(true)), + ) + .one(&*tx) + .await? + .ok_or_else(|| anyhow!("failed to read project host"))?; + + Ok(host.connection()) + }) + .await + .map(|guard| guard.into_inner()) + } + pub async fn project_collaborators( &self, project_id: ProjectId, diff --git a/crates/collab/src/rpc.rs b/crates/collab/src/rpc.rs index 835b48809da94dc60cd872d473e564a7456da81e..68774c22e6ec67782c76c6878dad974dfd7db984 100644 --- a/crates/collab/src/rpc.rs +++ b/crates/collab/src/rpc.rs @@ -217,39 +217,43 @@ impl Server { .add_message_handler(update_diagnostic_summary) .add_message_handler(update_worktree_settings) .add_message_handler(refresh_inlay_hints) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) - .add_request_handler(forward_project_request::) + .add_request_handler(forward_read_only_project_request::) + .add_request_handler(forward_read_only_project_request::) + .add_request_handler(forward_read_only_project_request::) + .add_request_handler(forward_read_only_project_request::) + .add_request_handler(forward_read_only_project_request::) + .add_request_handler(forward_read_only_project_request::) + .add_request_handler(forward_read_only_project_request::) + .add_request_handler(forward_read_only_project_request::) + .add_request_handler(forward_read_only_project_request::) + .add_request_handler(forward_read_only_project_request::) + .add_request_handler(forward_read_only_project_request::) + .add_request_handler(forward_mutating_project_request::) + .add_request_handler(forward_mutating_project_request::) + .add_request_handler( + forward_mutating_project_request::, + ) + .add_request_handler( + forward_mutating_project_request::, + ) + .add_request_handler(forward_mutating_project_request::) + .add_request_handler(forward_mutating_project_request::) + .add_request_handler(forward_mutating_project_request::) + .add_request_handler(forward_mutating_project_request::) + .add_request_handler(forward_mutating_project_request::) + .add_request_handler(forward_mutating_project_request::) + .add_request_handler(forward_mutating_project_request::) + .add_request_handler(forward_mutating_project_request::) + .add_request_handler(forward_mutating_project_request::) + .add_request_handler(forward_mutating_project_request::) + .add_request_handler(forward_mutating_project_request::) + .add_request_handler(forward_mutating_project_request::) + .add_request_handler(forward_mutating_project_request::) .add_message_handler(create_buffer_for_peer) .add_request_handler(update_buffer) .add_message_handler(update_buffer_file) .add_message_handler(buffer_reloaded) .add_message_handler(buffer_saved) - .add_request_handler(forward_project_request::) .add_request_handler(get_users) .add_request_handler(fuzzy_search_users) .add_request_handler(request_contact) @@ -1741,7 +1745,7 @@ async fn update_language_server( Ok(()) } -async fn forward_project_request( +async fn forward_read_only_project_request( request: T, response: Response, session: Session, @@ -1772,6 +1776,30 @@ where Ok(()) } +async fn forward_mutating_project_request( + request: T, + response: Response, + session: Session, +) -> Result<()> +where + T: EntityMessage + RequestMessage, +{ + let project_id = ProjectId::from_proto(request.remote_entity_id()); + let host_connection_id = session + .db() + .await + .host_for_mutating_project_request(project_id, session.connection_id) + .await?; + + let payload = session + .peer + .forward_request(session.connection_id, host_connection_id, request) + .await?; + + response.send(payload)?; + Ok(()) +} + async fn create_buffer_for_peer( request: proto::CreateBufferForPeer, session: Session, diff --git a/crates/collab/src/tests/channel_guest_tests.rs b/crates/collab/src/tests/channel_guest_tests.rs index e2051c44a038ced0724b616ac4e3b4cd851b4508..32cc074ec96a11e3806a256eedd8f74191689ace 100644 --- a/crates/collab/src/tests/channel_guest_tests.rs +++ b/crates/collab/src/tests/channel_guest_tests.rs @@ -82,5 +82,13 @@ async fn test_channel_guests( project_b.read_with(cx_b, |project, _| project.remote_id()), Some(project_id), ); - assert!(project_b.read_with(cx_b, |project, _| project.is_read_only())) + assert!(project_b.read_with(cx_b, |project, _| project.is_read_only())); + + assert!(project_b + .update(cx_b, |project, cx| { + let worktree_id = project.worktrees().next().unwrap().read(cx).id(); + project.create_entry((worktree_id, "b.txt"), false, cx) + }) + .await + .is_err()) }