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) + } +}