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:
1 parent
b4c982d565
commit
bef2f98f28
2 files changed
+38
-22
No files matched your search
@@ -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"})
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in new issue
Block a user