FR-048 Phase 2a — 授權守門盤點:participant / remote_agent

事實盤點,不下判斷。分支 fix/v1.8.0-bugs。所有 route 前綴 /api/1.0assert_project_manager 定義於 common/util/participant_guard.py(route 提示的 common.authz.project 尚未遷入,現況仍 common.util.participant_guard)。

§1

api/participant

重要脈絡api/participant/__init__.py 內多個 resource 已被註解停用(FR-038 DEAD-V1,2026-06-18,FE 無 caller)。停用者標「路由已註解」。仍掛載的 live 寫入端點:/task-assignees/batch(POST)、/task-assignee(POST/PUT/DELETE)、/project-participant(POST/PUT/DELETE)、/project-control-participant(POST/PUT/DELETE)、/control-group-participant(POST/PUT/DELETE)。

Route path Method Route file:line @jwt_required? 呼叫的 service method 現況守門 備註
/api/1.0/task-assignees POST api/participant/routes/task_assignee_route.py:49 TaskAssigneeService.get_task_assignees 僅 JWT POST 實為分頁查詢(讀)
/api/1.0/task-assignees/batch POST api/participant/routes/task_assignee_route.py:24 TaskAssigneeService.batch_add_task_assignees 僅 JWT 寫入端點但 service 為 FR-038 2A 死碼 stub,直接 return [](app/participant/service/task_assignee_service.py:173-188),無守門也無實際寫入
/api/1.0/task-assignee POST api/participant/routes/task_assignee_route.py:65 TaskAssigneeService.add_task_assignee 已有: assert_project_manager @ app/participant/service/task_assignee_service.py:142 role level: project manager,以 control_id scope(enforce_role 預設 True)
/api/1.0/task-assignee PUT api/participant/routes/task_assignee_route.py:80 TaskAssigneeService.update_task_assignee 已有: assert_project_manager @ app/participant/service/task_assignee_service.py:212 role level: project manager,control_id scope
/api/1.0/task-assignee DELETE api/participant/routes/task_assignee_route.py:103 TaskAssigneeService.delete_task_assignee 已有: assert_project_manager @ app/participant/service/task_assignee_service.py:249 role level: project manager,control_id scope
/api/1.0/project-participants POST api/participant/routes/project_participant_route.py:61 ProjectParticipantService.get_project_participants 僅 JWT POST 實為分頁查詢(讀)
/api/1.0/project-participant POST api/participant/routes/project_participant_route.py:78 ProjectParticipantService.add_project_participant 已有: assert_project_manager @ app/participant/service/project_participant_service.py:83 role level: project manager(無 group/control scope)
/api/1.0/project-participant PUT api/participant/routes/project_participant_route.py:95 ProjectParticipantService.update_project_participant 已有: assert_project_manager @ app/participant/service/project_participant_service.py:125 role level: project manager
/api/1.0/project-participant DELETE api/participant/routes/project_participant_route.py:109 ProjectParticipantService.delete_project_participant 已有: assert_project_manager @ app/participant/service/project_participant_service.py:144 role level: project manager
/api/1.0/project-control-participants POST api/participant/routes/project_control_participant_route.py:59 ProjectControlParticipantService.get_control_participants_with_inherits[_by_uid] 僅 JWT POST 實為分頁查詢(讀)
/api/1.0/project-control-participant POST api/participant/routes/project_control_participant_route.py:89 ProjectControlParticipantService.add_project_control_participant 已有: assert_project_manager @ app/participant/service/project_control_participant_service.py:66 role level: project manager,scope=group_id+control_id
/api/1.0/project-control-participant PUT api/participant/routes/project_control_participant_route.py:114 ProjectControlParticipantService.update_project_control_participant 已有: assert_project_manager @ app/participant/service/project_control_participant_service.py:110 role level: project manager,scope=group_id+control_id
/api/1.0/project-control-participant DELETE api/participant/routes/project_control_participant_route.py:139 ProjectControlParticipantService.delete_project_control_participant 已有: assert_project_manager @ app/participant/service/project_control_participant_service.py:143 role level: project manager,scope=group_id+control_id
/api/1.0/control-group-participant POST api/participant/routes/control_group_participant_route.py:78 ControlGroupParticipantService.add_control_group_participant 已有: assert_project_manager @ app/participant/service/control_group_participant_service.py:87 role level: project manager,scope=group_id
/api/1.0/control-group-participant PUT api/participant/routes/control_group_participant_route.py:101 ControlGroupParticipantService.update_control_group_participant 已有: assert_project_manager @ app/participant/service/control_group_participant_service.py:126 role level: project manager,scope=group_id
/api/1.0/control-group-participant DELETE api/participant/routes/control_group_participant_route.py:124 ControlGroupParticipantService.delete_control_group_participant 已有: assert_project_manager @ app/participant/service/control_group_participant_service.py:156 role level: project manager,scope=group_id
(control-group-participants) POST api/participant/routes/control_group_participant_route.py:51 ControlGroupParticipantService.get_control_group_participants_with_inherits[_by_uid] 僅 JWT POST 實為分頁查詢(讀);路由已註解init.py:64)
(control-group-participants/menu) GET api/participant/routes/control_group_participant_route.py:27 ControlGroupParticipantService.get_control_group_participant_menu[_by_uid] 僅 JWT 讀取 menu;路由已註解init.py:65)
(process-participant) POST api/participant/routes/process_participant_route.py:57 ProcessParticipantService.add_process_participant 已有: assert_project_manager @ app/participant/service/process_participant_service.py:65 role level: project manager,control_id=process_id;路由已註解init.py:50)
(process-participant) PUT api/participant/routes/process_participant_route.py:70 ProcessParticipantService.update_process_participant 已有: assert_project_manager(經 _assert_manager_for_process)@ app/participant/service/process_participant_service.py:90→57 role level: project manager,反解 record.project_id;路由已註解
(process-participant) DELETE api/participant/routes/process_participant_route.py:86 ProcessParticipantService.delete_process_participant 已有: assert_project_manager(經 _assert_manager_for_process)@ app/participant/service/process_participant_service.py:111→57 role level: project manager;路由已註解
(process-participants) POST api/participant/routes/process_participant_route.py:40 ProcessParticipantService.get_process_participants_with_inherits 僅 JWT POST 實為分頁查詢(讀);路由已註解init.py:48)
(process-participants/menu) GET api/participant/routes/process_participant_route.py:25 ProcessParticipantService.get_process_participant_menus 僅 JWT 讀取 menu;路由已註解init.py:49)
(project-group-participant) POST api/participant/routes/project_group_participant_route.py:78 ProjectGroupParticipantService.add_project_group_participant 僅 JWT 無 assert_project_manager(service 全檔無守門,app/participant/service/project_group_participant_service.py:67-92);路由已註解init.py:72)
(project-group-participant) PUT api/participant/routes/project_group_participant_route.py:101 ProjectGroupParticipantService.update_project_group_participant 僅 JWT 無 assert_project_manager(同上 service:95-119);路由已註解
(project-group-participant) DELETE api/participant/routes/project_group_participant_route.py:124 ProjectGroupParticipantService.delete_project_group_participant 僅 JWT 無 assert_project_manager(同上 service:122-136);路由已註解
(project-group-participants) POST api/participant/routes/project_group_participant_route.py:51 ProjectGroupParticipantService.get_project_group_participants_with_inherits[_by_uid] 僅 JWT POST 實為分頁查詢(讀);路由已註解init.py:70)
(project-group-participants/menu) GET api/participant/routes/project_group_participant_route.py:27 ProjectGroupParticipantService.get_project_group_participant_menu[_by_uid] 僅 JWT 讀取 menu;路由已註解init.py:71)
/api/1.0/project-participants/menu GET api/participant/routes/project_participant_route.py:25 ProjectParticipantService.get_project_participant_menus[_by_uid] 僅 JWT 讀取 menu(live)
(project-participant/permission/<project_id>) GET api/participant/routes/project_participant_route.py:45 ProjectParticipantService.get_project_participants_permissions 僅 JWT 讀取;路由已註解init.py:43)
/api/1.0/project-control-participants/menu GET api/participant/routes/project_control_participant_route.py:28 ProjectControlParticipantService.get_control_participant_menu[_by_uid] 僅 JWT 讀取 menu(live)
(enums/participant-roles) GET api/participant/routes/participant_enum_route.py:19 無(直接 enum dump) 僅 JWT 靜態 enum;路由已註解init.py:76)
§2

