Files
digital-psychology/apps/api/internal/middleware/admin_permission.go
T
jackyu66gitandCursor 62cd8c45dd chore: 合入 stash Ops hardening 与 migration 000041
Ask/catalog 权限与审计加固、量表读权限统一,以及未提交的 ops hardening 变更。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-13 01:46:34 +08:00

57 lines
1.6 KiB
Go

package middleware
import (
"context"
"net/http"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/yuxingu/digital-psychology/apps/api/pkg/response"
)
// AdminPermissionChecker validates admin permission codes.
type AdminPermissionChecker interface {
HasPermission(ctx context.Context, adminID uuid.UUID, code string) (bool, error)
DenyPermission(ctx context.Context, adminID uuid.UUID, code, path string)
}
// RequireAdminPermission aborts with 403 when the admin lacks code.
func RequireAdminPermission(checker AdminPermissionChecker, code string) gin.HandlerFunc {
return RequireAnyAdminPermission(checker, code)
}
// RequireAnyAdminPermission aborts with 403 when the admin lacks all of codes.
func RequireAnyAdminPermission(checker AdminPermissionChecker, codes ...string) gin.HandlerFunc {
return func(c *gin.Context) {
adminID, ok := AdminIDFromContext(c)
if !ok {
response.Fail(c, http.StatusUnauthorized, 40102, "admin session invalid")
c.Abort()
return
}
if len(codes) == 0 {
response.Fail(c, http.StatusInternalServerError, 50000, "permission check failed")
c.Abort()
return
}
var lastCode string
for _, code := range codes {
lastCode = code
okPerm, err := checker.HasPermission(c.Request.Context(), adminID, code)
if err != nil {
response.Fail(c, http.StatusInternalServerError, 50000, "permission check failed")
c.Abort()
return
}
if okPerm {
c.Next()
return
}
}
checker.DenyPermission(c.Request.Context(), adminID, lastCode, c.FullPath())
response.Fail(c, http.StatusForbidden, 40301, "forbidden")
c.Abort()
}
}