You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
OneAuth/auth/auth_test.go

275 lines
6.9 KiB
Go

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package auth
import (
"context"
"fmt"
"os"
"testing"
"time"
"github.com/veypi/vbase/cfg"
"github.com/veypi/vbase/models"
)
// setupTestDB 初始化测试数据库
func setupTestDB() {
// 使用 SQLite 内存模式
cfg.Config.DB = "sqlite"
cfg.Config.DSN = fmt.Sprintf("file::memory:?cache=shared&_time=%d", time.Now().UnixNano())
// 初始化数据库表
if err := models.AllModels.AutoMigrate(cfg.DB()); err != nil {
panic(err)
}
}
// TestMain 负责测试环境的全局初始化
func TestMain(m *testing.M) {
setupTestDB()
code := m.Run()
os.Exit(code)
}
func getTestAuth() Auth {
// 每次获取一个新的实例名,避免测试间冲突(虽然内存库是共享的,但数据清理可能不完全)
// 为了简单起见,我们在 TestMain 中只初始化一次 DB但可以通过应用名区分
appKey := fmt.Sprintf("test_app_%d", time.Now().UnixNano())
a := Factory.New(appKey, models.AppConfig{
Name: "Test App",
DefaultRoles: []models.RoleDefinition{
{
Code: "admin",
Name: "Administrator",
Policies: []string{"*:*"},
},
{
Code: "editor",
Name: "Editor",
Policies: []string{
"article:create",
"article:read",
"article:update",
},
},
{
Code: "viewer",
Name: "Viewer",
Policies: []string{
"article:read",
},
},
{
Code: "deleter",
Name: "Deleter",
Policies: []string{
"article:delete",
},
},
},
})
// 初始化
if err := a.(*appAuth).init(); err != nil {
panic(err)
}
return a
}
// TestBasicPermission 测试基础权限授予与检查
func TestBasicPermission(t *testing.T) {
a := getTestAuth()
ctx := context.Background()
userID := "user_basic_001"
// 初始状态应该无权限
ok, err := a.CheckPermission(ctx, userID, "", "article:read", "")
if err != nil {
t.Fatalf("CheckPermission failed: %v", err)
}
if ok {
t.Error("Expected no permission initially")
}
// 授予 viewer 角色
if err := a.GrantRole(ctx, userID, "", "viewer"); err != nil {
t.Fatalf("GrantRole failed: %v", err)
}
// 应该有 article:read 权限
ok, err = a.CheckPermission(ctx, userID, "", "article:read", "")
if err != nil {
t.Fatalf("CheckPermission failed: %v", err)
}
if !ok {
t.Error("Expected article:read permission after granting viewer role")
}
// 不应该有 article:create 权限
ok, err = a.CheckPermission(ctx, userID, "", "article:create", "")
if err != nil {
t.Fatalf("CheckPermission failed: %v", err)
}
if ok {
t.Error("Expected no article:create permission for viewer")
}
}
// TestWildcardPermission 测试通配符权限
func TestWildcardPermission(t *testing.T) {
a := getTestAuth()
ctx := context.Background()
userID := "user_admin_001"
// 授予 admin 角色 (*:*)
if err := a.GrantRole(ctx, userID, "", "admin"); err != nil {
t.Fatalf("GrantRole failed: %v", err)
}
// 应该拥有所有权限
tests := []string{
"article:read",
"article:delete",
"user:create",
"config:update", // system:config:update 会被解析为 system 应用的权限,而 admin 是 test_app 的 admin
}
for _, perm := range tests {
ok, err := a.CheckPermission(ctx, userID, "", perm, "")
if err != nil {
t.Errorf("CheckPermission %s failed: %v", perm, err)
continue
}
if !ok {
t.Errorf("Expected permission %s for admin", perm)
}
}
}
// TestStrictPermissionIsolation 验证权限严格隔离性 (用户请求场景)
// 验证:拥有 delete 权限的用户,是否能通过 read 权限检查
func TestStrictPermissionIsolation(t *testing.T) {
a := getTestAuth()
ctx := context.Background()
userID := "user_deleter_001"
// 授予 deleter 角色 (只包含 article:delete)
if err := a.GrantRole(ctx, userID, "", "deleter"); err != nil {
t.Fatalf("GrantRole failed: %v", err)
}
// 1. 检查 article:delete (应该通过)
ok, err := a.CheckPermission(ctx, userID, "", "article:delete", "")
if err != nil {
t.Fatalf("CheckPermission error: %v", err)
}
if !ok {
t.Errorf("Expected user to have article:delete permission")
}
// 2. 检查 article:read (应该失败)
// 关键验证点delete 不包含 read
ok, err = a.CheckPermission(ctx, userID, "", "article:read", "")
if err != nil {
t.Fatalf("CheckPermission error: %v", err)
}
if ok {
t.Errorf("Strict isolation failed: User with 'article:delete' passed 'article:read' check")
}
}
// TestOrgIsolation 测试多租户/组织隔离
func TestOrgIsolation(t *testing.T) {
a := getTestAuth()
ctx := context.Background()
userID := "user_org_001"
orgA := "org_a"
orgB := "org_b"
// 在 OrgA 中授予 editor 角色
if err := a.GrantRole(ctx, userID, orgA, "editor"); err != nil {
t.Fatalf("GrantRole failed: %v", err)
}
// 在 OrgA 上下文中检查 article:create (应该通过)
ok, err := a.CheckPermission(ctx, userID, orgA, "article:create", "")
if err != nil {
t.Fatalf("CheckPermission failed: %v", err)
}
if !ok {
t.Error("Expected permission in OrgA")
}
// 在 OrgB 上下文中检查 article:create (应该失败)
ok, err = a.CheckPermission(ctx, userID, orgB, "article:create", "")
if err != nil {
t.Fatalf("CheckPermission failed: %v", err)
}
if ok {
t.Error("Expected no permission in OrgB (role was granted in OrgA)")
}
// 在全局上下文中检查 (应该失败,因为角色绑定在 OrgA)
ok, err = a.CheckPermission(ctx, userID, "", "article:create", "")
if err != nil {
t.Fatalf("CheckPermission failed: %v", err)
}
if ok {
t.Error("Expected no global permission")
}
}
// TestResourcePermission 测试特定资源权限
func TestResourcePermission(t *testing.T) {
a := getTestAuth()
ctx := context.Background()
userID := "user_res_001"
resID := "doc_123"
// 需要先创建权限定义,因为 GrantResourcePerm 会检查权限是否存在
permID := fmt.Sprintf("%s:doc:read", a.(*appAuth).appKey)
perm := models.Permission{
ID: permID,
AppKey: a.(*appAuth).appKey,
Resource: "doc",
Action: "read",
Description: "Read Doc",
}
if err := cfg.DB().Create(&perm).Error; err != nil {
t.Fatalf("Failed to create permission: %v", err)
}
// 初始无权限
ok, err := a.CheckPermission(ctx, userID, "", "doc:read", resID)
if err != nil {
t.Fatalf("CheckPermission failed: %v", err)
}
if ok {
t.Error("Expected no permission")
}
// 授予对特定资源 doc_123 的 doc:read 权限
if err := a.GrantResourcePerm(ctx, userID, "", "doc:read", resID); err != nil {
t.Fatalf("GrantResourcePerm failed: %v", err)
}
// 检查 doc:read on doc_123 (应该通过)
ok, err = a.CheckPermission(ctx, userID, "", "doc:read", resID)
if err != nil {
t.Fatalf("CheckPermission failed: %v", err)
}
if !ok {
t.Error("Expected permission on specific resource")
}
// 检查 doc:read on doc_456 (应该失败)
ok, err = a.CheckPermission(ctx, userID, "", "doc:read", "doc_456")
if err != nil {
t.Fatalf("CheckPermission failed: %v", err)
}
if ok {
t.Error("Expected no permission on other resource")
}
}