chore: 并入 iotlcm-admin-svr 历史到 admin-svr/(原提交 49818c6)

This commit is contained in:
MiFaZhan
2026-11-02 13:27:59 +08:00
1250 changed files with 154111 additions and 0 deletions
@@ -0,0 +1,113 @@
package rolepolicy
import (
"context"
"reflect"
"strings"
"testing"
_ "github.com/gogf/gf/contrib/drivers/sqlite/v2"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
)
func TestNormalizeAssignableMenus(t *testing.T) {
cases := []struct {
name string
ids []int64
mutation string
want []int64
wantError bool
}{
{name: "补齐多级父节点但不把父权限写入角色", ids: []int64{1, 10, 100}, want: []int64{100}},
{name: "重复选择去重", ids: []int64{100, 100}, want: []int64{100}},
{name: "空选择不自动授权", ids: []int64{}, want: []int64{}},
{name: "无关菜单仍拒绝", ids: []int64{100, 200}, wantError: true},
{name: "父节点单独不能获得权限", ids: []int64{10}, wantError: true},
{name: "不存在菜单仍拒绝", ids: []int64{999}, wantError: true},
{name: "停用菜单不能继续下发", ids: []int64{100}, mutation: "UPDATE sys_menu SET status='1' WHERE menu_id=100", wantError: true},
{name: "模板保存前后不裁剪已下发权限", ids: []int64{100}, mutation: "DELETE FROM sys_role_menu WHERE role_id=82 AND menu_id=100", want: []int64{100}},
{name: "上级角色调整不裁剪本机构权限", ids: []int64{100}, mutation: "DELETE FROM sys_role_menu WHERE role_id=120 AND menu_id=100", want: []int64{100}},
{name: "本机构默认角色撤权后不能继续下放", ids: []int64{100}, mutation: "DELETE FROM sys_role_menu WHERE role_id=121", wantError: true},
{name: "操作员无权限不能借机构权限授权", ids: []int64{100}, mutation: "DELETE FROM sys_role_menu WHERE role_id=122", wantError: true},
{name: "操作员角色停用不能授权", ids: []int64{100}, mutation: "UPDATE sys_role SET status='1' WHERE role_id=122", wantError: true},
{name: "菜单循环不会无限遍历", ids: []int64{1, 10, 100}, mutation: "UPDATE sys_menu SET parent_id=100 WHERE menu_id=1", want: []int64{100}},
}
for _, test := range cases {
t.Run(test.name, func(t *testing.T) {
setupAssignableTestDB(t)
ctx := context.Background()
if test.mutation != "" {
if _, err := g.DB().Exec(ctx, test.mutation); err != nil {
t.Fatal(err)
}
}
got, err := NormalizeAssignableMenus(ctx, "000000", 199, 162, test.ids)
if test.wantError {
if err == nil || !strings.Contains(err.Error(), "菜单ID") {
t.Fatalf("expected actionable denial, got %v, %v", got, err)
}
return
}
if err != nil || !reflect.DeepEqual(got, test.want) {
t.Fatalf("got %v, %v; want %v", got, err, test.want)
}
})
}
}
func setupAssignableTestDB(t *testing.T) {
t.Helper()
if err := gdb.SetConfig(gdb.Config{"default": {{Type: "sqlite", Link: "sqlite::@file(:memory:)"}}}); err != nil {
t.Fatal(err)
}
ctx := context.Background()
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, data_scope TEXT, deleted_at DATETIME)",
"CREATE TABLE sys_dept (dept_id INTEGER PRIMARY KEY, tenant_id TEXT, parent_id INTEGER, dept_role INTEGER, status 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_menu (menu_id INTEGER PRIMARY KEY, parent_id INTEGER, status TEXT, perms TEXT)",
"INSERT INTO sys_config VALUES ('000000','agent.role.template.role_id','82')",
"INSERT INTO sys_dept VALUES (100,'000000',0,1,'0',NULL),(198,'000000',100,120,'0',NULL),(199,'000000',198,121,'0',NULL)",
"INSERT INTO sys_role VALUES (82,'000000',100,100,'0','4',NULL),(120,'000000',198,198,'0','4',NULL),(121,'000000',199,199,'0','4',NULL),(122,'000000',199,199,'0','4',NULL)",
"INSERT INTO sys_menu VALUES (1,0,'0',''),(10,1,'0','system:parent:list'),(100,10,'0','system:child:view'),(200,1,'0','system:other:view')",
"INSERT INTO sys_role_menu VALUES ('000000',82,100,'0'),('000000',120,100,'0'),('000000',121,100,'0'),('000000',122,100,'0')",
"INSERT INTO sys_user_role VALUES ('000000',162,122)",
} {
if _, err := g.DB().Exec(ctx, statement); err != nil {
t.Fatal(err)
}
}
}
// Runtime permissions come from the user's roles; delegation additionally uses
// the organization's current default role, never the template's menu list.
func TestIndividualRolePermissionsAndDelegation(t *testing.T) {
setupAssignableTestDB(t)
ctx := context.Background()
for _, statement := range []string{
"INSERT INTO sys_role_menu VALUES ('000000',122,200,'0')",
"DELETE FROM sys_role_menu WHERE role_id IN (82,120) AND menu_id=100",
} {
if _, err := g.DB().Exec(ctx, statement); err != nil {
t.Fatal(err)
}
}
got, err := RestrictMenus(ctx, "000000", 199, []int64{100, 200})
if err != nil || !reflect.DeepEqual(got, []int64{100, 200}) {
t.Fatalf("individual role expansion must survive runtime checks: %v %v", got, err)
}
if _, err := NormalizeAssignableMenus(ctx, "000000", 199, 162, []int64{200}); err == nil {
t.Fatal("operator cannot delegate outside local default role")
}
if _, err := g.DB().Exec(ctx, "INSERT INTO sys_role_menu VALUES ('000000',121,200,'0')"); err != nil {
t.Fatal(err)
}
got, err = NormalizeAssignableMenus(ctx, "000000", 199, 162, []int64{200})
if err != nil || !reflect.DeepEqual(got, []int64{200}) {
t.Fatalf("platform expansion of local default role must become delegable: %v %v", got, err)
}
}