180 lines
6.5 KiB
Go
180 lines
6.5 KiB
Go
package data
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
|
|
"kra/internal/biz"
|
|
"kra/internal/conf"
|
|
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
func authorityUintPointer(value uint) *uint { return &value }
|
|
|
|
func enableStrictAuthorityTestMode(data *Data) {
|
|
currentData, _ := data.runtime.Values()
|
|
data.runtime.Replace(currentData, &conf.AdminBackend{System: &conf.AdminBackend_System{UseStrictAuth: true}})
|
|
}
|
|
|
|
func TestCopyAuthorityStrictPolicyValidationMatchesAdministrationContract(t *testing.T) {
|
|
data := newTransactionTestData(t)
|
|
enableStrictAuthorityTestMode(data)
|
|
db := data.gormDB.WithContext(context.Background())
|
|
rootID := uint(888)
|
|
if err := db.Create(&[]authorityPO{
|
|
{AuthorityID: rootID, AuthorityName: "root", ParentID: authorityUintPointer(0)},
|
|
{AuthorityID: 900, AuthorityName: "source", ParentID: &rootID},
|
|
{AuthorityID: 901, AuthorityName: "stale-source", ParentID: &rootID},
|
|
}).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := db.Create(&apiPO{Path: "/registered", Method: "GET"}).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := db.Create(&[]casbinRulePO{
|
|
newPolicyRule(900, "/registered", "GET"),
|
|
newPolicyRule(901, "/stale", "POST"),
|
|
// A root role is allowed to grant registered APIs without already
|
|
// holding the policy, but a stale policy is still not a registered API.
|
|
newPolicyRule(rootID, "/stale", "POST"),
|
|
}).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
repo := &authorityAccessRepo{data: data}
|
|
ctx := biz.NewActorContext(context.Background(), biz.Actor{AuthorityID: rootID})
|
|
created := &biz.Authority{AuthorityID: 910, AuthorityName: "copy", ParentID: &rootID}
|
|
if err := repo.CopyAuthority(ctx, 900, created); err != nil {
|
|
t.Fatalf("root copy of a registered API failed: %v", err)
|
|
}
|
|
if exists, err := policyExists(db, created.AuthorityID, "/registered", "GET"); err != nil || !exists {
|
|
t.Fatalf("copied policy exists = %v, err = %v", exists, err)
|
|
}
|
|
|
|
staleCopy := &biz.Authority{AuthorityID: 911, AuthorityName: "stale-copy", ParentID: &rootID}
|
|
if err := repo.CopyAuthority(ctx, 901, staleCopy); err == nil || err.Error() != "存在api不在权限列表中" {
|
|
t.Fatalf("stale API copy error = %v", err)
|
|
}
|
|
var count int64
|
|
if err := db.Unscoped().Model(&authorityPO{}).Where("authority_id = ?", staleCopy.AuthorityID).Count(&count).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if count != 0 {
|
|
t.Fatalf("failed copy persisted target authority: count=%d", count)
|
|
}
|
|
}
|
|
|
|
func TestCopyAuthorityDuplicateIDWinsOverStrictParentValidation(t *testing.T) {
|
|
data := newTransactionTestData(t)
|
|
enableStrictAuthorityTestMode(data)
|
|
db := data.gormDB.WithContext(context.Background())
|
|
rootID := uint(888)
|
|
if err := db.Create(&[]authorityPO{
|
|
{AuthorityID: rootID, AuthorityName: "root", ParentID: authorityUintPointer(0)},
|
|
{AuthorityID: 920, AuthorityName: "existing", ParentID: &rootID},
|
|
}).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
invalidParent := uint(999999)
|
|
ctx := biz.NewActorContext(context.Background(), biz.Actor{AuthorityID: rootID})
|
|
err := (&authorityAccessRepo{data: data}).CopyAuthority(ctx, 0, &biz.Authority{AuthorityID: 920, AuthorityName: "duplicate", ParentID: &invalidParent})
|
|
if err == nil || err.Error() != "存在相同角色id" {
|
|
t.Fatalf("duplicate copy error = %v", err)
|
|
}
|
|
}
|
|
|
|
func TestListAuthoritiesRequiresCurrentAuthorityOutsideStrictMode(t *testing.T) {
|
|
data := newTransactionTestData(t)
|
|
ctx := biz.NewActorContext(context.Background(), biz.Actor{AuthorityID: 999999})
|
|
if _, err := (&authorityAccessRepo{data: data}).ListAuthorities(ctx); !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
t.Fatalf("missing current authority error = %v", err)
|
|
}
|
|
}
|
|
|
|
func TestDeleteAuthorityIgnoresOrphanedUserAssociation(t *testing.T) {
|
|
data := newTransactionTestData(t)
|
|
ctx := context.Background()
|
|
db := data.gormDB.WithContext(ctx)
|
|
if err := db.Create(&authorityPO{AuthorityID: 930, AuthorityName: "orphan-links"}).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := db.Create(&userAuthorityPO{SysUserID: 999999, SysAuthorityAuthorityID: 930}).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := (&authorityAccessRepo{data: data}).DeleteAuthority(ctx, 930); err != nil {
|
|
t.Fatalf("delete with orphaned user association: %v", err)
|
|
}
|
|
var authority authorityPO
|
|
if err := db.Unscoped().Where("authority_id = ?", 930).First(&authority).Error; !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
t.Fatalf("deleted authority lookup error = %v", err)
|
|
}
|
|
var links int64
|
|
if err := db.Model(&userAuthorityPO{}).Where("sys_authority_authority_id = ?", 930).Count(&links).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if links != 0 {
|
|
t.Fatalf("orphaned user associations were not removed: %d", links)
|
|
}
|
|
}
|
|
|
|
func TestSetAuthorityUsersKeepsPrimaryRoleFallbackBehavior(t *testing.T) {
|
|
data := newTransactionTestData(t)
|
|
ctx := context.Background()
|
|
db := data.gormDB.WithContext(ctx)
|
|
if err := db.Create(&[]authorityPO{{AuthorityID: 940, AuthorityName: "removed"}, {AuthorityID: 941, AuthorityName: "fallback"}}).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
users := []userPO{
|
|
{Username: "with-fallback", Password: "hash", AuthorityID: 940, Enable: 1},
|
|
{Username: "without-fallback", Password: "hash", AuthorityID: 940, Enable: 1},
|
|
}
|
|
if err := db.Create(&users).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := db.Create(&[]userAuthorityPO{
|
|
{SysUserID: users[0].ID, SysAuthorityAuthorityID: 940},
|
|
{SysUserID: users[0].ID, SysAuthorityAuthorityID: 941},
|
|
{SysUserID: users[1].ID, SysAuthorityAuthorityID: 940},
|
|
}).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := (&authorityAccessRepo{data: data}).SetAuthorityUsers(ctx, 940, nil); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var persisted []userPO
|
|
if err := db.Where("id IN ?", []uint{users[0].ID, users[1].ID}).Order("id").Find(&persisted).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(persisted) != 2 || persisted[0].AuthorityID != 941 || persisted[1].AuthorityID != 940 {
|
|
t.Fatalf("primary authority fallback = %+v", persisted)
|
|
}
|
|
}
|
|
|
|
func TestAuthorityCustomDataScopeUsesCompatibleColumns(t *testing.T) {
|
|
data := newTransactionTestData(t)
|
|
ctx := context.Background()
|
|
repo := &authorityAccessRepo{data: data}
|
|
if err := repo.SetDataScope(ctx, 950, 5, []uint{7, 7, 8}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ids, err := repo.DataScopeDepartmentIDs(ctx, 950)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(ids) != 3 || ids[0] != 7 || ids[1] != 7 || ids[2] != 8 {
|
|
t.Fatalf("custom department IDs = %v", ids)
|
|
}
|
|
if err = repo.SetDataScope(ctx, 950, 3, []uint{9}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
ids, err = repo.DataScopeDepartmentIDs(ctx, 950)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(ids) != 0 {
|
|
t.Fatalf("non-custom scope retained departments: %v", ids)
|
|
}
|
|
}
|