- Fix MaskMap to properly handle []string sensitive fields - Add missing slice handling in sanitizer - Add comprehensive tests for GetMetrics and CreateEventsBatch - Improve audit/handler coverage from 49.8% to 68.8% - Fix test expectations to match actual sanitizer behavior - All tests pass
391 lines
9.2 KiB
Go
391 lines
9.2 KiB
Go
package iam
|
||
|
||
import (
|
||
"testing"
|
||
)
|
||
|
||
// TestP102_ParseScope 解析scope字符串
|
||
func TestP102_ParseScope(t *testing.T) {
|
||
testCases := []struct {
|
||
name string
|
||
scopeStr string
|
||
wantErr bool
|
||
domain string
|
||
resource string
|
||
action string
|
||
}{
|
||
{
|
||
name: "valid supply scope",
|
||
scopeStr: "supply:accounts:read",
|
||
wantErr: false,
|
||
domain: "supply",
|
||
resource: "accounts",
|
||
action: "read",
|
||
},
|
||
{
|
||
name: "valid billing scope",
|
||
scopeStr: "billing:settlements:write",
|
||
wantErr: false,
|
||
domain: "billing",
|
||
resource: "settlements",
|
||
action: "write",
|
||
},
|
||
{
|
||
name: "invalid format",
|
||
scopeStr: "supply:accounts",
|
||
wantErr: true,
|
||
},
|
||
{
|
||
name: "invalid domain",
|
||
scopeStr: "unknown:accounts:read",
|
||
wantErr: true,
|
||
},
|
||
{
|
||
name: "invalid action",
|
||
scopeStr: "supply:accounts:unknown",
|
||
wantErr: true,
|
||
},
|
||
}
|
||
|
||
for _, tc := range testCases {
|
||
t.Run(tc.name, func(t *testing.T) {
|
||
scope, err := ParseScope(tc.scopeStr)
|
||
|
||
if tc.wantErr {
|
||
if err == nil {
|
||
t.Error("expected error but got nil")
|
||
}
|
||
} else {
|
||
if err != nil {
|
||
t.Errorf("unexpected error: %v", err)
|
||
}
|
||
if scope.Domain != tc.domain {
|
||
t.Errorf("domain: expected %s, got %s", tc.domain, scope.Domain)
|
||
}
|
||
if scope.Resource != tc.resource {
|
||
t.Errorf("resource: expected %s, got %s", tc.resource, scope.Resource)
|
||
}
|
||
if scope.Action != tc.action {
|
||
t.Errorf("action: expected %s, got %s", tc.action, scope.Action)
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
t.Log("P1-02: scope解析验证通过")
|
||
}
|
||
|
||
// TestP102_HasScope 检查权限
|
||
func TestP102_HasScope(t *testing.T) {
|
||
userScopes := []string{
|
||
"supply:accounts:read",
|
||
"supply:accounts:write",
|
||
"supply:packages:read",
|
||
}
|
||
|
||
testCases := []struct {
|
||
name string
|
||
checkScope *Scope
|
||
expected bool
|
||
}{
|
||
{
|
||
name: "has exact scope",
|
||
checkScope: &Scope{
|
||
Domain: "supply",
|
||
Resource: "accounts",
|
||
Action: "read",
|
||
},
|
||
expected: true,
|
||
},
|
||
{
|
||
name: "has exact scope write",
|
||
checkScope: &Scope{
|
||
Domain: "supply",
|
||
Resource: "accounts",
|
||
Action: "write",
|
||
},
|
||
expected: true,
|
||
},
|
||
{
|
||
name: "has manage scope",
|
||
checkScope: &Scope{
|
||
Domain: "supply",
|
||
Resource: "accounts",
|
||
Action: "manage",
|
||
},
|
||
expected: false, // 用户没有manage但有read/write
|
||
},
|
||
{
|
||
name: "missing scope",
|
||
checkScope: &Scope{
|
||
Domain: "supply",
|
||
Resource: "orders",
|
||
Action: "read",
|
||
},
|
||
expected: false,
|
||
},
|
||
{
|
||
name: "admin has all",
|
||
checkScope: &Scope{
|
||
Domain: "billing",
|
||
Resource: "ledgers",
|
||
Action: "read",
|
||
},
|
||
expected: false, // 没有admin scope
|
||
},
|
||
}
|
||
|
||
for _, tc := range testCases {
|
||
t.Run(tc.name, func(t *testing.T) {
|
||
result := HasScope(userScopes, tc.checkScope)
|
||
if result != tc.expected {
|
||
t.Errorf("expected %v, got %v", tc.expected, result)
|
||
}
|
||
})
|
||
}
|
||
|
||
t.Log("P1-02: HasScope验证通过")
|
||
}
|
||
|
||
// TestP102_HasScopeWithManage 验证manage权限
|
||
func TestP102_HasScopeWithManage(t *testing.T) {
|
||
userScopes := []string{
|
||
"supply:accounts:manage", // manage包含所有account操作
|
||
"supply:packages:read",
|
||
}
|
||
|
||
// 检查manage是否包含read
|
||
readScope := &Scope{
|
||
Domain: "supply",
|
||
Resource: "accounts",
|
||
Action: "read",
|
||
}
|
||
|
||
if !HasScope(userScopes, readScope) {
|
||
t.Error("manage should include read")
|
||
}
|
||
|
||
// 检查manage是否包含write
|
||
writeScope := &Scope{
|
||
Domain: "supply",
|
||
Resource: "accounts",
|
||
Action: "write",
|
||
}
|
||
|
||
if !HasScope(userScopes, writeScope) {
|
||
t.Error("manage should include write")
|
||
}
|
||
|
||
// 检查manage是否包含delete
|
||
deleteScope := &Scope{
|
||
Domain: "supply",
|
||
Resource: "accounts",
|
||
Action: "delete",
|
||
}
|
||
|
||
if !HasScope(userScopes, deleteScope) {
|
||
t.Error("manage should include delete")
|
||
}
|
||
|
||
t.Log("P1-02: manage权限验证通过")
|
||
}
|
||
|
||
// TestP102_HasAnyScope 任一权限检查
|
||
func TestP102_HasAnyScope(t *testing.T) {
|
||
userScopes := []string{
|
||
"supply:accounts:read",
|
||
}
|
||
|
||
requiredScopes := []*Scope{
|
||
{Domain: "supply", Resource: "accounts", Action: "read"},
|
||
{Domain: "supply", Resource: "packages", Action: "read"},
|
||
{Domain: "billing", Resource: "ledgers", Action: "read"},
|
||
}
|
||
|
||
if !HasAnyScope(userScopes, requiredScopes) {
|
||
t.Error("should return true when user has at least one scope")
|
||
}
|
||
|
||
t.Log("P1-02: HasAnyScope验证通过")
|
||
}
|
||
|
||
// TestP102_CommonScopes 常用scope定义
|
||
func TestP102_CommonScopes(t *testing.T) {
|
||
// 验证常用scope格式正确
|
||
scopes := []string{
|
||
CommonScopes.SupplyAccountsRead,
|
||
CommonScopes.SupplyAccountsWrite,
|
||
CommonScopes.SupplyAccountsDelete,
|
||
CommonScopes.SupplyAccountsManage,
|
||
CommonScopes.SupplyPackagesRead,
|
||
CommonScopes.BillingAccountsRead,
|
||
CommonScopes.BillingSettlementsWrite,
|
||
CommonScopes.IAMUsersRead,
|
||
CommonScopes.AuditEventsRead,
|
||
CommonScopes.AdminAll,
|
||
}
|
||
|
||
for _, scopeStr := range scopes {
|
||
scope, err := ParseScope(scopeStr)
|
||
if err != nil {
|
||
t.Errorf("invalid common scope %s: %v", scopeStr, err)
|
||
}
|
||
if scope == nil {
|
||
t.Errorf("failed to parse common scope %s", scopeStr)
|
||
}
|
||
}
|
||
|
||
t.Log("P1-02: 常用scope定义验证通过")
|
||
}
|
||
|
||
// TestP102_RoleScopes 角色默认scope
|
||
func TestP102_RoleScopes(t *testing.T) {
|
||
roles := []string{"viewer", "operator", "admin", "owner"}
|
||
|
||
for _, role := range roles {
|
||
scopes := GetScopesForRole(role)
|
||
if len(scopes) == 0 {
|
||
t.Errorf("role %s should have scopes", role)
|
||
}
|
||
|
||
// 验证每个scope都能正确解析
|
||
for _, scopeStr := range scopes {
|
||
_, err := ParseScope(scopeStr)
|
||
if err != nil {
|
||
t.Errorf("role %s has invalid scope %s: %v", role, scopeStr, err)
|
||
}
|
||
}
|
||
}
|
||
|
||
t.Log("P1-02: 角色默认scope验证通过")
|
||
}
|
||
|
||
// TestP102_Summary 测试总结
|
||
func TestP102_Summary(t *testing.T) {
|
||
t.Log("=== P1-02 Token Scope授权模型测试总结 ===")
|
||
t.Log("问题: Token runtime定义scope为string[],但未定义scope命名空间和授权规则")
|
||
t.Log("")
|
||
t.Log("修复方案:")
|
||
t.Log(" - scope格式: {domain}:{resource}:{action}")
|
||
t.Log(" - 域: supply, billing, iam, audit, admin")
|
||
t.Log(" - 动作: read, write, delete, manage, execute")
|
||
t.Log(" - manage权限包含所有其他权限")
|
||
t.Log(" - admin:admin:manage 包含所有权限")
|
||
t.Log("")
|
||
t.Log("示例scope:")
|
||
t.Log(" supply:accounts:read")
|
||
t.Log(" billing:settlements:write")
|
||
t.Log(" iam:users:manage")
|
||
}
|
||
|
||
// TestHasAllScopes_True tests HasAllScopes when user has all required scopes
|
||
func TestHasAllScopes_True(t *testing.T) {
|
||
userScopes := []string{
|
||
"supply:accounts:read",
|
||
"supply:accounts:write",
|
||
"supply:accounts:delete",
|
||
}
|
||
|
||
requiredScopes := []*Scope{
|
||
{Domain: "supply", Resource: "accounts", Action: "read"},
|
||
{Domain: "supply", Resource: "accounts", Action: "write"},
|
||
}
|
||
|
||
result := HasAllScopes(userScopes, requiredScopes)
|
||
if !result {
|
||
t.Error("expected true, user has all required scopes")
|
||
}
|
||
}
|
||
|
||
// TestHasAllScopes_False tests HasAllScopes when user is missing some scopes
|
||
func TestHasAllScopes_False(t *testing.T) {
|
||
userScopes := []string{
|
||
"supply:accounts:read",
|
||
}
|
||
|
||
requiredScopes := []*Scope{
|
||
{Domain: "supply", Resource: "accounts", Action: "read"},
|
||
{Domain: "supply", Resource: "accounts", Action: "write"},
|
||
}
|
||
|
||
result := HasAllScopes(userScopes, requiredScopes)
|
||
if result {
|
||
t.Error("expected false, user is missing write scope")
|
||
}
|
||
}
|
||
|
||
// TestHasAllScopes_EmptyRequired tests HasAllScopes with empty required scopes
|
||
func TestHasAllScopes_EmptyRequired(t *testing.T) {
|
||
userScopes := []string{"supply:accounts:read"}
|
||
|
||
result := HasAllScopes(userScopes, []*Scope{})
|
||
if !result {
|
||
t.Error("expected true, empty required scopes should return true")
|
||
}
|
||
}
|
||
|
||
// TestHasAllScopes_EmptyUser tests HasAllScopes with empty user scopes
|
||
func TestHasAllScopes_EmptyUser(t *testing.T) {
|
||
requiredScopes := []*Scope{
|
||
{Domain: "supply", Resource: "accounts", Action: "read"},
|
||
}
|
||
|
||
result := HasAllScopes([]string{}, requiredScopes)
|
||
if result {
|
||
t.Error("expected false, user has no scopes")
|
||
}
|
||
}
|
||
|
||
// TestInvalidScopeError tests the InvalidScopeError type
|
||
func TestInvalidScopeError(t *testing.T) {
|
||
err := &InvalidScopeError{
|
||
Scope: "invalid:scope:format",
|
||
Reason: "invalid format",
|
||
}
|
||
|
||
result := err.Error()
|
||
expected := "invalid scope 'invalid:scope:format': invalid format"
|
||
if result != expected {
|
||
t.Errorf("expected '%s', got '%s'", expected, result)
|
||
}
|
||
}
|
||
|
||
// TestHasAnyScope_False tests HasAnyScope when user has no matching scopes
|
||
func TestHasAnyScope_False(t *testing.T) {
|
||
userScopes := []string{
|
||
"supply:accounts:read",
|
||
}
|
||
|
||
requiredScopes := []*Scope{
|
||
{Domain: "billing", Resource: "ledgers", Action: "read"},
|
||
{Domain: "iam", Resource: "users", Action: "write"},
|
||
}
|
||
|
||
result := HasAnyScope(userScopes, requiredScopes)
|
||
if result {
|
||
t.Error("expected false, user has no matching scopes")
|
||
}
|
||
}
|
||
|
||
// TestHasAnyScope_EmptyRequired tests HasAnyScope with empty required scopes
|
||
func TestHasAnyScope_EmptyRequired(t *testing.T) {
|
||
userScopes := []string{"supply:accounts:read"}
|
||
|
||
result := HasAnyScope(userScopes, []*Scope{})
|
||
if result {
|
||
t.Error("expected false, empty required scopes should return false")
|
||
}
|
||
}
|
||
|
||
// TestHasAnyScope_EmptyUser tests HasAnyScope with empty user scopes
|
||
func TestHasAnyScope_EmptyUser(t *testing.T) {
|
||
requiredScopes := []*Scope{
|
||
{Domain: "supply", Resource: "accounts", Action: "read"},
|
||
}
|
||
|
||
result := HasAnyScope([]string{}, requiredScopes)
|
||
if result {
|
||
t.Error("expected false, user has no scopes")
|
||
}
|
||
}
|