From 4f096f26b5889bd163fbb8b003b5fecb5ba12c75 Mon Sep 17 00:00:00 2001 From: Hare Date: Fri, 30 Oct 2026 02:52:25 +0900 Subject: [PATCH] feat: expose current Plugin execution identity to trusted Hosts --- .../src/server/subject_authorization.rs | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/crates/workspace-server/src/server/subject_authorization.rs b/crates/workspace-server/src/server/subject_authorization.rs index 1cac9095e..7a993cd9c 100644 --- a/crates/workspace-server/src/server/subject_authorization.rs +++ b/crates/workspace-server/src/server/subject_authorization.rs @@ -709,6 +709,50 @@ pub(super) async fn interrupt_subject_workers( results } +impl WorkspaceApi { + /// Trusted Host plan creation uses the same identity as live Plugin operations. + /// Principal and delegation are derived here, never accepted from public JSON. + pub async fn plugin_execution_identity( + &self, + worker: &RuntimeWorkerRef, + ) -> crate::Result { + self.store + .get_workspace_runtime_binding(self.workspace_id(), &worker.runtime_id) + .await? + .filter(|binding| binding.revoked_at.is_none()) + .ok_or_else(|| { + Error::WorkspacePermissionDenied("Plugin Runtime trust revoked".into()) + })?; + let api = self.clone(); + let worker = worker.clone(); + self.host_executors + .host()? + .run(move || { + let member = api.store.get_worker_registry(api.workspace_id(), &worker)?; + let reserved = api + .store + .has_active_worker_create_reservation(api.workspace_id(), &worker)?; + if member.is_none() && !reserved { + return Err(Error::WorkspacePermissionDenied( + "Plugin Worker is not registered".into(), + )); + } + api.store + .require_current_worker_singleton_owner(api.workspace_id(), &worker)?; + let summary = api.runtime.worker(&worker).map_err(|_| { + Error::WorkspacePermissionDenied("Plugin Worker unavailable".into()) + })?; + if summary.workspace.workspace_id.as_deref() != Some(api.workspace_id()) { + return Err(Error::WorkspacePermissionDenied( + "foreign Plugin Worker".into(), + )); + } + plugin_execution_identity(&api, &worker).map_err(|error| error.error) + }) + .await + } +} + pub(super) fn administration_actor( context: &server_api::ServerRequestContext, ) -> ApiResult<&RequestActor> {