From 9fa25d5ce57c113291d4e36adb7c5f7f49537139 Mon Sep 17 00:00:00 2001 From: smail Date: Fri, 2 Oct 2026 03:53:35 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A1=A5=E5=85=85=E5=88=86=E7=BB=84=E6=9D=83?= =?UTF-8?q?=E9=99=90=E5=9B=9E=E5=BD=92=E7=94=A8=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 覆盖管理员所属分组范围、默认分组回退、管理员多分组和普通用户单分组约束。 --- backend/services/accountgroup/service_test.go | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/backend/services/accountgroup/service_test.go b/backend/services/accountgroup/service_test.go index a7b43bf..8a0cf77 100644 --- a/backend/services/accountgroup/service_test.go +++ b/backend/services/accountgroup/service_test.go @@ -41,3 +41,97 @@ func TestCanAccessExcludesSuperAdminFromAdminScope(t *testing.T) { t.Fatal("管理员应访问同组普通用户账户") } } + +func TestMembershipScopeGuardLimitsAdminToOwnedGroups(t *testing.T) { + engine, err := xorm.NewEngine("sqlite", ":memory:") + if err != nil { + t.Fatal(err) + } + defer engine.Close() + + if err := engine.Sync2(new(models.User), new(models.AccountGroup), new(models.AccountGroupMember)); err != nil { + t.Fatal(err) + } + admin := models.User{Account: "admin", Name: "分组管理员", Role: string(access.RoleAdmin), IsAdmin: 1} + superAdmin := models.User{Account: "superadmin", Name: "超级管理员", Role: string(access.RoleSuperAdmin)} + if _, err := engine.Insert(&admin, &superAdmin); err != nil { + t.Fatal(err) + } + if _, err := engine.Insert( + &models.AccountGroup{ID: 1, Name: "一组"}, + &models.AccountGroup{ID: 2, Name: "二组"}, + &models.AccountGroupMember{UserID: admin.ID, GroupID: 1}, + ); err != nil { + t.Fatal(err) + } + + tx := engine.NewSession() + defer tx.Close() + if err := tx.Begin(); err != nil { + t.Fatal(err) + } + defer tx.Rollback() + + if err := MembershipScopeGuard(admin.ID, access.RoleAdmin, []int{1})(tx); err != nil { + t.Fatalf("管理员应能管理所属分组:%v", err) + } + if err := MembershipScopeGuard(admin.ID, access.RoleAdmin, []int{2})(tx); err != ErrDenied { + t.Fatalf("管理员不应管理其他分组,得到:%v", err) + } + if err := MembershipScopeGuard(superAdmin.ID, access.RoleSuperAdmin, []int{2})(tx); err != nil { + t.Fatalf("超级管理员应能管理全部分组:%v", err) + } +} + +func TestMembershipScopeGuardUsesDefaultGroupWhenAdminHasNoMembership(t *testing.T) { + engine, err := xorm.NewEngine("sqlite", ":memory:") + if err != nil { + t.Fatal(err) + } + defer engine.Close() + + if err := engine.Sync2(new(models.User), new(models.AccountGroupMember)); err != nil { + t.Fatal(err) + } + admin := models.User{Account: "admin", Name: "默认分组管理员", Role: string(access.RoleAdmin), IsAdmin: 1} + if _, err := engine.Insert(&admin); err != nil { + t.Fatal(err) + } + + tx := engine.NewSession() + defer tx.Close() + if err := tx.Begin(); err != nil { + t.Fatal(err) + } + defer tx.Rollback() + + if err := MembershipScopeGuard(admin.ID, access.RoleAdmin, []int{0})(tx); err != nil { + t.Fatalf("无明确分组的管理员应能管理默认分组:%v", err) + } + if err := MembershipScopeGuard(admin.ID, access.RoleAdmin, []int{1})(tx); err != ErrDenied { + t.Fatalf("无明确分组的管理员不应管理其他分组,得到:%v", err) + } +} + +func TestValidateMembershipKeepsAdminMultiGroupAndUserSingleGroupRules(t *testing.T) { + engine, err := xorm.NewEngine("sqlite", ":memory:") + if err != nil { + t.Fatal(err) + } + defer engine.Close() + if err := engine.Sync2(new(models.AccountGroup)); err != nil { + t.Fatal(err) + } + if _, err := engine.Insert(&models.AccountGroup{ID: 1, Name: "一组"}, &models.AccountGroup{ID: 2, Name: "二组"}); err != nil { + t.Fatal(err) + } + + tx := engine.NewSession() + defer tx.Close() + if ids, err := ValidateMembership(tx, access.RoleAdmin, []int{1, 2}); err != nil || len(ids) != 2 { + t.Fatalf("管理员应允许多个分组,ids=%v err=%v", ids, err) + } + if _, err := ValidateMembership(tx, access.RoleUser, []int{1, 2}); err != ErrInvalid { + t.Fatalf("普通用户不应允许多个分组,得到:%v", err) + } +}