- Updated ACL configuration to include Cache and Provider fields for improved role management. - Initialized Role Manager within the ACL setup, ensuring proper role handling and cache management. - Enhanced tests to verify access denial and error handling for unmatched endpoints, improving overall test coverage and clarity in error responses.
538 lines
14 KiB
Go
538 lines
14 KiB
Go
package role_test
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"github.com/yaoapp/kun/maps"
|
|
"github.com/yaoapp/yao/openapi/oauth"
|
|
"github.com/yaoapp/yao/openapi/oauth/acl/role"
|
|
"github.com/yaoapp/yao/openapi/tests/testutils"
|
|
)
|
|
|
|
// Role Manager Test Suite
|
|
//
|
|
// PREREQUISITES:
|
|
// Before running tests, source the environment file:
|
|
// source $YAO_DEV/env.local.sh
|
|
//
|
|
// Then run tests:
|
|
// go test -v ./openapi/tests/oauth/acl/role/... -count=1
|
|
|
|
func TestNewManager(t *testing.T) {
|
|
testutils.Prepare(t)
|
|
defer testutils.Clean()
|
|
|
|
t.Run("CreateManagerWithProvider", func(t *testing.T) {
|
|
// Get cache and provider from OAuth service
|
|
cache := oauth.OAuth.GetCache()
|
|
require.NotNil(t, cache)
|
|
|
|
provider, err := oauth.OAuth.GetUserProvider()
|
|
require.NoError(t, err)
|
|
require.NotNil(t, provider)
|
|
|
|
// Create manager
|
|
manager := role.NewManager(cache, provider)
|
|
assert.NotNil(t, manager)
|
|
|
|
t.Log("Successfully created role manager with cache and provider")
|
|
})
|
|
|
|
t.Run("CreateManagerWithNilProvider", func(t *testing.T) {
|
|
// Get cache from OAuth service
|
|
cache := oauth.OAuth.GetCache()
|
|
require.NotNil(t, cache)
|
|
|
|
// Create manager with nil provider
|
|
manager := role.NewManager(cache, nil)
|
|
assert.NotNil(t, manager)
|
|
|
|
// Should work but will error when trying to get roles
|
|
ctx := context.Background()
|
|
_, err := manager.GetUserRole(ctx, "test-user")
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "user provider is not configured")
|
|
|
|
t.Log("Manager with nil provider correctly returns error")
|
|
})
|
|
}
|
|
|
|
func TestManagerWithNilCache(t *testing.T) {
|
|
testutils.Prepare(t)
|
|
defer testutils.Clean()
|
|
|
|
// Get provider
|
|
provider, err := oauth.OAuth.GetUserProvider()
|
|
require.NoError(t, err)
|
|
require.NotNil(t, provider)
|
|
|
|
// Create manager with nil cache
|
|
manager := role.NewManager(nil, provider)
|
|
require.NotNil(t, manager)
|
|
|
|
ctx := context.Background()
|
|
|
|
t.Run("GetUserRoleWithNilCache", func(t *testing.T) {
|
|
// Create a test role and user
|
|
testRoleID := "test_role_nil_cache"
|
|
roleData := maps.MapStrAny{
|
|
"role_id": testRoleID,
|
|
"name": "Test Nil Cache Role",
|
|
"description": "Role for nil cache testing",
|
|
"is_active": true,
|
|
}
|
|
_, err := provider.CreateRole(ctx, roleData)
|
|
if err != nil {
|
|
t.Skipf("Skipping test - cannot create role: %v", err)
|
|
return
|
|
}
|
|
defer provider.DeleteRole(ctx, testRoleID)
|
|
|
|
userID, err := provider.GenerateUserID(ctx, true)
|
|
require.NoError(t, err)
|
|
|
|
userData := maps.MapStrAny{
|
|
"user_id": userID,
|
|
"email": "nilcache@example.com",
|
|
"role_id": testRoleID,
|
|
"status": "active",
|
|
}
|
|
_, err = provider.CreateUser(ctx, userData)
|
|
require.NoError(t, err)
|
|
defer provider.DeleteUser(ctx, userID)
|
|
|
|
// Get user role - should work even without cache
|
|
roleID, err := manager.GetUserRole(ctx, userID)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, testRoleID, roleID)
|
|
|
|
t.Log("Successfully retrieved role without cache")
|
|
})
|
|
|
|
t.Run("GetScopesWithNilCache", func(t *testing.T) {
|
|
// Create test role with permissions
|
|
testRoleID := "test_role_scopes_nil_cache"
|
|
permissions := maps.MapStrAny{
|
|
"read": true,
|
|
"write": true,
|
|
}
|
|
restrictedPermissions := []string{"admin"}
|
|
|
|
roleData := maps.MapStrAny{
|
|
"role_id": testRoleID,
|
|
"name": "Test Nil Cache Scopes",
|
|
"description": "Role for scopes nil cache testing",
|
|
"is_active": true,
|
|
"permissions": permissions,
|
|
"restricted_permissions": restrictedPermissions,
|
|
}
|
|
_, err := provider.CreateRole(ctx, roleData)
|
|
if err != nil {
|
|
t.Skipf("Skipping test - cannot create role: %v", err)
|
|
return
|
|
}
|
|
defer provider.DeleteRole(ctx, testRoleID)
|
|
|
|
// Get scopes - should work even without cache
|
|
allowed, restricted, err := manager.GetScopes(ctx, testRoleID)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, allowed)
|
|
assert.NotNil(t, restricted)
|
|
|
|
t.Logf("Successfully retrieved scopes without cache: allowed=%v, restricted=%v", allowed, restricted)
|
|
})
|
|
|
|
t.Run("ClearCacheWithNilCache", func(t *testing.T) {
|
|
// Should not panic with nil cache
|
|
err := manager.ClearCache()
|
|
assert.NoError(t, err)
|
|
|
|
t.Log("ClearCache works safely with nil cache")
|
|
})
|
|
}
|
|
|
|
func TestGetUserRole(t *testing.T) {
|
|
testutils.Prepare(t)
|
|
defer testutils.Clean()
|
|
|
|
// Setup
|
|
cache := oauth.OAuth.GetCache()
|
|
require.NotNil(t, cache)
|
|
|
|
provider, err := oauth.OAuth.GetUserProvider()
|
|
require.NoError(t, err)
|
|
require.NotNil(t, provider)
|
|
|
|
manager := role.NewManager(cache, provider)
|
|
ctx := context.Background()
|
|
|
|
t.Run("GetRoleForExistingUser", func(t *testing.T) {
|
|
// First, create a test role using provider
|
|
testRoleID := "test_role_get_user"
|
|
roleData := maps.MapStrAny{
|
|
"role_id": testRoleID,
|
|
"name": "Test Role",
|
|
"description": "Role for testing",
|
|
"is_active": true,
|
|
}
|
|
|
|
// Create role
|
|
createdRoleID, err := provider.CreateRole(ctx, roleData)
|
|
if err != nil {
|
|
t.Skipf("Skipping test - cannot create role: %v", err)
|
|
return
|
|
}
|
|
require.NotEmpty(t, createdRoleID)
|
|
defer provider.DeleteRole(ctx, testRoleID)
|
|
|
|
// Generate unique user ID
|
|
userID, err := provider.GenerateUserID(ctx, true)
|
|
require.NoError(t, err)
|
|
|
|
// Create test user with role
|
|
userData := maps.MapStrAny{
|
|
"user_id": userID,
|
|
"email": "testuser@example.com",
|
|
"role_id": testRoleID,
|
|
"status": "active",
|
|
}
|
|
_, err = provider.CreateUser(ctx, userData)
|
|
require.NoError(t, err)
|
|
defer provider.DeleteUser(ctx, userID)
|
|
|
|
// Get user role
|
|
roleID, err := manager.GetUserRole(ctx, userID)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, testRoleID, roleID)
|
|
|
|
t.Logf("Successfully retrieved role %s for user %s", roleID, userID)
|
|
|
|
// Get again (should come from cache)
|
|
roleID2, err := manager.GetUserRole(ctx, userID)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, testRoleID, roleID2)
|
|
|
|
t.Log("Successfully retrieved role from cache")
|
|
})
|
|
|
|
t.Run("GetRoleForNonExistentUser", func(t *testing.T) {
|
|
_, err := manager.GetUserRole(ctx, "non-existent-user-12345")
|
|
assert.Error(t, err)
|
|
|
|
t.Log("Correctly returns error for non-existent user")
|
|
})
|
|
|
|
t.Run("GetRoleForUserWithoutRole", func(t *testing.T) {
|
|
// Create user without role
|
|
userID, err := provider.GenerateUserID(ctx, true)
|
|
require.NoError(t, err)
|
|
|
|
userData := maps.MapStrAny{
|
|
"user_id": userID,
|
|
"email": "norole@example.com",
|
|
"status": "active",
|
|
}
|
|
_, err = provider.CreateUser(ctx, userData)
|
|
require.NoError(t, err)
|
|
defer provider.DeleteUser(ctx, userID)
|
|
|
|
_, err = manager.GetUserRole(ctx, userID)
|
|
assert.Error(t, err)
|
|
|
|
t.Log("Correctly returns error for user without role")
|
|
})
|
|
}
|
|
|
|
func TestGetMemberRole(t *testing.T) {
|
|
testutils.Prepare(t)
|
|
defer testutils.Clean()
|
|
|
|
// Setup
|
|
cache := oauth.OAuth.GetCache()
|
|
require.NotNil(t, cache)
|
|
|
|
provider, err := oauth.OAuth.GetUserProvider()
|
|
require.NoError(t, err)
|
|
require.NotNil(t, provider)
|
|
|
|
manager := role.NewManager(cache, provider)
|
|
ctx := context.Background()
|
|
|
|
t.Run("GetRoleForExistingMember", func(t *testing.T) {
|
|
// Create test role
|
|
testRoleID := "test_role_member"
|
|
roleData := maps.MapStrAny{
|
|
"role_id": testRoleID,
|
|
"name": "Test Member Role",
|
|
"description": "Role for member testing",
|
|
"is_active": true,
|
|
}
|
|
_, err := provider.CreateRole(ctx, roleData)
|
|
if err != nil {
|
|
t.Skipf("Skipping test - cannot create role: %v", err)
|
|
return
|
|
}
|
|
defer provider.DeleteRole(ctx, testRoleID)
|
|
|
|
// Create test team
|
|
teamID, err := provider.GenerateUserID(ctx, true)
|
|
require.NoError(t, err)
|
|
|
|
teamData := maps.MapStrAny{
|
|
"team_id": teamID,
|
|
"name": "Test Team",
|
|
"owner_id": "test_owner",
|
|
"status": "active",
|
|
}
|
|
_, err = provider.CreateTeam(ctx, teamData)
|
|
require.NoError(t, err)
|
|
defer provider.DeleteTeam(ctx, teamID)
|
|
|
|
// Create test member
|
|
userID, err := provider.GenerateUserID(ctx, true)
|
|
require.NoError(t, err)
|
|
|
|
memberData := maps.MapStrAny{
|
|
"team_id": teamID,
|
|
"user_id": userID,
|
|
"role_id": testRoleID,
|
|
"member_type": "user",
|
|
"status": "active",
|
|
}
|
|
_, err = provider.CreateMember(ctx, memberData)
|
|
require.NoError(t, err)
|
|
defer provider.RemoveMember(ctx, teamID, userID)
|
|
|
|
// Get member role
|
|
roleID, err := manager.GetMemberRole(ctx, teamID, userID)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, testRoleID, roleID)
|
|
|
|
t.Logf("Successfully retrieved role %s for member %s in team %s", roleID, userID, teamID)
|
|
|
|
// Get again (should come from cache)
|
|
roleID2, err := manager.GetMemberRole(ctx, teamID, userID)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, testRoleID, roleID2)
|
|
|
|
t.Log("Successfully retrieved member role from cache")
|
|
})
|
|
|
|
t.Run("GetRoleForNonExistentMember", func(t *testing.T) {
|
|
_, err := manager.GetMemberRole(ctx, "non-existent-team", "non-existent-user")
|
|
assert.Error(t, err)
|
|
|
|
t.Log("Correctly returns error for non-existent member")
|
|
})
|
|
}
|
|
|
|
func TestGetScopes(t *testing.T) {
|
|
testutils.Prepare(t)
|
|
defer testutils.Clean()
|
|
|
|
// Setup
|
|
cache := oauth.OAuth.GetCache()
|
|
require.NotNil(t, cache)
|
|
|
|
provider, err := oauth.OAuth.GetUserProvider()
|
|
require.NoError(t, err)
|
|
require.NotNil(t, provider)
|
|
|
|
manager := role.NewManager(cache, provider)
|
|
ctx := context.Background()
|
|
|
|
t.Run("GetScopesForRoleWithPermissions", func(t *testing.T) {
|
|
// Create test role with permissions
|
|
testRoleID := "test_role_scopes"
|
|
|
|
// Create role with permissions as map
|
|
permissions := maps.MapStrAny{
|
|
"read": true,
|
|
"write": true,
|
|
"delete": false, // Should not be included
|
|
}
|
|
|
|
restrictedPermissions := []string{"admin", "superuser"}
|
|
|
|
roleData := maps.MapStrAny{
|
|
"role_id": testRoleID,
|
|
"name": "Test Role with Scopes",
|
|
"description": "Role for scopes testing",
|
|
"is_active": true,
|
|
"permissions": permissions,
|
|
"restricted_permissions": restrictedPermissions,
|
|
}
|
|
_, err := provider.CreateRole(ctx, roleData)
|
|
if err != nil {
|
|
t.Skipf("Skipping test - cannot create role: %v", err)
|
|
return
|
|
}
|
|
defer provider.DeleteRole(ctx, testRoleID)
|
|
|
|
// Get scopes
|
|
allowed, restricted, err := manager.GetScopes(ctx, testRoleID)
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, allowed)
|
|
assert.NotNil(t, restricted)
|
|
|
|
// Verify allowed scopes contain enabled permissions
|
|
t.Logf("Allowed scopes: %v", allowed)
|
|
t.Logf("Restricted scopes: %v", restricted)
|
|
|
|
// Note: The exact format depends on how the database stores JSON
|
|
// We just verify we can retrieve them without error
|
|
assert.True(t, len(allowed) >= 0, "Should return allowed scopes (empty or with values)")
|
|
assert.True(t, len(restricted) >= 0, "Should return restricted scopes (empty or with values)")
|
|
|
|
// Get again (should come from cache)
|
|
allowed2, restricted2, err := manager.GetScopes(ctx, testRoleID)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, allowed, allowed2)
|
|
assert.Equal(t, restricted, restricted2)
|
|
|
|
t.Log("Successfully retrieved scopes from cache")
|
|
})
|
|
|
|
t.Run("GetScopesForNonExistentRole", func(t *testing.T) {
|
|
_, _, err := manager.GetScopes(ctx, "non-existent-role-12345")
|
|
assert.Error(t, err)
|
|
|
|
t.Log("Correctly returns error for non-existent role")
|
|
})
|
|
}
|
|
|
|
func TestGetTeamRole(t *testing.T) {
|
|
testutils.Prepare(t)
|
|
defer testutils.Clean()
|
|
|
|
// Setup
|
|
cache := oauth.OAuth.GetCache()
|
|
require.NotNil(t, cache)
|
|
|
|
provider, err := oauth.OAuth.GetUserProvider()
|
|
require.NoError(t, err)
|
|
require.NotNil(t, provider)
|
|
|
|
manager := role.NewManager(cache, provider)
|
|
ctx := context.Background()
|
|
|
|
t.Run("GetRoleForTeamWithoutRole", func(t *testing.T) {
|
|
// Create team without role_id field
|
|
teamID, err := provider.GenerateUserID(ctx, true)
|
|
require.NoError(t, err)
|
|
|
|
teamData := maps.MapStrAny{
|
|
"team_id": teamID,
|
|
"name": "Test Team No Role",
|
|
"owner_id": "test_owner",
|
|
"status": "active",
|
|
}
|
|
_, err = provider.CreateTeam(ctx, teamData)
|
|
require.NoError(t, err)
|
|
defer provider.DeleteTeam(ctx, teamID)
|
|
|
|
// Get team role (should return default)
|
|
roleID, err := manager.GetTeamRole(ctx, teamID)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "team:default", roleID)
|
|
|
|
t.Logf("Successfully returned default role for team without role_id: %s", roleID)
|
|
})
|
|
|
|
t.Run("GetRoleForNonExistentTeam", func(t *testing.T) {
|
|
_, err := manager.GetTeamRole(ctx, "non-existent-team-12345")
|
|
assert.Error(t, err)
|
|
|
|
t.Log("Correctly returns error for non-existent team")
|
|
})
|
|
}
|
|
|
|
func TestGetClientRole(t *testing.T) {
|
|
testutils.Prepare(t)
|
|
defer testutils.Clean()
|
|
|
|
// Setup
|
|
cache := oauth.OAuth.GetCache()
|
|
require.NotNil(t, cache)
|
|
|
|
provider, err := oauth.OAuth.GetUserProvider()
|
|
require.NoError(t, err)
|
|
require.NotNil(t, provider)
|
|
|
|
manager := role.NewManager(cache, provider)
|
|
ctx := context.Background()
|
|
|
|
t.Run("GetClientRoleReturnsDefault", func(t *testing.T) {
|
|
// Note: Client role retrieval is TODO in the code
|
|
// It currently returns a default "system:root" role
|
|
roleID, err := manager.GetClientRole(ctx, "test-client")
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "system:root", roleID)
|
|
|
|
t.Log("Client role returns default system:root (TODO: implement ClientProvider)")
|
|
})
|
|
}
|
|
|
|
func TestCacheIntegration(t *testing.T) {
|
|
testutils.Prepare(t)
|
|
defer testutils.Clean()
|
|
|
|
// Setup
|
|
cache := oauth.OAuth.GetCache()
|
|
require.NotNil(t, cache)
|
|
|
|
provider, err := oauth.OAuth.GetUserProvider()
|
|
require.NoError(t, err)
|
|
require.NotNil(t, provider)
|
|
|
|
manager := role.NewManager(cache, provider)
|
|
ctx := context.Background()
|
|
|
|
t.Run("RoleCachingWorks", func(t *testing.T) {
|
|
// Create test role and user
|
|
testRoleID := "test_role_cache"
|
|
roleData := maps.MapStrAny{
|
|
"role_id": testRoleID,
|
|
"name": "Test Cache Role",
|
|
"description": "Role for cache testing",
|
|
"is_active": true,
|
|
}
|
|
_, err := provider.CreateRole(ctx, roleData)
|
|
if err != nil {
|
|
t.Skipf("Skipping test - cannot create role: %v", err)
|
|
return
|
|
}
|
|
defer provider.DeleteRole(ctx, testRoleID)
|
|
|
|
userID, err := provider.GenerateUserID(ctx, true)
|
|
require.NoError(t, err)
|
|
|
|
userData := maps.MapStrAny{
|
|
"user_id": userID,
|
|
"email": "cache@example.com",
|
|
"role_id": testRoleID,
|
|
"status": "active",
|
|
}
|
|
_, err = provider.CreateUser(ctx, userData)
|
|
require.NoError(t, err)
|
|
defer provider.DeleteUser(ctx, userID)
|
|
|
|
// First call - should hit database
|
|
roleID1, err := manager.GetUserRole(ctx, userID)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, testRoleID, roleID1)
|
|
|
|
// Second call - should hit cache
|
|
roleID2, err := manager.GetUserRole(ctx, userID)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, testRoleID, roleID2)
|
|
|
|
// Results should be identical
|
|
assert.Equal(t, roleID1, roleID2)
|
|
|
|
t.Log("Cache integration verified: same role retrieved from cache")
|
|
})
|
|
}
|