feat: 审批/执行角色权限校验 + LDAP用户禁止重置密码

- ticket.go: ApproveTicket 前置角色检查(admin/dba/auditor),ExecuteTicket 前置角色检查(admin/dba)
- ticket.go: 创建工单时自动从 instance.workflow_id 读取关联审批流程
- UsersPage.tsx: LDAP 用户重置密码按钮置灰(disabled+opacity-30+提示文案)
- 权限模型: 审核=管理员/DBA/审核员, 执行=管理员/DBA, 提交=所有角色
This commit is contained in:
logan committed 2026-05-31 22:20:24 +08:00
1 parent b4c982d565
commit bef2f98f28
2 files changed
+38 -22

No files matched your search

+34 -19
View File
@@ -218,6 +218,14 @@ func CreateTicket(c *gin.Context) {
return return
} }
// Auto-fill workflow_id from instance if not provided
if input.WorkflowID == nil || *input.WorkflowID == 0 {
var inst model.Instance
if model.DB.First(&inst, input.InstanceID).Error == nil && inst.WorkflowID != nil {
input.WorkflowID = inst.WorkflowID
}
}
status := "pending" status := "pending"
priority := "medium" priority := "medium"
riskLevel := "low" riskLevel := "low"
@@ -348,7 +356,13 @@ func CreateTicket(c *gin.Context) {
// POST /api/v1/tickets/:id/approve // POST /api/v1/tickets/:id/approve
func ApproveTicket(c *gin.Context) { func ApproveTicket(c *gin.Context) {
id := c.Param("id") id := c.Param("id")
userID, _, _ := middleware.GetCurrentUser(c) userID, userRole, _ := middleware.GetCurrentUser(c)
// Role check: only admin/dba/auditor can approve
if userRole != "admin" && userRole != "dba" && userRole != "auditor" {
c.JSON(http.StatusForbidden, gin.H{"detail": "权限不足:审核操作需要管理员、DBA 或审核员角色"})
return
}
var ticket model.Ticket var ticket model.Ticket
if err := model.DB.First(&ticket, id).Error; err != nil { if err := model.DB.First(&ticket, id).Error; err != nil {
@@ -370,25 +384,19 @@ func ApproveTicket(c *gin.Context) {
// Update approval record — auto-create if missing (legacy tickets) // Update approval record — auto-create if missing (legacy tickets)
var approval model.TicketApproval var approval model.TicketApproval
if err := model.DB.Where("ticket_id = ? AND approver_id = ? AND action IS NULL", id, userID).First(&approval).Error; err != nil { if err := model.DB.Where("ticket_id = ? AND approver_id = ? AND action IS NULL", id, userID).First(&approval).Error; err != nil {
// No approval record — check if user is authorized to review // No approval record — authorized role can create one
_, userRole, _ := middleware.GetCurrentUser(c) stepName := "审核"
if userRole == "admin" || userRole == "dba" { stepType := "audit"
stepName := "审核" approverRole := userRole
stepType := "audit" approval = model.TicketApproval{
approverRole := userRole TicketID: ticket.ID,
approval = model.TicketApproval{ StepNumber: 1,
TicketID: ticket.ID, StepName: &stepName,
StepNumber: 1, StepType: &stepType,
StepName: &stepName, ApproverID: &userID,
StepType: &stepType, ApproverRole: &approverRole,
ApproverID: &userID,
ApproverRole: &approverRole,
}
model.DB.Create(&approval)
} else {
c.JSON(http.StatusBadRequest, gin.H{"detail": "No pending approval for you"})
return
} }
model.DB.Create(&approval)
} }
approval.Action = &input.Action approval.Action = &input.Action
@@ -421,6 +429,13 @@ func ApproveTicket(c *gin.Context) {
func ExecuteTicket(c *gin.Context) { func ExecuteTicket(c *gin.Context) {
id := c.Param("id") id := c.Param("id")
// Role check: only admin/dba can execute
_, userRole, _ := middleware.GetCurrentUser(c)
if userRole != "admin" && userRole != "dba" {
c.JSON(http.StatusForbidden, gin.H{"detail": "权限不足:执行操作需要管理员或 DBA 角色"})
return
}
var ticket model.Ticket var ticket model.Ticket
if err := model.DB.First(&ticket, id).Error; err != nil { if err := model.DB.First(&ticket, id).Error; err != nil {
c.JSON(http.StatusNotFound, gin.H{"detail": "Ticket not found"}) c.JSON(http.StatusNotFound, gin.H{"detail": "Ticket not found"})
+4 -3
View File
@@ -487,9 +487,10 @@ export default function UsersPage() {
<Edit2 className="w-4 h-4" /> <Edit2 className="w-4 h-4" />
</button> </button>
<button <button
onClick={() => setShowResetPwd(user)} onClick={() => user.auth_source !== 'ldap' && setShowResetPwd(user)}
className="p-1.5 rounded-md hover:bg-[var(--dg-bg-tertiary)] text-[var(--dg-text-muted)] hover:text-yellow-400 transition-colors" className={`p-1.5 rounded-md transition-colors ${user.auth_source === 'ldap' ? 'opacity-30 cursor-not-allowed' : 'hover:bg-[var(--dg-bg-tertiary)] text-[var(--dg-text-muted)] hover:text-yellow-400'}`}
title="重置密码" title={user.auth_source === 'ldap' ? 'LDAP 用户密码由 LDAP 服务器管理' : '重置密码'}
disabled={user.auth_source === 'ldap'}
> >
<KeyRound className="w-4 h-4" /> <KeyRound className="w-4 h-4" />
</button> </button>