- 1.1(核对):registry 默认两布尔 false 已一致,零代码改动(设计文档已按裁决改写) - 1.3 + 10.4.2:新增迁移 0012_tool_policy(tool_policies 表,表名复数 + idx_tool_policies_name); 新增 registry.PolicyStore/SQLitePolicyStore,启动 Load 覆盖注册初值、SetPolicy 先落库后改内存 (落盘失败保持旧值并报错);面板保存路径不变但从此重启仍在 - 13.4.3:代审真接线 —— restrict.SensitiveCall 结构化判定(terminal / file write / http POST)、 restrict.Gate/Reviewer/Resolver(无代审者或代审报错一律 fail-closed 拦截), 组合根装进 loop.Options.Hooks 的 CallAdmit;gateway.RecordApproval 落 audit_logs (who=角色名 / what=approval.<工具> / result=granted|denied)并广播 approval_notice 帧 - 前端:代审小字「xxx 想要干什么,我审查后批准了」进聊天流顶部(帧→store→组件,文案单一来源 复用 audit.Approval.Explain()),附纯 node 逻辑测试 - 复核:迁移在 data/cyrene.db **副本**上 up/down/up 各一次通过;TestRegistry_PolicyStoreSurvivesRestart 与 TestAdminSetPolicySurvivesRestart 证明「重启后策略仍在」;terminal 触发代审 + 审计有行有端到端断言
71 lines
2.8 KiB
Go
71 lines
2.8 KiB
Go
package gateway
|
||
|
||
import (
|
||
"context"
|
||
"time"
|
||
|
||
"git.yeij.top/AskaEth/Cyrene_v2/internal/capability/restrict"
|
||
)
|
||
|
||
// 本文件是 design/11 §4「敏感操作代审」的对外侧实现(裁决 13.4.3):
|
||
// loop 的工具准入钩子判定出「该敏感操作已获(或未获)代审」后,组合根
|
||
// 把记录交给这里——① 落审计链 audit_logs(who/what/when/result),
|
||
// ② 向 owner/admin 连接广播小字帧。判定与代审本身在
|
||
// internal/capability/restrict(Gate);本文件只管「记录 + 呈现」。
|
||
|
||
// wsApprovalNotice 是主代理代审小字的 WS 事件类型(design/11 §4)。
|
||
// 它携带内容(角色名、动作、文案),因此 design/11 §7.1 的帧门默认
|
||
// fail-closed:只投递给 owner + admin 连接,viewer 收不到,也不进那 7 个
|
||
// 状态机信号白名单。
|
||
const wsApprovalNotice = "approval_notice"
|
||
|
||
// 代审在审计链里的 who/what/result 口径(design/11 §8):
|
||
// who = 想动手的角色名,what = "approval.<工具名>",result = granted|denied。
|
||
// 代审动作批准/拒绝**各落一行**,因此前缀固定、结果二分。
|
||
const (
|
||
approvalAuditPrefix = "approval."
|
||
approvalResultGrant = "granted"
|
||
approvalResultDeny = "denied"
|
||
)
|
||
|
||
// RecordApproval 记录一次主代理代审(design/11 §4 §8;裁决 13.4.3)。
|
||
//
|
||
// ① 审计:把 who(角色名)/ what(approval.<工具名>)/ when / result
|
||
// (granted / denied)追加进 audit_logs —— 代审动作批准与拒绝各落一行,
|
||
// 供管理面板「可观测性 → 审计」与通知 feed 查询。
|
||
// ② 前端小字:广播 approval_notice 帧,携带 design/11 §4 的固定文案
|
||
// 「xxx(角色名) 想要干什么,我审查后批准了」(explain),以及工具名、
|
||
// 操作类别与动作摘要(detail,客户端可作副文本或忽略)。
|
||
//
|
||
// 审计写入用 context.WithoutCancel:代审发生在一次 Run 内部,而 Run 的 ctx
|
||
// 可能已被取消(用户中断)——已经发生的代审仍然必须留痕。审计存储坏掉时
|
||
// gateway.audit 只记一条日志,不影响代审已经成立的结论。
|
||
//
|
||
// 方法对 nil 接收者安全(组合根在网关构建完成前调用它时不会 panic)。
|
||
func (s *Server) RecordApproval(ctx context.Context, ev restrict.ApprovalEvent) {
|
||
if s == nil {
|
||
return
|
||
}
|
||
if ctx == nil {
|
||
ctx = context.Background()
|
||
}
|
||
result := approvalResultDeny
|
||
if ev.Allowed {
|
||
result = approvalResultGrant
|
||
}
|
||
s.audit(context.WithoutCancel(ctx), ev.Actor, approvalAuditPrefix+ev.Tool, result)
|
||
s.hub.broadcast(wsEvent{
|
||
Type: wsApprovalNotice,
|
||
SessionID: ev.SessionID,
|
||
Data: map[string]any{
|
||
"actor": ev.Actor,
|
||
"tool": ev.Tool,
|
||
"op": string(ev.Op),
|
||
"detail": ev.Detail,
|
||
"explain": ev.Explain,
|
||
"allowed": ev.Allowed,
|
||
"at": ev.At.UTC().Format(time.RFC3339Nano),
|
||
},
|
||
})
|
||
}
|