Files
goclaw/internal/tools/team_metadata_keys.go
T
viettranx 6a9b747b65 feat: propagate RBAC role through dispatch + bypass file-writer grant for admins
Dashboard users (and other tenant-authenticated admin/operator/owner
actors) dispatching team tasks to agents in Telegram/Discord groups
previously hit the empty-sender DENY rule when the assigned agent
tried to write files — because dashboard users have tenant identities
(e.g. "viettx") that don't match the numeric file-writer grants stored
per channel.

Two-part fix:

1. CheckFileWriterPermission / CheckCronPermission add an RBAC bypass
   at the store layer: if ctx carries RoleFromContext in {admin,
   operator, owner}, skip the per-user grant check. Tenant RBAC already
   pre-authenticated these callers at the gateway edge; requiring a
   redundant per-channel grant blocks legitimate admin work.

2. Role propagation through the full dispatch chain:
     ctx (RoleFromContext)
       → RunRequest.Role
       → SubagentTask.OriginRole / AnnounceMetadata.OriginRole
         / DelegateRequest.Role
       → InboundMessage.Metadata[MetaOriginRole]
       → subagentAnnounceRouting.Role / teammateRole
       → RunRequest.Role (re-ingress)
       → store.WithRole(ctx, ...) in loop_context
   Covers subagent announce, delegate announce, teammate dispatch
   (WS dashboard, tool), session_send, and processNormalMessage's
   synthetic-sender override path. team_tasks_create persists
   origin_role for deferred dispatches.

Viewer and empty roles fall through to the existing sender-based path
(no bypass).

Regression tests:
- A.10 AdminRoleBypass: admin/operator/owner in ctx allowed even with
  empty sender (normally DENY).
- A.11 ViewerRoleDoesNotBypass: viewer and empty roles still hit DENY.

Builds + integration + unit tests green.
2026-04-16 14:17:48 +07:00

60 lines
2.5 KiB
Go

package tools
// Message metadata keys used in dispatch → consumer communication.
// These keys appear in bus.InboundMessage.Metadata for teammate and
// subagent messages routed through the gateway consumer.
const (
MetaOriginChannel = "origin_channel"
MetaOriginPeerKind = "origin_peer_kind"
MetaOriginChatID = "origin_chat_id"
MetaOriginUserID = "origin_user_id"
// MetaOriginSenderID carries the real acting sender through announce re-ingress
// so permission checks (e.g. CheckFileWriterPermission) attribute to the
// original user rather than a synthetic "subagent:<id>" / "notification:system" string.
MetaOriginSenderID = "origin_sender_id"
// MetaOriginRole carries the caller's RBAC role through dispatch + re-ingress
// so permission checks can bypass per-user grants for authenticated admins
// (e.g. dashboard user dispatches a task that writes files in a group chat).
MetaOriginRole = "origin_role"
MetaOriginLocalKey = "origin_local_key"
MetaOriginSessionKey = "origin_session_key"
MetaOriginTraceID = "origin_trace_id"
MetaOriginRootSpanID = "origin_root_span_id"
MetaFromAgent = "from_agent"
MetaToAgent = "to_agent"
MetaToAgentDisplay = "to_agent_display"
MetaTeamTaskID = "team_task_id"
MetaTeamID = "team_id"
MetaTeamWorkspace = "team_workspace"
MetaLeaderAgentID = "leader_agent_id"
MetaParentAgent = "parent_agent"
MetaSubagentLabel = "subagent_label"
MetaSubagentStatus = "subagent_status"
MetaSubagentResult = "subagent_result"
MetaSubagentRuntime = "subagent_runtime_ms"
MetaSubagentIterations = "subagent_iterations"
MetaSubagentInputToks = "subagent_input_tokens"
MetaSubagentOutputToks = "subagent_output_tokens"
MetaCommand = "command"
MetaIsForum = "is_forum"
MetaMessageThreadID = "message_thread_id"
MetaDMThreadID = "dm_thread_id"
MetaChatTitle = "chat_title"
MetaUsername = "username"
MetaUserName = "user_name"
MetaTopicSystemPrompt = "topic_system_prompt"
MetaTopicSkills = "topic_skills"
)
// Task metadata keys stored in store.TeamTaskData.Metadata.
// Written during task creation/dispatch, read during deferred dispatch
// and trace context restoration.
const (
TaskMetaPeerKind = "peer_kind"
TaskMetaLocalKey = "local_key"
TaskMetaOriginSession = "origin_session_key"
TaskMetaOriginTrace = "origin_trace_id"
TaskMetaOriginRootSpan = "origin_root_span_id"
TaskMetaTeamWorkspace = "team_workspace"
)