This commit is contained in:
Yvan
2026-08-18 15:04:07 +08:00
parent 10ccc005be
commit 1ef8ecb7b2
42 changed files with 3027 additions and 296 deletions
+53 -21
View File
@@ -2,6 +2,7 @@ package data
import (
"database/sql"
"errors"
"log/slog"
"reflect"
"strings"
@@ -18,6 +19,11 @@ import (
// Casbin, while ownership columns on business tables are row-level scope.
type dataScopeAuditEnqueue func(dataAccessLogPO)
var (
errDataScopeRequired = errors.New("受控表访问缺少数据权限上下文")
errInvalidDataScope = errors.New("数据权限范围不合法")
)
func registerDataScopeCallbacks(db *gorm.DB, enqueue dataScopeAuditEnqueue) {
if db == nil {
return
@@ -40,7 +46,7 @@ func registerDataScopeCallbacks(db *gorm.DB, enqueue dataScopeAuditEnqueue) {
}
c := db.Callback().Create()
if c.Get("data_scope:stamp") == nil {
_ = c.Before("gorm:create").Register("data_scope:stamp", stampOwnership)
_ = c.Before("gorm:create").Register("data_scope:stamp", stampOwnership(enqueue))
}
}
@@ -52,6 +58,16 @@ func isControlledTable(db *gorm.DB) bool {
return db.Statement.Schema != nil && !strings.HasPrefix(db.Statement.Table, "sys_") && (hasScopeField(db, "dept_id") || hasScopeField(db, "created_by"))
}
func skipDataScope(db *gorm.DB) bool {
skip, ok := db.Get("data_scope:skip")
value, _ := skip.(bool)
return ok && value
}
func validDataScope(scope biz.DataScope) bool {
return scope.UserID != 0 && scope.AuthorityID != 0 && scope.Scope >= 1 && scope.Scope <= 5 && scope.All == (scope.Scope == 1)
}
func applyDataScope(operation string, enqueue dataScopeAuditEnqueue) func(*gorm.DB) {
return func(db *gorm.DB) {
if !isControlledTable(db) {
@@ -60,15 +76,19 @@ func applyDataScope(operation string, enqueue dataScopeAuditEnqueue) func(*gorm.
if _, done := db.Statement.Clauses["data_scope:applied"]; done {
return
}
if skip, ok := db.Get("data_scope:skip"); ok {
if value, _ := skip.(bool); value {
return
}
if skipDataScope(db) {
return
}
scope, ok := biz.DataScopeFromContext(db.Statement.Context)
if !ok {
slog.WarnContext(db.Statement.Context, "数据权限: 业务表访问无身份上下文, 已放行(待补 ctx / 或使用系统上下文)", "mod", "data-scope", "table", db.Statement.Table)
recordDataScopeEvent(db, enqueue, "no_identity", operation, "无身份上下文访问受控表, 已放行", biz.DataScope{})
slog.WarnContext(db.Statement.Context, "数据权限: 业务表访问无身份上下文, 已拒绝", "mod", "data-scope", "table", db.Statement.Table)
recordDataScopeEvent(db, enqueue, "no_identity", operation, "无身份上下文访问受控表, 已拒绝", biz.DataScope{})
_ = db.AddError(errDataScopeRequired)
return
}
if !validDataScope(scope) {
recordDataScopeEvent(db, enqueue, "invalid_scope", operation, "数据权限上下文不合法, 已拒绝", scope)
_ = db.AddError(errInvalidDataScope)
return
}
if (operation == "update" || operation == "delete") && !db.AllowGlobalUpdate && !hasWriteConditions(db) {
@@ -86,10 +106,13 @@ func applyDataScope(operation string, enqueue dataScopeAuditEnqueue) func(*gorm.
if hasScopeField(db, "dept_id") {
ids := scope.DepartmentIDs
if len(ids) == 0 {
ids = []uint{0}
db.Where("1 = 0")
return
}
db.Where(table+".dept_id IN ?", ids)
return
}
db.Where("1 = 0")
}
}
@@ -111,19 +134,28 @@ func auditBlockedWrite(operation string, enqueue dataScopeAuditEnqueue) func(*go
}
}
func stampOwnership(db *gorm.DB) {
if !isControlledTable(db) {
return
}
scope, ok := biz.DataScopeFromContext(db.Statement.Context)
if !ok {
return
}
if hasScopeField(db, "created_by") && scope.UserID != 0 {
db.Statement.SetColumn("created_by", scope.UserID, true)
}
if hasScopeField(db, "dept_id") && scope.PrimaryDeptID != 0 {
db.Statement.SetColumn("dept_id", scope.PrimaryDeptID, true)
func stampOwnership(enqueue dataScopeAuditEnqueue) func(*gorm.DB) {
return func(db *gorm.DB) {
if !isControlledTable(db) || skipDataScope(db) {
return
}
scope, ok := biz.DataScopeFromContext(db.Statement.Context)
if !ok {
recordDataScopeEvent(db, enqueue, "no_identity", "create", "无身份上下文访问受控表, 已拒绝", biz.DataScope{})
_ = db.AddError(errDataScopeRequired)
return
}
if !validDataScope(scope) {
recordDataScopeEvent(db, enqueue, "invalid_scope", "create", "数据权限上下文不合法, 已拒绝", scope)
_ = db.AddError(errInvalidDataScope)
return
}
if hasScopeField(db, "created_by") {
db.Statement.SetColumn("created_by", scope.UserID, true)
}
if hasScopeField(db, "dept_id") {
db.Statement.SetColumn("dept_id", scope.PrimaryDeptID, true)
}
}
}