api/remote_agent

重要脈絡:FR-039 File Agent registry。程式碼註解多處寫「需 admin」(EnrollToken / Revoke / Rebind 等),但追進 service 層後,所有 FE 端點皆無任何 admin / role / is_admin 守門——僅 @jwt_required()。控制面端點(/agents/register/agents/heartbeat刻意無 JWT(agent→cloud,走 token+mTLS,非 user JWT)。agent_enrollment_service.py:186ForbiddenError 是 agent status=revoked 的業務拒絕,非授權守門

Route path Method Route file:line @jwt_required? 呼叫的 service method 現況守門 備註
/api/1.0/remote-agents GET api/remote_agent/routes/remote_agent_route.py:34 RemoteAgentService.list_enabled_agents 僅 JWT 下拉清單(讀)
/api/1.0/remote-agents POST api/remote_agent/routes/remote_agent_route.py:47 RemoteAgentService.create_agent 僅 JWT 寫入;registry 新增,無 admin 守門
/api/1.0/remote-agents/all GET api/remote_agent/routes/remote_agent_route.py:88 RemoteAgentService.list_all_agents 僅 JWT 管理頁列表(讀,含停用)
/api/1.0/remote-agents/health-check POST api/remote_agent/routes/remote_agent_route.py:61 RemoteAgentService.check_health 僅 JWT 雲端打 agent /health;POST 但非寫 DB、非分頁查詢(動作型)
/api/1.0/remote-agents/<uid>/reconcile POST api/remote_agent/routes/remote_agent_route.py:74 RemoteAgentService.reconcile 僅 JWT 對帳動作(讀取/比對),無 admin 守門
/api/1.0/remote-agents/<uid> GET api/remote_agent/routes/remote_agent_route.py:102 RemoteAgentService.get_agent 僅 JWT 單筆 detail(讀)
/api/1.0/remote-agents/<uid> PUT api/remote_agent/routes/remote_agent_route.py:115 RemoteAgentService.update_agent 僅 JWT 寫入(含啟用/停用),無 admin 守門
/api/1.0/remote-agents/<uid> DELETE api/remote_agent/routes/remote_agent_route.py:128 RemoteAgentService.delete_agent 僅 JWT 寫入(刪除),無 admin 守門
/api/1.0/agents/register POST api/remote_agent/routes/remote_agent_route.py:143 AgentEnrollmentService.register 無 JWT FR-039 控制面,agent→cloud,刻意不擋(token+TLS bootstrap)
/api/1.0/agents/heartbeat POST api/remote_agent/routes/remote_agent_route.py:155 AgentEnrollmentService.heartbeat 無 JWT FR-039 控制面,agent→cloud,刻意不擋(mTLS);service:186 對 revoked agent 拋 ForbiddenError(業務拒絕非授權)
/api/1.0/agents/enroll-token GET api/remote_agent/routes/remote_agent_route.py:168 AgentEnrollTokenService.get_current 僅 JWT 讀 token 摘要;註解稱「需 admin」但 service 無 admin 守門
/api/1.0/agents/enroll-token POST api/remote_agent/routes/remote_agent_route.py:178 AgentEnrollTokenService.generate 僅 JWT 敏感 GET/寫:產生註冊 token(明文僅此次回);註解稱「需 admin」但 service 無 admin 守門
/api/1.0/agents/enroll-token/revoke POST api/remote_agent/routes/remote_agent_route.py:193 AgentEnrollTokenService.revoke 僅 JWT 停用全 tenant token;註解稱「需 admin」但 service 無 admin 守門
/api/1.0/remote-agents/<uid>/revoke POST api/remote_agent/routes/remote_agent_route.py:206 RemoteAgentService.revoke_agent 僅 JWT 撤銷 agent;註解稱「需 admin」但 service 無 admin 守門
/api/1.0/remote-agents/<uid>/unrevoke POST api/remote_agent/routes/remote_agent_route.py:220 RemoteAgentService.unrevoke_agent 僅 JWT 解除撤銷;註解稱「需 admin」但 service 無 admin 守門
/api/1.0/remote-agents/<uid>/rebind POST api/remote_agent/routes/remote_agent_route.py:235 RemoteAgentService.rebind_agent 僅 JWT 重新綁定設備指紋;註解稱「需 admin」但 service 無 admin 守門