|
|
@@ -0,0 +1,233 @@
|
|
|
+package user
|
|
|
+
|
|
|
+import (
|
|
|
+ "errors"
|
|
|
+ "testing"
|
|
|
+ "time"
|
|
|
+
|
|
|
+ "github.com/stretchr/testify/assert"
|
|
|
+ "github.com/stretchr/testify/require"
|
|
|
+
|
|
|
+ "perms-system-server/internal/consts"
|
|
|
+ "perms-system-server/internal/loaders"
|
|
|
+ memberModel "perms-system-server/internal/model/productmember"
|
|
|
+ "perms-system-server/internal/response"
|
|
|
+ "perms-system-server/internal/svc"
|
|
|
+ "perms-system-server/internal/testutil"
|
|
|
+ "perms-system-server/internal/testutil/ctxhelper"
|
|
|
+ "perms-system-server/internal/types"
|
|
|
+)
|
|
|
+
|
|
|
+// insertTestMemberWithType 创建指定类型的产品成员,供 getUserPerms 测试使用。
|
|
|
+func insertTestMemberWithType(t *testing.T, svcCtx *svc.ServiceContext, productCode string, userId int64, memberType string) int64 {
|
|
|
+ t.Helper()
|
|
|
+ now := time.Now().Unix()
|
|
|
+ res, err := svcCtx.SysProductMemberModel.Insert(ctxhelper.SuperAdminCtx(), &memberModel.SysProductMember{
|
|
|
+ ProductCode: productCode,
|
|
|
+ UserId: userId,
|
|
|
+ MemberType: memberType,
|
|
|
+ Status: consts.StatusEnabled,
|
|
|
+ CreateTime: now,
|
|
|
+ UpdateTime: now,
|
|
|
+ })
|
|
|
+ require.NoError(t, err)
|
|
|
+ id, _ := res.LastInsertId()
|
|
|
+ return id
|
|
|
+}
|
|
|
+
|
|
|
+// insertUserPerm 直接向 sys_user_perm 插入一条覆盖记录,绕过 SetUserPerms 的业务校验。
|
|
|
+func insertUserPerm(t *testing.T, svcCtx *svc.ServiceContext, userId, permId int64, effect string) {
|
|
|
+ t.Helper()
|
|
|
+ conn := testutil.GetTestSqlConn()
|
|
|
+ now := time.Now().Unix()
|
|
|
+ _, err := conn.ExecCtx(ctxhelper.SuperAdminCtx(),
|
|
|
+ "INSERT INTO `sys_user_perm` (`userId`,`permId`,`effect`,`createTime`,`updateTime`) VALUES (?,?,?,?,?)",
|
|
|
+ userId, permId, effect, now, now)
|
|
|
+ require.NoError(t, err)
|
|
|
+}
|
|
|
+
|
|
|
+// TC-1257: 超管查任意用户权限覆盖
|
|
|
+func TestGetUserPerms_SuperAdmin(t *testing.T) {
|
|
|
+ ctx := ctxhelper.SuperAdminCtx()
|
|
|
+ svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
|
|
|
+ conn := testutil.GetTestSqlConn()
|
|
|
+
|
|
|
+ username := testutil.UniqueId()
|
|
|
+ userId := insertTestUser(t, ctx, username, testutil.HashPassword("pass"))
|
|
|
+ mId := insertTestMember(t, svcCtx, "test_product", userId)
|
|
|
+ permId := insertTestPerm(t, svcCtx, "test_product")
|
|
|
+
|
|
|
+ insertUserPerm(t, svcCtx, userId, permId, consts.PermEffectAllow)
|
|
|
+
|
|
|
+ t.Cleanup(func() {
|
|
|
+ testutil.CleanTableByField(ctx, conn, "`sys_user_perm`", "userId", userId)
|
|
|
+ testutil.CleanTable(ctx, conn, "`sys_product_member`", mId)
|
|
|
+ testutil.CleanTable(ctx, conn, "`sys_user`", userId)
|
|
|
+ testutil.CleanTable(ctx, conn, "`sys_perm`", permId)
|
|
|
+ })
|
|
|
+
|
|
|
+ resp, err := NewGetUserPermsLogic(ctx, svcCtx).GetUserPerms(&types.GetUserPermsReq{UserId: userId})
|
|
|
+ require.NoError(t, err)
|
|
|
+ require.NotNil(t, resp)
|
|
|
+ require.Len(t, resp.Perms, 1)
|
|
|
+ assert.Equal(t, permId, resp.Perms[0].PermId)
|
|
|
+ assert.Equal(t, consts.PermEffectAllow, resp.Perms[0].Effect)
|
|
|
+}
|
|
|
+
|
|
|
+// TC-1258: 用户查询自己的权限覆盖(包含 ALLOW 和 DENY)
|
|
|
+func TestGetUserPerms_SelfQuery(t *testing.T) {
|
|
|
+ bootstrapCtx := ctxhelper.SuperAdminCtx()
|
|
|
+ svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
|
|
|
+ conn := testutil.GetTestSqlConn()
|
|
|
+
|
|
|
+ username := testutil.UniqueId()
|
|
|
+ userId := insertTestUser(t, bootstrapCtx, username, testutil.HashPassword("pass"))
|
|
|
+ mId := insertTestMember(t, svcCtx, "test_product", userId)
|
|
|
+ permId1 := insertTestPerm(t, svcCtx, "test_product")
|
|
|
+ permId2 := insertTestPerm(t, svcCtx, "test_product")
|
|
|
+
|
|
|
+ insertUserPerm(t, svcCtx, userId, permId1, consts.PermEffectAllow)
|
|
|
+ insertUserPerm(t, svcCtx, userId, permId2, consts.PermEffectDeny)
|
|
|
+
|
|
|
+ t.Cleanup(func() {
|
|
|
+ testutil.CleanTableByField(bootstrapCtx, conn, "`sys_user_perm`", "userId", userId)
|
|
|
+ testutil.CleanTable(bootstrapCtx, conn, "`sys_product_member`", mId)
|
|
|
+ testutil.CleanTable(bootstrapCtx, conn, "`sys_user`", userId)
|
|
|
+ testutil.CleanTable(bootstrapCtx, conn, "`sys_perm`", permId1, permId2)
|
|
|
+ })
|
|
|
+
|
|
|
+ // caller == target(isSelf 分支,跳过 RequireProductAdminFor + CheckManageAccess)
|
|
|
+ selfCtx := ctxhelper.CustomCtx(&loaders.UserDetails{
|
|
|
+ UserId: userId,
|
|
|
+ Username: username,
|
|
|
+ MemberType: consts.MemberTypeMember,
|
|
|
+ ProductCode: "test_product",
|
|
|
+ Status: consts.StatusEnabled,
|
|
|
+ })
|
|
|
+ resp, err := NewGetUserPermsLogic(selfCtx, svcCtx).GetUserPerms(&types.GetUserPermsReq{UserId: userId})
|
|
|
+ require.NoError(t, err)
|
|
|
+ require.NotNil(t, resp)
|
|
|
+ assert.Len(t, resp.Perms, 2, "应返回 ALLOW 和 DENY 两条记录")
|
|
|
+}
|
|
|
+
|
|
|
+// TC-1259: 产品 ADMIN 查同产品 MEMBER 的权限覆盖
|
|
|
+func TestGetUserPerms_AdminQueriesMember(t *testing.T) {
|
|
|
+ bootstrapCtx := ctxhelper.SuperAdminCtx()
|
|
|
+ svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
|
|
|
+ conn := testutil.GetTestSqlConn()
|
|
|
+
|
|
|
+ username := testutil.UniqueId()
|
|
|
+ userId := insertTestUser(t, bootstrapCtx, username, testutil.HashPassword("pass"))
|
|
|
+ mId := insertTestMember(t, svcCtx, "test_product", userId) // MEMBER 类型
|
|
|
+ permId := insertTestPerm(t, svcCtx, "test_product")
|
|
|
+
|
|
|
+ insertUserPerm(t, svcCtx, userId, permId, consts.PermEffectAllow)
|
|
|
+
|
|
|
+ t.Cleanup(func() {
|
|
|
+ testutil.CleanTableByField(bootstrapCtx, conn, "`sys_user_perm`", "userId", userId)
|
|
|
+ testutil.CleanTable(bootstrapCtx, conn, "`sys_product_member`", mId)
|
|
|
+ testutil.CleanTable(bootstrapCtx, conn, "`sys_user`", userId)
|
|
|
+ testutil.CleanTable(bootstrapCtx, conn, "`sys_perm`", permId)
|
|
|
+ })
|
|
|
+
|
|
|
+ adminCtx := ctxhelper.AdminCtx("test_product")
|
|
|
+ resp, err := NewGetUserPermsLogic(adminCtx, svcCtx).GetUserPerms(&types.GetUserPermsReq{UserId: userId})
|
|
|
+ require.NoError(t, err)
|
|
|
+ require.NotNil(t, resp)
|
|
|
+ assert.Len(t, resp.Perms, 1)
|
|
|
+ assert.Equal(t, permId, resp.Perms[0].PermId)
|
|
|
+}
|
|
|
+
|
|
|
+// TC-1260: 产品 ADMIN 查同级 ADMIN 被拒绝(permsLevel 等级相同时 CheckManageAccess 拒绝)
|
|
|
+func TestGetUserPerms_AdminQueriesSameLevelAdmin_Forbidden(t *testing.T) {
|
|
|
+ bootstrapCtx := ctxhelper.SuperAdminCtx()
|
|
|
+ svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
|
|
|
+ conn := testutil.GetTestSqlConn()
|
|
|
+
|
|
|
+ // 目标:ADMIN 类型成员
|
|
|
+ targetUsername := testutil.UniqueId()
|
|
|
+ targetUserId := insertTestUser(t, bootstrapCtx, targetUsername, testutil.HashPassword("pass"))
|
|
|
+ mId := insertTestMemberWithType(t, svcCtx, "test_product", targetUserId, consts.MemberTypeAdmin)
|
|
|
+
|
|
|
+ t.Cleanup(func() {
|
|
|
+ testutil.CleanTable(bootstrapCtx, conn, "`sys_product_member`", mId)
|
|
|
+ testutil.CleanTable(bootstrapCtx, conn, "`sys_user`", targetUserId)
|
|
|
+ })
|
|
|
+
|
|
|
+ // AdminCtx UserId=2,无任何角色 → callerNoRole=true → CheckManageAccess 拒绝(等级不低于目标)
|
|
|
+ adminCtx := ctxhelper.AdminCtx("test_product")
|
|
|
+ _, err := NewGetUserPermsLogic(adminCtx, svcCtx).GetUserPerms(&types.GetUserPermsReq{UserId: targetUserId})
|
|
|
+ require.Error(t, err)
|
|
|
+ var ce *response.CodeError
|
|
|
+ require.True(t, errors.As(err, &ce))
|
|
|
+ assert.Equal(t, 403, ce.Code())
|
|
|
+}
|
|
|
+
|
|
|
+// TC-1261: 普通 MEMBER 查他人被拒绝(RequireProductAdminFor 前置拦截)
|
|
|
+func TestGetUserPerms_MemberQueriesOther_Forbidden(t *testing.T) {
|
|
|
+ bootstrapCtx := ctxhelper.SuperAdminCtx()
|
|
|
+ svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
|
|
|
+ conn := testutil.GetTestSqlConn()
|
|
|
+
|
|
|
+ otherUsername := testutil.UniqueId()
|
|
|
+ otherUserId := insertTestUser(t, bootstrapCtx, otherUsername, testutil.HashPassword("pass"))
|
|
|
+ mId := insertTestMember(t, svcCtx, "test_product", otherUserId)
|
|
|
+
|
|
|
+ t.Cleanup(func() {
|
|
|
+ testutil.CleanTable(bootstrapCtx, conn, "`sys_product_member`", mId)
|
|
|
+ testutil.CleanTable(bootstrapCtx, conn, "`sys_user`", otherUserId)
|
|
|
+ })
|
|
|
+
|
|
|
+ memberCtx := ctxhelper.MemberCtx("test_product")
|
|
|
+ _, err := NewGetUserPermsLogic(memberCtx, svcCtx).GetUserPerms(&types.GetUserPermsReq{UserId: otherUserId})
|
|
|
+ require.Error(t, err)
|
|
|
+ var ce *response.CodeError
|
|
|
+ require.True(t, errors.As(err, &ce))
|
|
|
+ assert.Equal(t, 403, ce.Code())
|
|
|
+ assert.Contains(t, ce.Error(), "仅超级管理员或该产品的管理员可执行此操作")
|
|
|
+}
|
|
|
+
|
|
|
+// TC-1262: 非 ADMIN 查不存在的 userId 必须 403(RequireProductAdminFor 先于实体读取,消除枚举 oracle)
|
|
|
+func TestGetUserPerms_NonAdmin_NonExistentUser_Returns403(t *testing.T) {
|
|
|
+ svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
|
|
|
+
|
|
|
+ memberCtx := ctxhelper.MemberCtx("test_product")
|
|
|
+ _, err := NewGetUserPermsLogic(memberCtx, svcCtx).GetUserPerms(&types.GetUserPermsReq{UserId: 999999999})
|
|
|
+ require.Error(t, err)
|
|
|
+ var ce *response.CodeError
|
|
|
+ require.True(t, errors.As(err, &ce))
|
|
|
+ assert.Equal(t, 403, ce.Code(), "必须是 403 而非 404,防止枚举 userId 存在性")
|
|
|
+}
|
|
|
+
|
|
|
+// TC-1263: ADMIN 查不是当前产品成员的用户被拒绝
|
|
|
+func TestGetUserPerms_AdminQueriesNonMember_Forbidden(t *testing.T) {
|
|
|
+ bootstrapCtx := ctxhelper.SuperAdminCtx()
|
|
|
+ svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
|
|
|
+ conn := testutil.GetTestSqlConn()
|
|
|
+
|
|
|
+ // 创建用户,但不加入 test_product
|
|
|
+ username := testutil.UniqueId()
|
|
|
+ userId := insertTestUser(t, bootstrapCtx, username, testutil.HashPassword("pass"))
|
|
|
+
|
|
|
+ t.Cleanup(func() {
|
|
|
+ testutil.CleanTable(bootstrapCtx, conn, "`sys_user`", userId)
|
|
|
+ })
|
|
|
+
|
|
|
+ adminCtx := ctxhelper.AdminCtx("test_product")
|
|
|
+ _, err := NewGetUserPermsLogic(adminCtx, svcCtx).GetUserPerms(&types.GetUserPermsReq{UserId: userId})
|
|
|
+ require.Error(t, err)
|
|
|
+ var ce *response.CodeError
|
|
|
+ require.True(t, errors.As(err, &ce))
|
|
|
+ assert.Equal(t, 403, ce.Code())
|
|
|
+}
|
|
|
+
|
|
|
+// TC-1264: nil UserDetails(模拟无 JWT)返回 401
|
|
|
+func TestGetUserPerms_NoAuth_Returns401(t *testing.T) {
|
|
|
+ svcCtx := svc.NewServiceContext(testutil.GetTestConfig())
|
|
|
+
|
|
|
+ _, err := NewGetUserPermsLogic(ctxhelper.CustomCtx(nil), svcCtx).GetUserPerms(&types.GetUserPermsReq{UserId: 1})
|
|
|
+ require.Error(t, err)
|
|
|
+ var ce *response.CodeError
|
|
|
+ require.True(t, errors.As(err, &ce))
|
|
|
+ assert.Equal(t, 401, ce.Code())
|
|
|
+}
|