完善角色权限与数据隔离

This commit is contained in:
yml2213
2026-07-26 14:00:50 +08:00
parent b483cbc26d
commit 16d9ab0e5e
38 changed files with 2847 additions and 292 deletions
+101 -9
View File
@@ -97,6 +97,90 @@ func (h *Hub) Run() {
}
}
// DisconnectUser 断开指定用户的所有 WebSocket 连接并推送通知。
func (h *Hub) DisconnectUser(userID uint, message string) {
h.mu.Lock()
defer h.mu.Unlock()
for client := range h.clients {
if client.UserID == userID {
if message != "" {
payload, err := NewEvent("kicked", 0, map[string]string{"message": message})
if err == nil {
h.send(client, payload)
}
}
delete(h.clients, client)
close(client.Send)
if client.Conn != nil {
client.Conn.Close()
}
}
}
}
func roleHasPermission(tenantID uint, role, code string) bool {
var roleRecord model.Role
if err := model.DB.Where("tenant_id = ? AND code = ?", tenantID, role).First(&roleRecord).Error; err != nil {
for _, permissionCode := range model.BuiltinRolePermissionCodes()[role] {
if permissionCode == code {
return true
}
}
return false
}
var count int64
return model.DB.Table("role_permissions rp").
Joins("JOIN permissions p ON p.id = rp.permission_id").
Where("rp.role_id = ? AND p.code = ?", roleRecord.ID, code).
Count(&count).Error == nil && count > 0
}
func roleDataScope(tenantID uint, role, module string) string {
defaultScope := model.DefaultRoleDataScopes(role)[module]
var roleRecord model.Role
if err := model.DB.Where("tenant_id = ? AND code = ?", tenantID, role).First(&roleRecord).Error; err != nil {
return defaultScope
}
var scope model.RoleDataScope
if err := model.DB.Where("role_id = ? AND module = ?", roleRecord.ID, module).First(&scope).Error; err != nil {
return defaultScope
}
return scope.Scope
}
// canReceiveSessionEvent 在每次推送前重新校验账号、角色权限和会话数据范围。
func canReceiveSessionEvent(client *Client, session *model.Session) bool {
if client.Kind != "agent" {
return false
}
// 单元测试未初始化数据库时保留原有内置角色判定;生产环境始终走实时数据库校验。
if model.DB == nil {
if client.Role == "admin" || client.Role == "supervisor" {
return true
}
return session == nil || session.Status == "waiting" ||
(session.AgentID != nil && client.Role == "agent" && *session.AgentID == client.UserID)
}
var user model.User
if err := model.DB.Select("id", "tenant_id", "role", "status").First(&user, client.UserID).Error; err != nil ||
user.TenantID != client.TenantID || user.Status == "disabled" {
return false
}
permissionCode := "session.view"
module := "session"
if session != nil && (session.Status == "ended" || session.Status == "archived") {
permissionCode = "chat_history.view"
module = "chat_history"
}
if !roleHasPermission(user.TenantID, user.Role, permissionCode) {
return false
}
if session == nil || roleDataScope(user.TenantID, user.Role, module) == model.DataScopeAll {
return true
}
return session.Status == "waiting" || (session.AgentID != nil && *session.AgentID == user.ID)
}
// Stats 返回当前连接统计(总连接 / 坐席 / 访客)。
func (h *Hub) Stats() (total, agents, visitors int) {
h.mu.RLock()
@@ -134,8 +218,9 @@ func (h *Hub) BroadcastToSession(tenantID, sessionID uint, agentID *uint, messag
}
continue
}
if client.Role == "admin" || client.Role == "supervisor" ||
(agentID != nil && client.Role == "agent" && client.UserID == *agentID) {
if canReceiveSessionEvent(client, &model.Session{
TenantID: tenantID, ID: sessionID, AgentID: agentID, Status: "active",
}) {
h.send(client, message)
}
}
@@ -146,8 +231,19 @@ func (h *Hub) BroadcastToTenantStaff(tenantID uint, message []byte) {
h.mu.RLock()
defer h.mu.RUnlock()
var event Event
_ = json.Unmarshal(message, &event)
var session *model.Session
if event.SessionID != 0 {
var current model.Session
if err := model.DB.Where("id = ? AND tenant_id = ?", event.SessionID, tenantID).First(&current).Error; err == nil {
session = &current
} else {
session = &model.Session{TenantID: tenantID, ID: event.SessionID, Status: "active"}
}
}
for client := range h.clients {
if client.TenantID == tenantID && client.Kind == "agent" {
if client.TenantID == tenantID && canReceiveSessionEvent(client, session) {
h.send(client, message)
}
}
@@ -173,8 +269,7 @@ func (h *Hub) BroadcastToSessionStaff(tenantID uint, agentID *uint, message []by
if client.TenantID != tenantID || client.Kind != "agent" {
continue
}
if client.Role == "admin" || client.Role == "supervisor" ||
(agentID != nil && client.Role == "agent" && client.UserID == *agentID) {
if canReceiveSessionEvent(client, &model.Session{TenantID: tenantID, AgentID: agentID, Status: "active"}) {
h.send(client, message)
}
}
@@ -247,10 +342,7 @@ func handleClientEvent(client *Client, event ClientEvent) {
if client.Kind != "agent" || event.Type != "typing" {
return
}
if client.Role == "agent" && (session.AgentID == nil || *session.AgentID != client.UserID) {
return
}
if client.Role != "agent" && client.Role != "admin" && client.Role != "supervisor" {
if !canReceiveSessionEvent(client, &session) {
return
}
payload, err := NewEvent("typing", session.ID, map[string]string{"from": "agent"})