// Package permission - composite_handler.go 实现多处理器叠加的 CompositeHandler. // // 宪法第 8 条「叠加而非替换」在权限层的实现. // 多个权限处理器可以共存:CLI 弹框决策 + 审计日志记录 + WebHook 通知. // // 使用示例: // // cliHandler := func(ctx context.Context, req *Request) (*Response, error) { // // 终端弹框,等待用户输入 // return &Response{Decision: DecisionAllow, Reason: "user approved"}, nil // } // auditHandler := func(ctx context.Context, req *Request) (*Response, error) { // // 记审计日志(不参与决策) // log.Printf("permission request: %s", req.ToolName) // return nil, nil // } // combined := NewCompositeHandler( // NamedHandler{Name: "cli", Handler: cliHandler, IsDecisionMaker: true}, // NamedHandler{Name: "audit", Handler: auditHandler, IsDecisionMaker: false}, // ) // // 权限请求同时弹框给用户 + 记录审计日志 // // 执行策略: // - 所有 Handler 都会被调用(审计需要) // - 第一个返回 Allow/Deny 的决策者 Handler 决定最终结果 // - 观察者 Handler 仍然执行,但不影响决策 // - 如果所有决策者都返回 nil/error,默认 Deny // // 升华改进(ELEVATED): 多渠道权限处理.CLI,HTTP,Hook,审计可以同时工作. // 替代方案:单个 Handler(原始设计,只支持一种交互方式). package permission import ( "context" "sync" ) // NamedHandler 是带名称和角色标记的权限处理器. // // 使用示例: // // nh := NamedHandler{ // Name: "cli-prompt", // Handler: myCliHandler, // IsDecisionMaker: true, // 参与决策 // } // // audit := NamedHandler{ // Name: "audit-log", // Handler: myAuditHandler, // IsDecisionMaker: false, // 仅观察,不参与决策 // } type NamedHandler struct { // Name 处理器名称(用于日志和动态移除) Name string // Handler 实际的权限处理函数 Handler Handler // IsDecisionMaker 是否参与决策. // true: 该处理器的响应可以决定最终结果(如 CLI 弹框) // false: 仅观察/审计,响应被忽略但仍然执行 IsDecisionMaker bool } // CompositeHandler 将多个权限处理器叠加. // // 使用示例: // // ch := NewCompositeHandler( // NamedHandler{Name: "cli", Handler: cliHandler, IsDecisionMaker: true}, // NamedHandler{Name: "webhook", Handler: webhookHandler, IsDecisionMaker: false}, // ) // // // 作为 permission.Handler 使用: // engine := permission.NewEngine(permission.ModeDefault, ch.Handle) type CompositeHandler struct { mu sync.RWMutex handlers []NamedHandler } // NewCompositeHandler 创建叠加处理器. // // 使用示例: // // // 单处理器退化--行为与直接使用该 Handler 一致 // ch := NewCompositeHandler(NamedHandler{ // Name: "cli", Handler: myHandler, IsDecisionMaker: true, // }) // // // 多处理器叠加--CLI 决策 + 审计日志 // ch := NewCompositeHandler( // NamedHandler{Name: "cli", Handler: cliHandler, IsDecisionMaker: true}, // NamedHandler{Name: "audit", Handler: auditHandler, IsDecisionMaker: false}, // ) func NewCompositeHandler(handlers ...NamedHandler) *CompositeHandler { ch := &CompositeHandler{ handlers: make([]NamedHandler, 0, len(handlers)), } for _, h := range handlers { if h.Handler != nil { ch.handlers = append(ch.handlers, h) } } return ch } // Handle 执行所有处理器并返回决策结果. // 实现 permission.Handler 签名,可直接传给 NewEngine. // // 精妙之处(CLEVER): 分离决策者和观察者. // 决策者(CLI 弹框)的响应决定最终结果. // 观察者(审计日志)的响应被忽略但仍然执行. // 这样审计永远不会被跳过,即使决策已经做出. func (ch *CompositeHandler) Handle(ctx context.Context, req *Request) (*Response, error) { ch.mu.RLock() handlers := make([]NamedHandler, len(ch.handlers)) copy(handlers, ch.handlers) ch.mu.RUnlock() var decision *Response for _, h := range handlers { resp, err := h.Handler(ctx, req) // 精妙之处(CLEVER): 分离决策者和观察者. // 决策者(CLI 弹框)的响应决定最终结果. // 观察者(审计日志)的响应被忽略但仍然执行. // 这样审计永远不会被跳过,即使决策已经做出. if h.IsDecisionMaker && decision == nil && err == nil && resp != nil { decision = resp } // 观察者的 error 不中断流程(审计失败不应阻止工具执行) } if decision == nil { return &Response{ Decision: DecisionDeny, Reason: "no handler provided a decision", }, nil } return decision, nil } // Add 动态添加处理器(运行时扩展). // // 使用示例: // // ch.Add(NamedHandler{Name: "webhook", Handler: webhookFn, IsDecisionMaker: false}) // // Add 动态添加处理器. // 如果已有同名处理器,替换(upsert 语义). func (ch *CompositeHandler) Add(h NamedHandler) { if h.Handler == nil { return } ch.mu.Lock() defer ch.mu.Unlock() // upsert:同名替换 for i, existing := range ch.handlers { if existing.Name == h.Name { ch.handlers[i] = h return } } ch.handlers = append(ch.handlers, h) } // Remove 按名称移除处理器.返回是否成功移除. // // 使用示例: // // ch.Remove("webhook") func (ch *CompositeHandler) Remove(name string) bool { ch.mu.Lock() defer ch.mu.Unlock() for i, h := range ch.handlers { if h.Name == name { ch.handlers = append(ch.handlers[:i], ch.handlers[i+1:]...) return true } } return false } // Len 返回处理器数量. func (ch *CompositeHandler) Len() int { ch.mu.RLock() defer ch.mu.RUnlock() return len(ch.handlers) }