chore: 并入 iotlcm-admin-svr 历史到 admin-svr/(原提交 49818c6)
This commit is contained in:
@@ -0,0 +1,437 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
_ "github.com/gogf/gf/contrib/drivers/sqlite/v2"
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gcfg"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/consts"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/contexts"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/rolepolicy"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/library/rolesync"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/logic/agent"
|
||||
_ "git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/logic/memorydb"
|
||||
"git.5iot.com/iotlcm-public/iotlcm-admin-svr/internal/model"
|
||||
)
|
||||
|
||||
func TestRoleMenuTemplateValidationIsolation(t *testing.T) {
|
||||
if os.Getenv("ROLE_TEMPLATE_TEST_CHILD") != "1" {
|
||||
configPath := filepath.Join(t.TempDir(), "config.yaml")
|
||||
if err := os.WriteFile(configPath, []byte("database:\n default:\n type: sqlite\n link: sqlite::@file(:memory:)\n memorydb:\n link: \"\"\n"), 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
executable, err := os.Executable()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
command := exec.Command(executable, "-test.run=^TestRoleMenuTemplateValidationIsolation$", "-test.v")
|
||||
command.Env = append(os.Environ(), "ROLE_TEMPLATE_TEST_CHILD=1", "GF_GCFG_FILE="+configPath)
|
||||
output, err := command.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("isolated role tests failed: %v\n%s", err, output)
|
||||
}
|
||||
t.Log(string(output))
|
||||
return
|
||||
}
|
||||
originalAdapter := g.Cfg().GetAdapter()
|
||||
adapter, err := gcfg.NewAdapterContent("database:\n default:\n type: sqlite\n link: sqlite::@file(:memory:)\n memorydb:\n link: \"\"\n", "yaml")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
g.Cfg().SetAdapter(adapter)
|
||||
t.Cleanup(func() { g.Cfg().SetAdapter(originalAdapter) })
|
||||
if err := gdb.SetConfig(gdb.Config{"default": {{Type: "sqlite", Link: "sqlite::@file(:memory:)"}}}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ctx := contexts.Set(context.Background(), &model.Context{User: &model.Identity{BaseClaims: model.BaseClaims{ID: 1, TenantId: "000000", DeptId: 100, Roles: []model.Role{{RoleId: consts.SuperAdminRoleId}}}}})
|
||||
t.Cleanup(func() { _ = g.DB().Close(ctx) })
|
||||
for _, statement := range []string{
|
||||
"CREATE TABLE sys_config (tenant_id TEXT, config_key TEXT, config_value TEXT)",
|
||||
"CREATE TABLE sys_role (role_id INTEGER PRIMARY KEY, tenant_id TEXT, dept_id INTEGER, created_dept INTEGER, status TEXT, is_pass INTEGER, data_scope TEXT, role_name TEXT, role_sort INTEGER, menu_check_strictly INTEGER, dept_check_strictly INTEGER, created_by INTEGER, created_at DATETIME, updated_by INTEGER, updated_at DATETIME, deleted_by INTEGER, remark TEXT, deleted_at DATETIME)",
|
||||
"CREATE TABLE sys_role_menu (tenant_id TEXT, role_id INTEGER, menu_id INTEGER, data_scope TEXT)",
|
||||
"CREATE TABLE sys_user_role (tenant_id TEXT, user_id INTEGER, role_id INTEGER)",
|
||||
"CREATE TABLE sys_role_dept (tenant_id TEXT, role_id INTEGER, dept_id INTEGER)",
|
||||
"INSERT INTO sys_config VALUES ('000000','agent.role.template.role_id','82')",
|
||||
"INSERT INTO sys_role (role_id,tenant_id,dept_id,created_dept,status,is_pass,data_scope,deleted_at) VALUES (82,'000000',0,100,'0',0,'4',NULL)",
|
||||
} {
|
||||
if _, err := g.DB().Exec(ctx, statement); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
service := NewSysRole()
|
||||
t.Run("普通角色新增菜单不受无效模板影响", func(t *testing.T) {
|
||||
if err := service.RoleMenu(ctx, 200, []int64{10, 11}, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
count, err := g.DB().Model("sys_role_menu").Where("role_id", 200).Count()
|
||||
if err != nil || count != 2 {
|
||||
t.Fatalf("menu count %d: %v", count, err)
|
||||
}
|
||||
})
|
||||
t.Run("普通角色修改菜单不受无效模板影响", func(t *testing.T) {
|
||||
if err := service.RoleMenu(ctx, 200, []int64{11, 12}, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
count, err := g.DB().Model("sys_role_menu").Where("role_id", 200).Where("menu_id", 10).Count()
|
||||
if err != nil || count != 0 {
|
||||
t.Fatalf("removed menu count %d: %v", count, err)
|
||||
}
|
||||
})
|
||||
t.Run("模板仍禁止全部按钮数据权限", func(t *testing.T) {
|
||||
err := service.RoleMenu(ctx, 82, []int64{10}, map[int64]string{10: consts.SysRoleDataScopeAll})
|
||||
if err == nil || !strings.Contains(err.Error(), "模板按钮") {
|
||||
t.Fatalf("expected template restriction: %v", err)
|
||||
}
|
||||
})
|
||||
t.Run("无效模板仍禁止删除", func(t *testing.T) {
|
||||
_, err := service.Delete(ctx, &model.SysRoleDeleteParam{RoleId: 82})
|
||||
if err == nil || !strings.Contains(err.Error(), "模板不能删除") {
|
||||
t.Fatalf("expected template deletion rejection: %v", err)
|
||||
}
|
||||
})
|
||||
t.Run("批量删除不能包含模板", func(t *testing.T) {
|
||||
_, err := service.Delete(ctx, &model.SysRoleDeleteParam{RoleIds: []int64{200, 82}})
|
||||
if err == nil || !strings.Contains(err.Error(), "模板不能删除") {
|
||||
t.Fatalf("expected batch deletion rejection: %v", err)
|
||||
}
|
||||
})
|
||||
t.Run("非管理员不能绕过无效模板授权", func(t *testing.T) {
|
||||
limited := contexts.Set(context.Background(), &model.Context{User: &model.Identity{BaseClaims: model.BaseClaims{ID: 2, TenantId: "000000", DeptId: 175}}})
|
||||
if err := service.RoleMenu(limited, 201, []int64{10}, nil); err == nil {
|
||||
t.Fatal("invalid template must fail closed")
|
||||
}
|
||||
count, err := g.DB().Model("sys_role_menu").Where("role_id", 201).Count()
|
||||
if err != nil || count != 0 {
|
||||
t.Fatalf("unauthorized menu count %d: %v", count, err)
|
||||
}
|
||||
})
|
||||
t.Run("运行时模板校验仍然严格", func(t *testing.T) {
|
||||
if _, err := rolepolicy.Template(ctx); err == nil {
|
||||
t.Fatal("invalid template was accepted")
|
||||
}
|
||||
})
|
||||
t.Run("无关角色不读取模板角色实体", func(t *testing.T) {
|
||||
template, err := rolepolicy.TemplateForRoles(ctx, 200)
|
||||
if err != nil || template != nil {
|
||||
t.Fatalf("unexpected template: %+v %v", template, err)
|
||||
}
|
||||
})
|
||||
t.Run("已删除模板仍保留身份保护", func(t *testing.T) {
|
||||
if _, err := g.DB().Exec(ctx, "DELETE FROM sys_role WHERE role_id=82"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
template, err := rolepolicy.TemplateForRoles(ctx, 82)
|
||||
if err != nil || template == nil || template.RoleId != 82 {
|
||||
t.Fatalf("template identity lost: %+v %v", template, err)
|
||||
}
|
||||
})
|
||||
t.Run("错误参数不阻断无关角色但不能用于授权", func(t *testing.T) {
|
||||
if _, err := g.DB().Exec(ctx, "UPDATE sys_config SET config_value='invalid'"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := service.RoleMenu(ctx, 202, []int64{10}, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := rolepolicy.Template(ctx); err == nil {
|
||||
t.Fatal("invalid configuration must fail strict validation")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("平台管理员权限加载不依赖无效模板", func(t *testing.T) {
|
||||
for _, statement := range []string{
|
||||
"UPDATE sys_config SET config_value='82'",
|
||||
"ALTER TABLE sys_role ADD COLUMN role_key TEXT",
|
||||
"INSERT INTO sys_role (role_id,tenant_id,dept_id,status,role_key,data_scope) VALUES (1,'000000',100,'0','admin','1'),(102,'000000',175,'0','agent','1')",
|
||||
"CREATE TABLE sys_dept (dept_id INTEGER PRIMARY KEY, tenant_id TEXT, parent_id INTEGER, dept_role INTEGER, status TEXT, ancestors TEXT, dept_type INTEGER, order_num INTEGER DEFAULT 0, deleted_at DATETIME)",
|
||||
"INSERT INTO sys_dept (dept_id,tenant_id,parent_id,dept_role,status,deleted_at) VALUES (100,'000000',0,1,'0',NULL),(175,'000000',100,102,'0',NULL)",
|
||||
"CREATE TABLE sys_menu (menu_id INTEGER PRIMARY KEY, menu_name TEXT, parent_id INTEGER, path TEXT, menu_type TEXT, order_num INTEGER, perms TEXT, status TEXT, deleted_at DATETIME)",
|
||||
"INSERT INTO sys_menu VALUES (10,'授权功能',0,'allowed','C',1,'system:test:allowed','0',NULL),(11,'受限功能',0,'restricted','C',2,'system:test:restricted','0',NULL)",
|
||||
"CREATE TABLE sys_user (user_id INTEGER PRIMARY KEY, tenant_id TEXT, dept_id INTEGER, user_name TEXT, status TEXT, deleted_at DATETIME)",
|
||||
"INSERT INTO sys_user VALUES (1,'000000',100,'admin','0',NULL),(2,'000000',175,'agent','0',NULL)",
|
||||
"CREATE TABLE sys_user_post (tenant_id TEXT, user_id INTEGER, post_id INTEGER)",
|
||||
"INSERT INTO sys_user_role VALUES ('000000',1,1),('000000',2,102)",
|
||||
} {
|
||||
if _, err := g.DB().Exec(ctx, statement); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
accessCodes, _, err := NewSysAuth().GetUserAccessCodeList(ctx, 1)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !slices.Contains(accessCodes, "cpm:system:test:allowed") {
|
||||
t.Fatalf("administrator permissions missing: %v", accessCodes)
|
||||
}
|
||||
menus, err := NewSysMenu().GetUserMenu(ctx)
|
||||
if err != nil || len(menus) != 2 {
|
||||
t.Fatalf("administrator menu loading failed: %v %v", menus, err)
|
||||
}
|
||||
})
|
||||
t.Run("不能仅凭旧令牌声称是平台管理员", func(t *testing.T) {
|
||||
stale := contexts.Set(context.Background(), &model.Context{User: &model.Identity{BaseClaims: model.BaseClaims{ID: 2, TenantId: "000000", DeptId: 175, Roles: []model.Role{{RoleId: consts.SuperAdminRoleId}}}}})
|
||||
if _, _, err := NewSysAuth().GetUserAccessCodeList(stale, 2); err == nil {
|
||||
t.Fatal("stale administrator claim bypassed template validation")
|
||||
}
|
||||
})
|
||||
t.Run("停用平台管理员角色不能跳过模板校验", func(t *testing.T) {
|
||||
if _, err := g.DB().Exec(ctx, "UPDATE sys_role SET status='1' WHERE role_id=1"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer func() {
|
||||
if _, err := g.DB().Exec(ctx, "UPDATE sys_role SET status='0' WHERE role_id=1"); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}()
|
||||
if _, _, err := NewSysAuth().GetUserAccessCodeList(ctx, 1); err == nil {
|
||||
t.Fatal("disabled administrator role bypassed validation")
|
||||
}
|
||||
})
|
||||
t.Run("跨租户管理员标记不能跳过模板校验", func(t *testing.T) {
|
||||
other := contexts.Set(context.Background(), &model.Context{User: &model.Identity{BaseClaims: model.BaseClaims{ID: 1, TenantId: "other", DeptId: 100, Roles: []model.Role{{RoleId: consts.SuperAdminRoleId}}}}})
|
||||
if _, _, err := NewSysAuth().GetUserAccessCodeList(other, 1); err == nil {
|
||||
t.Fatal("cross-tenant administrator claim bypassed validation")
|
||||
}
|
||||
})
|
||||
t.Run("模板参数格式异常不阻断平台管理员", func(t *testing.T) {
|
||||
if _, err := g.DB().Exec(ctx, "UPDATE sys_config SET config_value='invalid'"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, _, err := NewSysAuth().GetUserAccessCodeList(ctx, 1); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
t.Run("平台单独扩大默认角色权限后菜单及鉴权均生效且不扩大数据范围", func(t *testing.T) {
|
||||
for _, statement := range []string{
|
||||
"UPDATE sys_config SET config_value='82'",
|
||||
"INSERT INTO sys_role (role_id,tenant_id,dept_id,created_dept,status,is_pass,data_scope) VALUES (82,'000000',100,100,'0',1,'4')",
|
||||
"INSERT INTO sys_role_menu VALUES ('000000',82,10,'0'),('000000',102,10,'0'),('000000',102,11,'0')",
|
||||
} {
|
||||
if _, err := g.DB().Exec(ctx, statement); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
agent := contexts.Set(context.Background(), &model.Context{User: &model.Identity{BaseClaims: model.BaseClaims{ID: 2, TenantId: "000000", DeptId: 175}}})
|
||||
accessCodes, roleCodes, err := NewSysAuth().GetUserAccessCodeList(agent, 2)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !slices.Contains(accessCodes, "cpm:system:test:allowed") || !slices.Contains(accessCodes, "cpm:system:test:restricted") {
|
||||
t.Fatalf("agent menu boundary violated: %v", accessCodes)
|
||||
}
|
||||
menus, err := NewSysMenu().GetUserMenu(agent)
|
||||
if err != nil || len(menus) != 2 {
|
||||
t.Fatalf("individual menus missing: %v %v", menus, err)
|
||||
}
|
||||
if len(roleCodes) != 2 || !strings.HasSuffix(roleCodes[0], "|4") || !strings.HasSuffix(roleCodes[1], "|4") {
|
||||
t.Fatalf("agent data scope boundary violated: %v", roleCodes)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("平台从角色管理扩大普通角色权限不受默认角色限制", func(t *testing.T) {
|
||||
for _, statement := range []string{
|
||||
"DELETE FROM sys_role_menu WHERE role_id=102 AND menu_id=11",
|
||||
"INSERT INTO sys_role(role_id,tenant_id,dept_id,created_dept,status,data_scope,role_key) VALUES(103,'000000',175,175,'0','4','special')",
|
||||
"INSERT INTO sys_user VALUES(3,'000000',175,'special','0',NULL)",
|
||||
"INSERT INTO sys_user_role VALUES('000000',3,103)",
|
||||
} {
|
||||
if _, err := g.DB().Exec(ctx, statement); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if _, err := service.Edit(rolesync.DeferRecording(ctx), &model.SysRoleEditParam{RoleId: 103, MenuIds: []int64{11}}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
special := contexts.Set(context.Background(), &model.Context{User: &model.Identity{BaseClaims: model.BaseClaims{ID: 3, TenantId: "000000", DeptId: 175}}})
|
||||
codes, _, err := NewSysAuth().GetUserAccessCodeList(special, 3)
|
||||
if err != nil || !slices.Contains(codes, "cpm:system:test:restricted") {
|
||||
t.Fatalf("saved permission did not reach API authorization: %v %v", codes, err)
|
||||
}
|
||||
menus, err := NewSysMenu().GetUserMenu(special)
|
||||
if err != nil || len(menus) != 1 || menus[0].MenuId != 11 {
|
||||
t.Fatalf("saved permission did not reach navigation: %v %v", menus, err)
|
||||
}
|
||||
if _, err := g.DB().Exec(ctx, "DELETE FROM sys_role_menu WHERE role_id=103"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
codes, _, err = NewSysAuth().GetUserAccessCodeList(special, 3)
|
||||
if err != nil || slices.Contains(codes, "cpm:system:test:restricted") {
|
||||
t.Fatalf("revoked role permission remained usable: %v %v", codes, err)
|
||||
}
|
||||
})
|
||||
t.Run("角色详情说明模板和默认角色身份", func(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
id int64
|
||||
template, defaultRole bool
|
||||
}{{82, true, false}, {102, false, true}, {103, false, false}} {
|
||||
role, err := service.View(ctx, &model.SysRoleViewParam{RoleId: test.id})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if role.IsTemplateRole != test.template || role.IsDeptDefaultRole != test.defaultRole {
|
||||
t.Fatalf("wrong role metadata: %+v", role)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("非平台管理员不能单独修改模板菜单或数据范围", func(t *testing.T) {
|
||||
operator := contexts.Set(context.Background(), &model.Context{User: &model.Identity{BaseClaims: model.BaseClaims{ID: 2, TenantId: "000000", DeptId: 175}}})
|
||||
if err := service.RoleMenu(operator, 82, []int64{10}, nil); err == nil || !strings.Contains(err.Error(), "仅平台超级管理员") {
|
||||
t.Fatalf("template menu change was not rejected: %v", err)
|
||||
}
|
||||
if err := service.EditRoleDataScope(operator, &model.SysRoleDataScopeEditParam{RoleId: 82, DataScope: consts.SysRoleDataScopeDeptAndBelow}); err == nil || !strings.Contains(err.Error(), "仅平台超级管理员") {
|
||||
t.Fatalf("template data scope change was not rejected: %v", err)
|
||||
}
|
||||
})
|
||||
t.Run("企业入口允许已有下发角色时新增多个普通角色", func(t *testing.T) {
|
||||
for _, roleName := range []string{"财务", "客服"} {
|
||||
roleID, err := agent.NewAgentRole().RoleSave(ctx, &model.AgentRoleSaveParam{RoleName: roleName, RoleKey: "staff", MenuIds: []int64{10}, DataScope: consts.SysRoleDataScopePersonal})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
record, err := g.DB().Model("sys_role").Where("role_id", roleID).One()
|
||||
if err != nil || record["is_pass"].Int() != 0 || record["data_scope"].String() != consts.SysRoleDataScopePersonal {
|
||||
t.Fatalf("ordinary role was changed: %v %v", record, err)
|
||||
}
|
||||
}
|
||||
})
|
||||
t.Run("配置平台模板后历史下发标记不限制普通角色维护", func(t *testing.T) {
|
||||
_, err := agent.NewAgentRole().RoleSave(ctx, &model.AgentRoleSaveParam{RoleName: "历史标记角色", RoleKey: "legacy", IsPass: 1})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
t.Run("平台模板由参数识别不依赖隐藏下发标记", func(t *testing.T) {
|
||||
_, err := agent.NewAgentRole().RoleSave(rolesync.DeferRecording(ctx), &model.AgentRoleSaveParam{RoleId: 82, RoleName: "模板", RoleKey: "template", MenuIds: []int64{10}, IsPass: 0})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
template, err := rolepolicy.Template(ctx)
|
||||
if err != nil || template == nil || template.RoleId != 82 || template.IsPass != 0 {
|
||||
t.Fatalf("configured template not recognized: %+v %v", template, err)
|
||||
}
|
||||
})
|
||||
t.Run("企业入口拒绝错误历史传递标记", func(t *testing.T) {
|
||||
_, err := agent.NewAgentRole().RoleSave(ctx, &model.AgentRoleSaveParam{RoleName: "无效标记", RoleKey: "invalid", IsPass: 2})
|
||||
if err == nil || !strings.Contains(err.Error(), "传递标记无效") {
|
||||
t.Fatalf("invalid legacy marker was accepted: %v", err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("配置模板后历史下发标记不阻断普通角色删除", func(t *testing.T) {
|
||||
for _, statement := range []string{
|
||||
"INSERT INTO sys_role(role_id,tenant_id,dept_id,created_dept,status,is_pass,data_scope) VALUES(600,'000000',100,100,'0',1,'4')",
|
||||
"UPDATE sys_role SET created_dept=100 WHERE role_id=102",
|
||||
} {
|
||||
if _, err := g.DB().Exec(ctx, statement); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
if _, err := service.Delete(ctx, &model.SysRoleDeleteParam{RoleId: 600}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
})
|
||||
t.Run("授权范围不能超出当前操作员有效角色", func(t *testing.T) {
|
||||
if _, err := g.DB().Exec(ctx, "INSERT INTO sys_role(role_id,tenant_id,dept_id,created_dept,status,is_pass,data_scope,role_key) VALUES(501,'000000',175,175,'0',0,'4','staff')"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := g.DB().Exec(ctx, "INSERT INTO sys_user_role VALUES('000000',3,501)"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
operator := contexts.Set(context.Background(), &model.Context{User: &model.Identity{BaseClaims: model.BaseClaims{ID: 3, TenantId: "000000", DeptId: 175}}})
|
||||
if err := service.RoleMenu(operator, 501, []int64{10}, nil); err == nil || !strings.Contains(err.Error(), "超出") {
|
||||
t.Fatalf("operator without permission granted it: %v", err)
|
||||
}
|
||||
if _, err := g.DB().Exec(ctx, "INSERT INTO sys_role_menu VALUES('000000',501,10,'0')"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := service.RoleMenu(operator, 501, []int64{10}, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := g.DB().Exec(ctx, "UPDATE sys_role SET status='1' WHERE role_id=501"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := service.RoleMenu(operator, 501, []int64{10}, nil); err == nil {
|
||||
t.Fatal("disabled operator role granted permission")
|
||||
}
|
||||
})
|
||||
t.Run("展示祖先兼容旧请求但不写入实际授权", func(t *testing.T) {
|
||||
for _, statement := range []string{
|
||||
"INSERT INTO sys_menu VALUES (900,'展示目录',0,'display','M',0,'','0',NULL),(901,'未授权父菜单',900,'parent','C',0,'system:parent:list','0',NULL)",
|
||||
"UPDATE sys_menu SET parent_id=901 WHERE menu_id=10",
|
||||
} {
|
||||
if _, err := g.DB().Exec(ctx, statement); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
operator := contexts.Set(context.Background(), &model.Context{User: &model.Identity{BaseClaims: model.BaseClaims{ID: 2, TenantId: "000000", DeptId: 175}}})
|
||||
tree, assignable, err := agent.NewAgentMenu().AllowedMenuTree(operator, 175)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !slices.Contains(assignable, 10) || slices.Contains(assignable, 900) || slices.Contains(assignable, 901) {
|
||||
t.Fatalf("display ancestors leaked into assignable IDs: %v", assignable)
|
||||
}
|
||||
if len(tree) != 3 || !slices.ContainsFunc(tree, func(menu *model.SysMenuTreeModel) bool { return menu.MenuId == 901 && menu.ParentId == 900 }) || !slices.ContainsFunc(tree, func(menu *model.SysMenuTreeModel) bool { return menu.MenuId == 10 && menu.ParentId == 901 }) {
|
||||
t.Fatalf("display hierarchy is incomplete: %+v", tree)
|
||||
}
|
||||
if err := service.RoleMenu(operator, 502, []int64{900, 901, 10}, nil); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var stored []int64
|
||||
values, err := g.DB().Model("sys_role_menu").Where("role_id", 502).Array("menu_id")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, value := range values {
|
||||
stored = append(stored, value.Int64())
|
||||
}
|
||||
if len(stored) != 1 || stored[0] != 10 {
|
||||
t.Fatalf("display-only permissions were persisted: %v", stored)
|
||||
}
|
||||
newRole, err := agent.NewAgentRole().RoleSave(rolesync.DeferRecording(operator), &model.AgentRoleSaveParam{
|
||||
RoleName: "下级测试角色", RoleKey: "scoped-agent", MenuIds: []int64{900, 901, 10},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
storedMenus, err := g.DB().Model("sys_role_menu").Where("role_id", newRole).Array("menu_id")
|
||||
if err != nil || len(storedMenus) != 1 || storedMenus[0].Int64() != 10 {
|
||||
t.Fatalf("agent save did not preserve the same authorization boundary: %v %v", storedMenus, err)
|
||||
}
|
||||
if err := service.RoleMenu(operator, 502, []int64{10, 11}, nil); err == nil {
|
||||
t.Fatal("unrelated permission was accepted")
|
||||
}
|
||||
count, err := g.DB().Model("sys_role_menu").Where("role_id", 502).Count()
|
||||
if err != nil || count != 1 {
|
||||
t.Fatalf("failed validation changed permissions: %d %v", count, err)
|
||||
}
|
||||
})
|
||||
t.Run("默认角色缺失明确报错而不是静默返回404", func(t *testing.T) {
|
||||
if _, err := g.DB().Exec(ctx, "UPDATE sys_dept SET dept_role=0 WHERE dept_id=175"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer func() { _, _ = g.DB().Exec(ctx, "UPDATE sys_dept SET dept_role=102 WHERE dept_id=175") }()
|
||||
if _, err := rolepolicy.RestrictMenus(ctx, "000000", 175, []int64{10}); err == nil || !strings.Contains(err.Error(), "未关联默认角色") {
|
||||
t.Fatalf("missing role must be actionable: %v", err)
|
||||
}
|
||||
operator := contexts.Set(context.Background(), &model.Context{User: &model.Identity{BaseClaims: model.BaseClaims{ID: 2, TenantId: "000000", DeptId: 175}}})
|
||||
if _, err := g.DB().Exec(ctx, "UPDATE sys_dept SET dept_type=1 WHERE dept_id=175"); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := agent.NewAgentDept().EnsureCurrentDeptDefaultRole(operator); err == nil {
|
||||
t.Fatal("missing default role was not reported")
|
||||
}
|
||||
count, err := g.DB().Model("sys_user_role").Where("user_id", 2).Count()
|
||||
if err != nil || count != 1 {
|
||||
t.Fatalf("read request changed user roles: %d %v", count, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user