package rolepolicy import ( "context" "git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/consts" "git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/dao" "git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model/entity" "github.com/gogf/gf/v2/errors/gerror" "strconv" "strings" ) const TemplateConfigKey = "agent.role.template.role_id" // TemplateForRoles reports whether any of roleIDs is the configured platform template. // Callers use it to tell "this save touches the template" apart from "this save is an // ordinary role" — the latter must not be forced to is_pass=1. func TemplateForRoles(ctx context.Context, roleIDs ...int64) (*entity.SysRole, error) { var configs []*entity.SysConfig if err := dao.SysConfig.Ctx(ctx).Master().Where("tenant_id", consts.DefaultSystemTenantCode).Where("config_key", TemplateConfigKey).Scan(&configs); err != nil { return nil, err } for _, config := range configs { roleID, err := strconv.ParseInt(strings.TrimSpace(config.ConfigValue), 10, 64) if err != nil || roleID <= 0 { continue } for _, targetID := range roleIDs { if targetID != roleID { continue } if len(configs) != 1 { return nil, gerror.New("代理模板角色系统参数重复") } var role *entity.SysRole if err := dao.SysRole.Ctx(ctx).Master().Where("role_id", roleID).Where("tenant_id", consts.DefaultSystemTenantCode).Scan(&role); err != nil { return nil, err } if role == nil { role = &entity.SysRole{RoleId: roleID, TenantId: consts.DefaultSystemTenantCode} } return role, nil } } return nil, nil } func Template(ctx context.Context) (*entity.SysRole, error) { var configs []*entity.SysConfig if err := dao.SysConfig.Ctx(ctx).Where("tenant_id", consts.DefaultSystemTenantCode).Where("config_key", TemplateConfigKey).Scan(&configs); err != nil { return nil, err } if len(configs) == 0 { return nil, nil } if len(configs) != 1 { return nil, gerror.New("代理模板角色系统参数重复") } return ValidateTemplate(ctx, configs[0].ConfigValue) } func ValidateTemplate(ctx context.Context, value string) (*entity.SysRole, error) { roleID, err := strconv.ParseInt(strings.TrimSpace(value), 10, 64) if err != nil || roleID <= 0 || roleID == consts.SuperAdminRoleId { return nil, gerror.New("代理模板参数必须填写非超级管理员的有效角色ID") } var role *entity.SysRole if err = dao.SysRole.Ctx(ctx).Where("role_id", roleID).Where("tenant_id", consts.DefaultSystemTenantCode).Scan(&role); err != nil { return nil, err } if role == nil || role.Status != "0" || (role.CreatedDept > 0 && role.CreatedDept != role.DeptId) || role.DeptId <= 0 || role.IsPass != 1 { return nil, gerror.New("请在企业管理的角色管理创建下发模板,模板须正常启用、绑定本级机构且非上级继承角色") } if role.DataScope != consts.SysRoleDataScopeDeptAndBelow { return nil, gerror.New("代理模板数据范围必须为本机构及下级") } var dept *entity.SysDept if err := dao.SysDept.Ctx(ctx).Where("tenant_id", role.TenantId).Where("dept_id", role.DeptId).Where("status", "0").Scan(&dept); err != nil { return nil, err } if dept == nil { return nil, gerror.New("代理模板所属机构不存在或已停用") } invalidScopes, err := dao.SysRoleMenu.Ctx(ctx).Where("tenant_id", role.TenantId).Where("role_id", role.RoleId).WhereIn("data_scope", []string{consts.SysRoleDataScopeAll, consts.SysRoleDataScopeCustom}).Count() if err != nil { return nil, err } if invalidScopes > 0 { return nil, gerror.New("代理模板按钮不允许全部或自定义数据范围") } return role, nil } func MenuIDs(ctx context.Context, roleID int64) ([]int64, error) { var menus []*entity.SysRoleMenu if err := dao.SysRoleMenu.Ctx(ctx).Where("role_id", roleID).Scan(&menus); err != nil { return nil, err } ids := make([]int64, 0, len(menus)) for _, menu := range menus { ids = append(ids, menu.MenuId) } return ids, nil } func Intersect(left, right []int64) []int64 { allowed := make(map[int64]bool, len(right)) for _, id := range right { allowed[id] = true } result := make([]int64, 0, len(left)) for _, id := range left { if allowed[id] { result = append(result, id) } } return result } func CurrentSuperAdmin(ctx context.Context, userID int64) (bool, error) { if userID <= 0 { return false, nil } var relations []*entity.SysUserRole if err := dao.SysUserRole.Ctx(ctx).Where("tenant_id", consts.DefaultSystemTenantCode).Where("user_id", userID).Where("role_id", consts.SuperAdminRoleId).Scan(&relations); err != nil { return false, err } if len(relations) == 0 { return false, nil } var role *entity.SysRole if err := dao.SysRole.Ctx(ctx).Where("tenant_id", consts.DefaultSystemTenantCode).Where("role_id", consts.SuperAdminRoleId).Where("status", "0").Scan(&role); err != nil { return false, err } return role != nil, nil } func RestrictMenus(ctx context.Context, tenantID string, deptID int64, ids []int64) ([]int64, error) { template, err := Template(ctx) if err != nil { return nil, err } if template == nil { return ids, nil } seen := make(map[int64]bool) for deptID > 0 { if seen[deptID] { return nil, gerror.New("机构上下级存在循环") } seen[deptID] = true var dept *entity.SysDept if err := dao.SysDept.Ctx(ctx).Where("tenant_id", tenantID).Where("dept_id", deptID).Scan(&dept); err != nil { return nil, err } if dept == nil || dept.Status != "0" { return []int64{}, nil } if template != nil && tenantID == template.TenantId && deptID == template.DeptId { if len(seen) > 1 { menus, err := MenuIDs(ctx, template.RoleId) if err != nil { return nil, err } ids = Intersect(ids, menus) } break } if dept.DeptRole != consts.SuperAdminRoleId { var role *entity.SysRole if err := dao.SysRole.Ctx(ctx).Where("tenant_id", tenantID).Where("dept_id", deptID).Where("role_id", dept.DeptRole).Where("status", "0").Scan(&role); err != nil { return nil, err } if role == nil { return []int64{}, nil } menus, err := MenuIDs(ctx, role.RoleId) if err != nil { return nil, err } ids = Intersect(ids, menus) } deptID = dept.ParentId } if tenantID == template.TenantId && !seen[template.DeptId] { return []int64{}, nil } return ids, nil }