383 lines
13 KiB
Go
383 lines
13 KiB
Go
package data
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"kra/internal/biz"
|
|
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
type authorityAccessRepo struct{ data *Data }
|
|
|
|
func NewAuthorityAccessRepo(data *Data) biz.AuthorityAccessRepo {
|
|
return &authorityAccessRepo{data: data}
|
|
}
|
|
|
|
func (r *authorityAccessRepo) strictAuthorityIDs(ctx context.Context, actorID uint) (map[uint]bool, error) {
|
|
allowed := make(map[uint]bool)
|
|
if actorID == 0 {
|
|
return allowed, nil
|
|
}
|
|
var actor authorityPO
|
|
if err := r.data.gormDB.WithContext(ctx).Where("authority_id = ?", actorID).First(&actor).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
var authorities []authorityPO
|
|
if err := r.data.gormDB.WithContext(ctx).Find(&authorities).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
children := make(map[uint][]uint)
|
|
for _, authority := range authorities {
|
|
if authority.ParentID != nil {
|
|
children[*authority.ParentID] = append(children[*authority.ParentID], authority.AuthorityID)
|
|
}
|
|
}
|
|
var walk func(uint)
|
|
walk = func(id uint) {
|
|
for _, child := range children[id] {
|
|
if !allowed[child] {
|
|
allowed[child] = true
|
|
walk(child)
|
|
}
|
|
}
|
|
}
|
|
if actor.ParentID == nil || *actor.ParentID == 0 {
|
|
allowed[actorID] = true
|
|
}
|
|
walk(actorID)
|
|
return allowed, nil
|
|
}
|
|
|
|
func (r *authorityAccessRepo) checkAuthorityIDAuth(ctx context.Context, targetID uint) error {
|
|
config := r.data.runtime.Admin()
|
|
if config == nil || config.System == nil || !config.System.UseStrictAuth {
|
|
return nil
|
|
}
|
|
actor, ok := biz.ActorFromContext(ctx)
|
|
if !ok {
|
|
return errors.New("您提交的角色ID不合法")
|
|
}
|
|
allowed, err := r.strictAuthorityIDs(ctx, actor.AuthorityID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !allowed[targetID] {
|
|
return errors.New("您提交的角色ID不合法")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r *authorityAccessRepo) CreateAuthority(ctx context.Context, value *biz.Authority) error {
|
|
config := r.data.runtime.Admin()
|
|
if config != nil && config.System != nil && config.System.UseStrictAuth && (value.ParentID == nil || *value.ParentID == 0) {
|
|
if actor, ok := biz.ActorFromContext(ctx); ok {
|
|
value.ParentID = &actor.AuthorityID
|
|
}
|
|
}
|
|
return r.data.gormDB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
|
var count int64
|
|
if err := tx.Model(&authorityPO{}).Where("authority_id = ?", value.AuthorityID).Count(&count).Error; err != nil {
|
|
return err
|
|
}
|
|
if count > 0 {
|
|
return errors.New("存在相同角色id")
|
|
}
|
|
if value.DefaultRouter == "" {
|
|
value.DefaultRouter = "dashboard"
|
|
}
|
|
if err := tx.Create(&authorityPO{AuthorityID: value.AuthorityID, AuthorityName: value.AuthorityName, ParentID: value.ParentID, DataScope: value.DataScope, DefaultRouter: value.DefaultRouter}).Error; err != nil {
|
|
return err
|
|
}
|
|
var dashboard menuPO
|
|
if err := tx.Where("name = ?", "dashboard").First(&dashboard).Error; err == nil {
|
|
if err = tx.Create(&authorityMenuPO{SysAuthorityAuthorityID: value.AuthorityID, SysBaseMenuID: dashboard.ID}).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
defaults := []struct{ path, method string }{{"/menu/getMenu", "POST"}, {"/jwt/jsonInBlacklist", "POST"}, {"/base/login", "POST"}, {"/user/changePassword", "POST"}, {"/user/setUserAuthority", "POST"}, {"/user/getUserInfo", "GET"}, {"/user/setSelfInfo", "PUT"}, {"/fileUploadAndDownload/upload", "POST"}, {"/sysDictionary/findSysDictionary", "GET"}}
|
|
for _, item := range defaults {
|
|
var api apiPO
|
|
if err := tx.Where("path = ? AND method = ?", item.path, item.method).First(&api).Error; err == nil {
|
|
if err = tx.Create(&authorityAPIPO{AuthorityID: value.AuthorityID, APIID: api.ID}).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
}
|
|
func (r *authorityAccessRepo) CopyAuthority(ctx context.Context, sourceID uint, value *biz.Authority) error {
|
|
return r.data.gormDB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
|
config := r.data.runtime.Admin()
|
|
actor, hasActor := biz.ActorFromContext(ctx)
|
|
strict := hasActor && actor.AuthorityID != 888 && config != nil && config.System != nil && config.System.UseStrictAuth
|
|
if strict {
|
|
allowed, err := r.strictAuthorityIDs(ctx, actor.AuthorityID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if value.ParentID == nil || !allowed[*value.ParentID] {
|
|
return errors.New("严格角色模式下只能在自己的角色树中复制角色")
|
|
}
|
|
}
|
|
var count int64
|
|
if err := tx.Model(&authorityPO{}).Where("authority_id = ?", value.AuthorityID).Count(&count).Error; err != nil {
|
|
return err
|
|
}
|
|
if count != 0 {
|
|
return errors.New("存在相同角色id")
|
|
}
|
|
if err := tx.Create(&authorityPO{AuthorityID: value.AuthorityID, AuthorityName: value.AuthorityName, ParentID: value.ParentID, DataScope: value.DataScope, DefaultRouter: value.DefaultRouter}).Error; err != nil {
|
|
return err
|
|
}
|
|
copyLinks := func(table string, destination any, columns map[string]any) error {
|
|
return tx.Table(table).Where(columns).Find(destination).Error
|
|
}
|
|
var menus []authorityMenuPO
|
|
if err := copyLinks("sys_authority_menus", &menus, map[string]any{"sys_authority_authority_id": sourceID}); err != nil {
|
|
return err
|
|
}
|
|
for i := range menus {
|
|
menus[i].SysAuthorityAuthorityID = value.AuthorityID
|
|
}
|
|
if len(menus) > 0 {
|
|
if err := tx.Create(&menus).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
var apis []authorityAPIPO
|
|
if err := copyLinks("sys_authority_apis", &apis, map[string]any{"authority_id": sourceID}); err != nil {
|
|
return err
|
|
}
|
|
for i := range apis {
|
|
if strict {
|
|
var allowedCount int64
|
|
if err := tx.Model(&authorityAPIPO{}).Where("authority_id = ? AND api_id = ?", actor.AuthorityID, apis[i].APIID).Count(&allowedCount).Error; err != nil {
|
|
return err
|
|
}
|
|
if allowedCount == 0 {
|
|
return errors.New("严格角色模式下不能复制当前角色未拥有的 API")
|
|
}
|
|
}
|
|
apis[i].AuthorityID = value.AuthorityID
|
|
}
|
|
if len(apis) > 0 {
|
|
if err := tx.Create(&apis).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
var buttons []authorityButtonPO
|
|
if err := copyLinks("sys_authority_btns", &buttons, map[string]any{"authority_id": sourceID}); err != nil {
|
|
return err
|
|
}
|
|
for i := range buttons {
|
|
buttons[i].AuthorityID = value.AuthorityID
|
|
}
|
|
if len(buttons) > 0 {
|
|
if err := tx.Create(&buttons).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
}
|
|
func (r *authorityAccessRepo) UpdateAuthority(ctx context.Context, value *biz.Authority) error {
|
|
db := r.data.gormDB.WithContext(ctx)
|
|
var current authorityPO
|
|
if err := db.Where("authority_id = ?", value.AuthorityID).First(¤t).Error; err != nil {
|
|
return errors.New("查询角色数据失败")
|
|
}
|
|
updates := &authorityPO{AuthorityName: value.AuthorityName, ParentID: value.ParentID, DataScope: value.DataScope, DefaultRouter: value.DefaultRouter}
|
|
return db.Model(¤t).Updates(updates).Error
|
|
}
|
|
func (r *authorityAccessRepo) DeleteAuthority(ctx context.Context, id uint) error {
|
|
return r.data.gormDB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
|
var authority authorityPO
|
|
if err := tx.Where("authority_id = ?", id).First(&authority).Error; err != nil {
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return errors.New("该角色不存在")
|
|
}
|
|
return err
|
|
}
|
|
var users, children int64
|
|
if err := tx.Model(&userAuthorityPO{}).Where("sys_authority_authority_id = ?", id).Count(&users).Error; err != nil {
|
|
return err
|
|
}
|
|
if users > 0 {
|
|
return errors.New("此角色有用户正在使用禁止删除")
|
|
}
|
|
if err := tx.Model(&userPO{}).Where("authority_id = ?", id).Count(&users).Error; err != nil {
|
|
return err
|
|
}
|
|
if users > 0 {
|
|
return errors.New("此角色有用户正在使用禁止删除")
|
|
}
|
|
if err := tx.Model(&authorityPO{}).Where("parent_id = ?", id).Count(&children).Error; err != nil {
|
|
return err
|
|
}
|
|
if children > 0 {
|
|
return errors.New("此角色存在子角色不允许删除")
|
|
}
|
|
if err := tx.Where("sys_authority_authority_id = ?", id).Delete(&authorityMenuPO{}).Error; err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Where("authority_id = ?", id).Delete(&authorityAPIPO{}).Error; err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Where("authority_id = ?", id).Delete(&authorityButtonPO{}).Error; err != nil {
|
|
return err
|
|
}
|
|
return tx.Delete(&authorityPO{}, "authority_id = ?", id).Error
|
|
})
|
|
}
|
|
func (r *authorityAccessRepo) ListAuthorities(ctx context.Context) ([]*biz.Authority, error) {
|
|
var pos []authorityPO
|
|
if err := r.data.gormDB.WithContext(ctx).Find(&pos).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
var allowed map[uint]bool
|
|
config := r.data.runtime.Admin()
|
|
if actor, ok := biz.ActorFromContext(ctx); ok && config != nil && config.System != nil && config.System.UseStrictAuth {
|
|
var err error
|
|
allowed, err = r.strictAuthorityIDs(ctx, actor.AuthorityID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
out := make([]*biz.Authority, 0, len(pos))
|
|
for _, po := range pos {
|
|
if allowed != nil && !allowed[po.AuthorityID] {
|
|
continue
|
|
}
|
|
v := toBizAuthority(po)
|
|
out = append(out, &v)
|
|
}
|
|
return out, nil
|
|
}
|
|
func (r *authorityAccessRepo) SetAuthorityUsers(ctx context.Context, id uint, ids []uint) error {
|
|
return r.data.gormDB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
|
var oldIDs []uint
|
|
if err := tx.Model(&userAuthorityPO{}).Where("sys_authority_authority_id = ?", id).Pluck("sys_user_id", &oldIDs).Error; err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Where("sys_authority_authority_id = ?", id).Delete(&userAuthorityPO{}).Error; err != nil {
|
|
return err
|
|
}
|
|
links := make([]userAuthorityPO, 0, len(ids))
|
|
for _, uid := range ids {
|
|
links = append(links, userAuthorityPO{SysUserID: uid, SysAuthorityAuthorityID: id})
|
|
}
|
|
if len(links) > 0 {
|
|
if err := tx.Create(&links).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
selected := map[uint]bool{}
|
|
for _, uid := range ids {
|
|
selected[uid] = true
|
|
}
|
|
for _, uid := range oldIDs {
|
|
if selected[uid] {
|
|
continue
|
|
}
|
|
var user userPO
|
|
if err := tx.First(&user, uid).Error; err == nil && user.AuthorityID == id {
|
|
var another userAuthorityPO
|
|
if err = tx.Where("sys_user_id = ?", uid).First(&another).Error; errors.Is(err, gorm.ErrRecordNotFound) {
|
|
continue
|
|
} else if err != nil {
|
|
return err
|
|
}
|
|
if err = tx.Model(&user).Update("authority_id", another.SysAuthorityAuthorityID).Error; err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
}
|
|
func (r *authorityAccessRepo) AuthorityUserIDs(ctx context.Context, id uint) ([]uint, error) {
|
|
var ids []uint
|
|
err := r.data.gormDB.WithContext(ctx).Model(&userAuthorityPO{}).Where("sys_authority_authority_id = ?", id).Pluck("sys_user_id", &ids).Error
|
|
return ids, err
|
|
}
|
|
func (r *authorityAccessRepo) SetDataScope(ctx context.Context, id uint, scope int, deptIDs []uint) error {
|
|
return r.data.gormDB.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
|
if err := tx.Model(&authorityPO{}).Where("authority_id = ?", id).Update("data_scope", scope).Error; err != nil {
|
|
return err
|
|
}
|
|
if err := tx.Where("authority_id = ?", id).Delete(&authorityDepartmentPO{}).Error; err != nil {
|
|
return err
|
|
}
|
|
if scope == 5 {
|
|
links := make([]authorityDepartmentPO, 0, len(deptIDs))
|
|
for _, did := range deptIDs {
|
|
links = append(links, authorityDepartmentPO{AuthorityID: id, DepartmentID: did})
|
|
}
|
|
if len(links) > 0 {
|
|
return tx.Create(&links).Error
|
|
}
|
|
}
|
|
return nil
|
|
})
|
|
}
|
|
func (r *authorityAccessRepo) DataScopeDepartmentIDs(ctx context.Context, id uint) ([]uint, error) {
|
|
var ids []uint
|
|
err := r.data.gormDB.WithContext(ctx).Model(&authorityDepartmentPO{}).Where("authority_id = ?", id).Pluck("department_id", &ids).Error
|
|
return ids, err
|
|
}
|
|
func (r *authorityAccessRepo) ResolveDataScope(ctx context.Context, authorityID, userID uint) (biz.DataScope, error) {
|
|
identity := biz.DataScope{UserID: userID, AuthorityID: authorityID}
|
|
var user userPO
|
|
_ = r.data.gormDB.WithContext(ctx).Select("id", "dept_id").First(&user, userID).Error
|
|
identity.PrimaryDeptID = user.DeptID
|
|
var authority authorityPO
|
|
_ = r.data.gormDB.WithContext(ctx).Select("authority_id", "data_scope").First(&authority, "authority_id = ?", authorityID).Error
|
|
identity.Scope = authority.DataScope
|
|
if identity.Scope == 0 {
|
|
identity.Scope = 1
|
|
}
|
|
identity.All = identity.Scope == 1
|
|
if identity.Scope == 4 {
|
|
identity.OwnerUserID = userID
|
|
}
|
|
var ids []uint
|
|
_ = r.data.gormDB.WithContext(ctx).Model(&userDepartmentPO{}).Where("sys_user_id = ?", userID).Pluck("sys_department_id", &ids).Error
|
|
selected := make(map[uint]bool, len(ids)+1)
|
|
for _, id := range ids {
|
|
selected[id] = true
|
|
}
|
|
if user.DeptID != 0 {
|
|
selected[user.DeptID] = true
|
|
}
|
|
ids = ids[:0]
|
|
for id := range selected {
|
|
ids = append(ids, id)
|
|
}
|
|
if identity.Scope == 2 && len(ids) > 0 {
|
|
var departments []departmentPO
|
|
_ = r.data.gormDB.WithContext(ctx).Find(&departments).Error
|
|
for _, department := range departments {
|
|
for _, part := range strings.Split(department.Ancestors, ",") {
|
|
value, _ := strconv.ParseUint(part, 10, 64)
|
|
if selected[uint(value)] {
|
|
selected[department.ID] = true
|
|
break
|
|
}
|
|
}
|
|
}
|
|
ids = ids[:0]
|
|
for id := range selected {
|
|
ids = append(ids, id)
|
|
}
|
|
} else if identity.Scope == 5 {
|
|
ids, _ = r.DataScopeDepartmentIDs(ctx, authorityID)
|
|
}
|
|
identity.DepartmentIDs = ids
|
|
return identity, nil
|
|
}
|