Files
Agent-System/internal/projectinstance/duty_action.go
T
weichenandCursor f02600d75c fix(projectinstance): resolve author_only duties in ABAC gates
Use ResolveDocTaskDuty so normalized author-only plans (author ID only)
are treated as full A/A/A for access, stage visibility, and readiness.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-17 13:15:05 +00:00

109 lines
2.9 KiB
Go

package projectinstance
const (
DutyRoleAuthor = "author"
DutyRoleReviewer = "reviewer"
DutyRoleApprover = "approver"
AccessCodeNotActivated = "WORKFLOW_NOT_ACTIVATED"
GovernanceBlockedHint = "当前项目尚未激活工作流,文档任务不会启动。请先在设置中配置并激活至少一份纳入人工审批的文档。"
)
// requiredDutyRole maps a document action to the single duty that may perform it.
func requiredDutyRole(action string) string {
switch action {
case DocAccessActionRun, DocAccessActionEdit, DocAccessActionSubmit:
return DutyRoleAuthor
case DocAccessActionReview:
return DutyRoleReviewer
case DocAccessActionApprove:
return DutyRoleApprover
default:
return ""
}
}
func userHoldsDuty(d DocDuty, userID int64, role string) bool {
if userID <= 0 || role == "" {
return false
}
switch role {
case DutyRoleAuthor:
return d.AuthorUserID == userID
case DutyRoleReviewer:
return d.ReviewerUserID == userID
case DutyRoleApprover:
return d.ApproverUserID == userID
default:
return false
}
}
// classifyAccess applies the locked duty-action matrix. govLive=false is pre-activation.
func classifyAccess(dt DocTaskPlan, action string, userID int64, isCreator bool, govLive bool) DocAccessDecision {
allow := DocAccessDecision{Allowed: true}
if action == DocAccessActionView {
return allow
}
if !govLive {
if action == DocAccessActionRun {
return DocAccessDecision{
Allowed: true,
Code: AccessCodeNotActivated,
Message: GovernanceBlockedHint,
}
}
return DocAccessDecision{
Allowed: false,
Code: AccessCodeNotActivated,
Message: GovernanceBlockedHint,
}
}
if !dt.Enabled {
return DocAccessDecision{Allowed: false, Code: AccessCodeDocTaskDisabled, Message: "该文档已被创建者禁用"}
}
mode, effective, _ := ResolveDocTaskDuty(dt)
switch mode {
case DutyModeNone:
switch action {
case DocAccessActionRun, DocAccessActionEdit:
if isCreator {
return allow
}
return DocAccessDecision{
Allowed: false,
Code: AccessCodeForbiddenDuty,
Message: "未纳入人工审批的文档仅创建者可编辑或执行",
}
case DocAccessActionReview, DocAccessActionApprove, DocAccessActionSubmit:
return DocAccessDecision{
Allowed: false,
Code: AccessCodeDutyNotManaged,
Message: "该文档未纳入人工审批,不能审核、批准或提交审批",
}
}
case DutyModePartial:
return DocAccessDecision{
Allowed: false,
Code: AccessCodeForbiddenDuty,
Message: "文档职责未完整指派,不能执行该动作",
}
case DutyModeFull:
role := requiredDutyRole(action)
if userHoldsDuty(effective, userID, role) {
return allow
}
return DocAccessDecision{
Allowed: false,
Code: AccessCodeForbiddenDuty,
Message: "当前用户不是该文档的对应职责人",
}
}
return DocAccessDecision{
Allowed: false,
Code: AccessCodeForbiddenDuty,
Message: "当前用户不是该文档职责人",
}
}