mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-07-14 07:57:26 +02:00
Add files via upload
This commit is contained in:
@@ -0,0 +1,376 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"cyberstrike-ai/internal/audit"
|
||||
"cyberstrike-ai/internal/database"
|
||||
"cyberstrike-ai/internal/security"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type RBACHandler struct {
|
||||
db *database.DB
|
||||
logger *zap.Logger
|
||||
audit *audit.Service
|
||||
auth *security.AuthManager
|
||||
}
|
||||
|
||||
func NewRBACHandler(db *database.DB, logger *zap.Logger) *RBACHandler {
|
||||
return &RBACHandler{db: db, logger: logger}
|
||||
}
|
||||
|
||||
func (h *RBACHandler) SetAudit(s *audit.Service) {
|
||||
h.audit = s
|
||||
}
|
||||
|
||||
func (h *RBACHandler) SetAuthManager(m *security.AuthManager) {
|
||||
h.auth = m
|
||||
}
|
||||
|
||||
func (h *RBACHandler) Me(c *gin.Context) {
|
||||
session, _ := security.CurrentSession(c)
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"user": gin.H{
|
||||
"id": session.UserID,
|
||||
"username": session.Username,
|
||||
"display_name": session.DisplayName,
|
||||
},
|
||||
"roles": session.Roles,
|
||||
"permissions": permissionKeys(session.Permissions),
|
||||
"scope": session.Scope,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *RBACHandler) Metadata(c *gin.Context) {
|
||||
roles, err := h.db.ListRBACRoles()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
rolePermissions := map[string][]string{}
|
||||
for _, role := range roles {
|
||||
keys, _ := h.db.ListRBACRolePermissionKeys(role.ID)
|
||||
rolePermissions[role.ID] = keys
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"permissions": security.PermissionCatalog,
|
||||
"roles": roles,
|
||||
"role_permissions": rolePermissions,
|
||||
"scopes": []string{database.RBACScopeAll, database.RBACScopeAssigned, database.RBACScopeOwn},
|
||||
})
|
||||
}
|
||||
|
||||
func (h *RBACHandler) ListRoles(c *gin.Context) {
|
||||
roles, err := h.db.ListRBACRoles()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
out := make([]gin.H, 0, len(roles))
|
||||
for _, role := range roles {
|
||||
keys, _ := h.db.ListRBACRolePermissionKeys(role.ID)
|
||||
out = append(out, gin.H{
|
||||
"id": role.ID,
|
||||
"name": role.Name,
|
||||
"description": role.Description,
|
||||
"scope": role.Scope,
|
||||
"is_system": role.IsSystem,
|
||||
"permissions": keys,
|
||||
"created_at": role.CreatedAt,
|
||||
"updated_at": role.UpdatedAt,
|
||||
})
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"roles": out})
|
||||
}
|
||||
|
||||
type upsertRBACRoleRequest struct {
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name" binding:"required"`
|
||||
Description string `json:"description"`
|
||||
Scope string `json:"scope"`
|
||||
Permissions []string `json:"permissions"`
|
||||
}
|
||||
|
||||
func (h *RBACHandler) CreateRole(c *gin.Context) {
|
||||
var req upsertRBACRoleRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
role, err := h.db.UpsertRBACRole("", req.Name, req.Description, req.Scope, req.Permissions)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
if h.audit != nil {
|
||||
h.audit.RecordOK(c, "rbac", "create_role", "创建平台角色", "role", role.ID, nil)
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"role": role})
|
||||
}
|
||||
|
||||
func (h *RBACHandler) UpdateRole(c *gin.Context) {
|
||||
id := strings.TrimSpace(c.Param("id"))
|
||||
existing, err := h.db.GetRBACRoleByID(id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "角色不存在"})
|
||||
return
|
||||
}
|
||||
if existing.IsSystem {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "系统内置角色不可修改,请创建自定义角色"})
|
||||
return
|
||||
}
|
||||
var req upsertRBACRoleRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
role, err := h.db.UpsertRBACRole(id, req.Name, req.Description, req.Scope, req.Permissions)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
if existing.IsSystem {
|
||||
role.IsSystem = true
|
||||
}
|
||||
if h.audit != nil {
|
||||
h.audit.RecordOK(c, "rbac", "update_role", "更新平台角色", "role", id, nil)
|
||||
}
|
||||
if h.auth != nil {
|
||||
h.auth.RevokeAllSessions()
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"role": role})
|
||||
}
|
||||
|
||||
func (h *RBACHandler) DeleteRole(c *gin.Context) {
|
||||
id := strings.TrimSpace(c.Param("id"))
|
||||
if err := h.db.DeleteRBACRole(id); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
if h.audit != nil {
|
||||
h.audit.RecordOK(c, "rbac", "delete_role", "删除平台角色", "role", id, nil)
|
||||
}
|
||||
if h.auth != nil {
|
||||
h.auth.RevokeAllSessions()
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"success": true})
|
||||
}
|
||||
|
||||
func (h *RBACHandler) ListUsers(c *gin.Context) {
|
||||
users, err := h.db.ListRBACUsers()
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
out := make([]gin.H, 0, len(users))
|
||||
for _, user := range users {
|
||||
roleIDs, _ := h.db.ListRBACUserRoleIDs(user.ID)
|
||||
out = append(out, gin.H{
|
||||
"id": user.ID,
|
||||
"username": user.Username,
|
||||
"display_name": user.DisplayName,
|
||||
"enabled": user.Enabled,
|
||||
"is_builtin": user.IsBuiltin,
|
||||
"roles": roleIDs,
|
||||
"created_at": user.CreatedAt,
|
||||
"updated_at": user.UpdatedAt,
|
||||
})
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"users": out})
|
||||
}
|
||||
|
||||
type createRBACUserRequest struct {
|
||||
Username string `json:"username" binding:"required"`
|
||||
DisplayName string `json:"display_name"`
|
||||
Password string `json:"password" binding:"required"`
|
||||
Enabled *bool `json:"enabled"`
|
||||
Roles []string `json:"roles"`
|
||||
}
|
||||
|
||||
func (h *RBACHandler) CreateUser(c *gin.Context) {
|
||||
var req createRBACUserRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
if len(strings.TrimSpace(req.Password)) < 8 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "密码长度至少需要 8 位"})
|
||||
return
|
||||
}
|
||||
hash, err := security.HashPassword(req.Password)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
enabled := true
|
||||
if req.Enabled != nil {
|
||||
enabled = *req.Enabled
|
||||
}
|
||||
user, err := h.db.CreateRBACUser(req.Username, req.DisplayName, hash, enabled, req.Roles)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
if h.audit != nil {
|
||||
h.audit.RecordOK(c, "rbac", "create_user", "创建平台用户", "user", user.ID, map[string]interface{}{"username": user.Username})
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"user": user})
|
||||
}
|
||||
|
||||
type updateRBACUserRequest struct {
|
||||
DisplayName *string `json:"display_name"`
|
||||
Password *string `json:"password"`
|
||||
Enabled *bool `json:"enabled"`
|
||||
Roles *[]string `json:"roles"`
|
||||
}
|
||||
|
||||
func (h *RBACHandler) UpdateUser(c *gin.Context) {
|
||||
id := strings.TrimSpace(c.Param("id"))
|
||||
user, err := h.db.GetRBACUserByID(id)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "用户不存在"})
|
||||
return
|
||||
}
|
||||
var req updateRBACUserRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
displayName := user.DisplayName
|
||||
if req.DisplayName != nil {
|
||||
displayName = *req.DisplayName
|
||||
}
|
||||
if err := h.db.UpdateRBACUser(id, displayName, req.Enabled, req.Roles); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
if req.Password != nil && strings.TrimSpace(*req.Password) != "" {
|
||||
if len(strings.TrimSpace(*req.Password)) < 8 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "密码长度至少需要 8 位"})
|
||||
return
|
||||
}
|
||||
hash, err := security.HashPassword(*req.Password)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
if err := h.db.UpdateRBACUserPassword(id, hash); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
}
|
||||
if h.audit != nil {
|
||||
h.audit.RecordOK(c, "rbac", "update_user", "更新平台用户", "user", id, nil)
|
||||
}
|
||||
if h.auth != nil {
|
||||
h.auth.RevokeUserSessions(id)
|
||||
}
|
||||
updated, _ := h.db.GetRBACUserByID(id)
|
||||
c.JSON(http.StatusOK, gin.H{"user": updated})
|
||||
}
|
||||
|
||||
func (h *RBACHandler) DeleteUser(c *gin.Context) {
|
||||
id := strings.TrimSpace(c.Param("id"))
|
||||
if err := h.db.DeleteRBACUser(id); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
if h.audit != nil {
|
||||
h.audit.RecordOK(c, "rbac", "delete_user", "删除平台用户", "user", id, nil)
|
||||
}
|
||||
if h.auth != nil {
|
||||
h.auth.RevokeUserSessions(id)
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"success": true})
|
||||
}
|
||||
|
||||
type assignResourceRequest struct {
|
||||
UserID string `json:"user_id" binding:"required"`
|
||||
ResourceType string `json:"resource_type" binding:"required"`
|
||||
ResourceID string `json:"resource_id"`
|
||||
ResourceIDs []string `json:"resource_ids"`
|
||||
}
|
||||
|
||||
func (h *RBACHandler) AssignResource(c *gin.Context) {
|
||||
var req assignResourceRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
resourceIDs := append([]string(nil), req.ResourceIDs...)
|
||||
if strings.TrimSpace(req.ResourceID) != "" {
|
||||
resourceIDs = append(resourceIDs, req.ResourceID)
|
||||
}
|
||||
if len(resourceIDs) == 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "至少需要一个资源 ID"})
|
||||
return
|
||||
}
|
||||
created, err := h.db.AssignResourcesToUser(req.UserID, req.ResourceType, resourceIDs)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
if h.audit != nil {
|
||||
for _, resourceID := range resourceIDs {
|
||||
h.audit.RecordOK(c, "rbac", "assign_resource", "授权资源访问", req.ResourceType, strings.TrimSpace(resourceID), map[string]interface{}{"user_id": req.UserID})
|
||||
}
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"requested": len(resourceIDs),
|
||||
"created": created,
|
||||
"skipped": int64(len(resourceIDs)) - created,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *RBACHandler) ListResourceAssignments(c *gin.Context) {
|
||||
rows, err := h.db.ListRBACResourceAssignments(c.Query("user_id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"assignments": rows})
|
||||
}
|
||||
|
||||
func (h *RBACHandler) ListAssignableResources(c *gin.Context) {
|
||||
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "50"))
|
||||
if limit <= 0 || limit > 50 {
|
||||
limit = 50
|
||||
}
|
||||
offset, _ := strconv.Atoi(c.DefaultQuery("offset", "0"))
|
||||
if offset < 0 {
|
||||
offset = 0
|
||||
}
|
||||
resources, err := h.db.ListAssignableRBACResourcesPage(c.Query("type"), c.Query("q"), limit+1, offset)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
hasMore := len(resources) > limit
|
||||
if hasMore {
|
||||
resources = resources[:limit]
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"resources": resources,
|
||||
"has_more": hasMore,
|
||||
"limit": limit,
|
||||
"offset": offset,
|
||||
})
|
||||
}
|
||||
|
||||
func (h *RBACHandler) DeleteResourceAssignment(c *gin.Context) {
|
||||
id := strings.TrimSpace(c.Param("id"))
|
||||
if err := h.db.DeleteRBACResourceAssignment(id); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
if h.audit != nil {
|
||||
h.audit.RecordOK(c, "rbac", "delete_resource_assignment", "撤销资源授权", "resource_assignment", id, nil)
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"success": true})
|
||||
}
|
||||
Reference in New Issue
Block a user