package test import ( "context" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap" "github.com/RyanCopley/skybridge/kms/internal/auth" ) func TestPermissionHierarchy_InitializeDefaultPermissions(t *testing.T) { hierarchy := auth.NewPermissionHierarchy() // Test that default permissions are created permissions := hierarchy.ListPermissions() assert.NotEmpty(t, permissions) // Test specific permissions exist permissionNames := make(map[string]bool) for _, perm := range permissions { permissionNames[perm.Name] = true } expectedPermissions := []string{ "admin", "read", "write", "app.admin", "app.read", "app.write", "app.create", "app.update", "app.delete", "token.admin", "token.read", "token.write", "token.create", "token.revoke", "token.verify", "permission.admin", "permission.read", "permission.write", "permission.grant", "permission.revoke", "user.admin", "user.read", "user.write", } for _, expected := range expectedPermissions { assert.True(t, permissionNames[expected], "Permission %s should exist", expected) } } func TestPermissionHierarchy_InitializeDefaultRoles(t *testing.T) { hierarchy := auth.NewPermissionHierarchy() // Test that default roles are created roles := hierarchy.ListRoles() assert.NotEmpty(t, roles) // Test specific roles exist roleNames := make(map[string]bool) for _, role := range roles { roleNames[role.Name] = true } expectedRoles := []string{ "super_admin", "app_admin", "developer", "viewer", "token_manager", } for _, expected := range expectedRoles { assert.True(t, roleNames[expected], "Role %s should exist", expected) } } func TestPermissionManager_HasPermission(t *testing.T) { configMock := NewTestConfig() configMock.values["CACHE_ENABLED"] = "false" // Disable cache for testing logger := zap.NewNop() pm := auth.NewPermissionManager(configMock, logger) tests := []struct { name string userID string appID string permission string expectedResult bool description string }{ { name: "admin user has admin permission", userID: "admin@example.com", appID: "test-app", permission: "admin", expectedResult: true, description: "Admin users should have admin permissions", }, { name: "developer user has token.create permission", userID: "dev@example.com", appID: "test-app", permission: "token.create", expectedResult: true, description: "Developer users should have token creation permissions", }, { name: "viewer user has read permission", userID: "viewer@example.com", appID: "test-app", permission: "app.read", expectedResult: true, description: "Viewer users should have read permissions", }, { name: "viewer user denied write permission", userID: "viewer@example.com", appID: "test-app", permission: "app.write", expectedResult: false, description: "Viewer users should not have write permissions", }, { name: "non-existent permission", userID: "admin@example.com", appID: "test-app", permission: "non.existent", expectedResult: false, description: "Non-existent permissions should be denied", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { ctx := context.Background() evaluation, err := pm.HasPermission(ctx, tt.userID, tt.appID, tt.permission) require.NoError(t, err) assert.NotNil(t, evaluation) assert.Equal(t, tt.expectedResult, evaluation.Granted, tt.description) assert.Equal(t, tt.permission, evaluation.Permission) assert.NotZero(t, evaluation.EvaluatedAt) if evaluation.Granted { assert.NotEmpty(t, evaluation.GrantedBy, "Granted permissions should have GrantedBy information") } else { assert.NotEmpty(t, evaluation.DeniedReason, "Denied permissions should have a reason") } }) } } func TestPermissionManager_EvaluateBulkPermissions(t *testing.T) { configMock := NewTestConfig() configMock.values["CACHE_ENABLED"] = "false" logger := zap.NewNop() pm := auth.NewPermissionManager(configMock, logger) ctx := context.Background() req := &auth.BulkPermissionRequest{ UserID: "dev@example.com", AppID: "test-app", Permissions: []string{ "app.read", "token.create", "token.read", "app.delete", // Should be denied for developer "admin", // Should be denied for developer }, } response, err := pm.EvaluateBulkPermissions(ctx, req) require.NoError(t, err) assert.NotNil(t, response) assert.Equal(t, req.UserID, response.UserID) assert.Equal(t, req.AppID, response.AppID) assert.Len(t, response.Results, len(req.Permissions)) // Check specific results assert.True(t, response.Results["app.read"].Granted, "Developer should have app.read permission") assert.True(t, response.Results["token.create"].Granted, "Developer should have token.create permission") assert.True(t, response.Results["token.read"].Granted, "Developer should have token.read permission") assert.False(t, response.Results["app.delete"].Granted, "Developer should not have app.delete permission") assert.False(t, response.Results["admin"].Granted, "Developer should not have admin permission") } func TestPermissionManager_AddPermission(t *testing.T) { configMock := NewTestConfig() logger := zap.NewNop() pm := auth.NewPermissionManager(configMock, logger) tests := []struct { name string permission *auth.Permission expectError bool description string }{ { name: "add valid permission", permission: &auth.Permission{ Name: "custom.permission", Description: "Custom permission for testing", Parent: "read", Level: 2, Resource: "custom", Action: "test", }, expectError: false, description: "Valid permissions should be added successfully", }, { name: "add permission without name", permission: &auth.Permission{ Description: "Permission without name", Parent: "read", Level: 2, }, expectError: true, description: "Permissions without names should be rejected", }, { name: "add permission with non-existent parent", permission: &auth.Permission{ Name: "invalid.permission", Description: "Permission with invalid parent", Parent: "non.existent", Level: 2, }, expectError: true, description: "Permissions with non-existent parents should be rejected", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := pm.AddPermission(tt.permission) if tt.expectError { assert.Error(t, err, tt.description) } else { assert.NoError(t, err, tt.description) // Verify permission was added permissions := pm.ListPermissions() found := false for _, perm := range permissions { if perm.Name == tt.permission.Name { found = true assert.Equal(t, tt.permission.Description, perm.Description) assert.Equal(t, tt.permission.Parent, perm.Parent) break } } assert.True(t, found, "Added permission should be found in the list") } }) } } func TestPermissionManager_AddRole(t *testing.T) { configMock := NewTestConfig() logger := zap.NewNop() pm := auth.NewPermissionManager(configMock, logger) tests := []struct { name string role *auth.Role expectError bool description string }{ { name: "add valid role", role: &auth.Role{ Name: "custom_role", Description: "Custom role for testing", Permissions: []string{"read", "app.read"}, Metadata: map[string]string{"level": "custom"}, }, expectError: false, description: "Valid roles should be added successfully", }, { name: "add role without name", role: &auth.Role{ Description: "Role without name", Permissions: []string{"read"}, }, expectError: true, description: "Roles without names should be rejected", }, { name: "add role with non-existent permission", role: &auth.Role{ Name: "invalid_role", Description: "Role with invalid permission", Permissions: []string{"non.existent.permission"}, }, expectError: true, description: "Roles with non-existent permissions should be rejected", }, { name: "add role with non-existent inherited role", role: &auth.Role{ Name: "invalid_inherited_role", Description: "Role with invalid inheritance", Permissions: []string{"read"}, Inherits: []string{"non_existent_role"}, }, expectError: true, description: "Roles with non-existent inherited roles should be rejected", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := pm.AddRole(tt.role) if tt.expectError { assert.Error(t, err, tt.description) } else { assert.NoError(t, err, tt.description) // Verify role was added roles := pm.ListRoles() found := false for _, role := range roles { if role.Name == tt.role.Name { found = true assert.Equal(t, tt.role.Description, role.Description) assert.Equal(t, tt.role.Permissions, role.Permissions) break } } assert.True(t, found, "Added role should be found in the list") } }) } } func TestPermissionManager_ListPermissions(t *testing.T) { configMock := NewTestConfig() logger := zap.NewNop() pm := auth.NewPermissionManager(configMock, logger) permissions := pm.ListPermissions() // Should have default permissions assert.NotEmpty(t, permissions) // Should be sorted by level and name for i := 1; i < len(permissions); i++ { prev := permissions[i-1] curr := permissions[i] if prev.Level == curr.Level { assert.True(t, prev.Name <= curr.Name, "Permissions at same level should be sorted by name") } else { assert.True(t, prev.Level <= curr.Level, "Permissions should be sorted by level") } } // Verify hierarchy structure for _, perm := range permissions { if perm.Parent != "" { // Find parent permission parentFound := false for _, parent := range permissions { if parent.Name == perm.Parent { parentFound = true assert.True(t, parent.Level < perm.Level, "Parent should have lower level than child") assert.Contains(t, parent.Children, perm.Name, "Parent should contain child in children list") break } } assert.True(t, parentFound, "Parent permission should exist for %s", perm.Name) } } } func TestPermissionManager_ListRoles(t *testing.T) { configMock := NewTestConfig() logger := zap.NewNop() pm := auth.NewPermissionManager(configMock, logger) roles := pm.ListRoles() // Should have default roles assert.NotEmpty(t, roles) // Should be sorted by name for i := 1; i < len(roles); i++ { assert.True(t, roles[i-1].Name <= roles[i].Name, "Roles should be sorted by name") } // Verify all permissions in roles exist allPermissions := pm.ListPermissions() permissionNames := make(map[string]bool) for _, perm := range allPermissions { permissionNames[perm.Name] = true } for _, role := range roles { for _, perm := range role.Permissions { assert.True(t, permissionNames[perm], "Role %s references non-existent permission %s", role.Name, perm) } } } func TestPermissionManager_InvalidatePermissionCache(t *testing.T) { configMock := NewTestConfig() logger := zap.NewNop() pm := auth.NewPermissionManager(configMock, logger) ctx := context.Background() err := pm.InvalidatePermissionCache(ctx, "user123", "app123") // Should not error (currently just logs) assert.NoError(t, err) } func TestPermissionHierarchy_BuildHierarchy(t *testing.T) { hierarchy := auth.NewPermissionHierarchy() // Test that parent-child relationships are built correctly permissions := hierarchy.ListPermissions() // Find admin permission var adminPerm *auth.Permission for _, perm := range permissions { if perm.Name == "admin" { adminPerm = perm break } } require.NotNil(t, adminPerm, "Admin permission should exist") // Admin should have children assert.NotEmpty(t, adminPerm.Children, "Admin permission should have children") // Check that app.admin is a child of admin assert.Contains(t, adminPerm.Children, "app.admin", "app.admin should be a child of admin") // Find app.write permission var appWritePerm *auth.Permission for _, perm := range permissions { if perm.Name == "app.write" { appWritePerm = perm break } } require.NotNil(t, appWritePerm, "app.write permission should exist") // app.write should have children assert.NotEmpty(t, appWritePerm.Children, "app.write permission should have children") assert.Contains(t, appWritePerm.Children, "app.create", "app.create should be a child of app.write") assert.Contains(t, appWritePerm.Children, "app.update", "app.update should be a child of app.write") assert.Contains(t, appWritePerm.Children, "app.delete", "app.delete should be a child of app.write") } // Benchmark tests for permission operations func BenchmarkPermissionManager_HasPermission(b *testing.B) { configMock := NewTestConfig() configMock.values["CACHE_ENABLED"] = "false" logger := zap.NewNop() pm := auth.NewPermissionManager(configMock, logger) ctx := context.Background() b.ResetTimer() for i := 0; i < b.N; i++ { _, err := pm.HasPermission(ctx, "dev@example.com", "test-app", "token.create") if err != nil { b.Fatal(err) } } } func BenchmarkPermissionManager_EvaluateBulkPermissions(b *testing.B) { configMock := NewTestConfig() configMock.values["CACHE_ENABLED"] = "false" logger := zap.NewNop() pm := auth.NewPermissionManager(configMock, logger) ctx := context.Background() req := &auth.BulkPermissionRequest{ UserID: "dev@example.com", AppID: "test-app", Permissions: []string{ "app.read", "token.create", "token.read", "app.delete", "admin", }, } b.ResetTimer() for i := 0; i < b.N; i++ { _, err := pm.EvaluateBulkPermissions(ctx, req) if err != nil { b.Fatal(err) } } } func BenchmarkPermissionManager_ListPermissions(b *testing.B) { configMock := NewTestConfig() logger := zap.NewNop() pm := auth.NewPermissionManager(configMock, logger) b.ResetTimer() for i := 0; i < b.N; i++ { permissions := pm.ListPermissions() if len(permissions) == 0 { b.Fatal("No permissions returned") } } } func BenchmarkPermissionManager_ListRoles(b *testing.B) { configMock := NewTestConfig() logger := zap.NewNop() pm := auth.NewPermissionManager(configMock, logger) b.ResetTimer() for i := 0; i < b.N; i++ { roles := pm.ListRoles() if len(roles) == 0 { b.Fatal("No roles returned") } } } // Test permission hierarchy traversal func TestPermissionHierarchy_PermissionInheritance(t *testing.T) { configMock := NewTestConfig() configMock.values["CACHE_ENABLED"] = "false" logger := zap.NewNop() pm := auth.NewPermissionManager(configMock, logger) // Test that admin users get hierarchical permissions ctx := context.Background() // Admin should have all permissions through hierarchy adminPermissions := []string{ "admin", "app.admin", "token.admin", "permission.admin", "user.admin", } for _, perm := range adminPermissions { evaluation, err := pm.HasPermission(ctx, "admin@example.com", "test-app", perm) require.NoError(t, err) assert.True(t, evaluation.Granted, "Admin should have %s permission", perm) } } // Test role inheritance func TestPermissionManager_RoleInheritance(t *testing.T) { configMock := NewTestConfig() logger := zap.NewNop() pm := auth.NewPermissionManager(configMock, logger) // Add a role that inherits from another role parentRole := &auth.Role{ Name: "base_role", Description: "Base role with basic permissions", Permissions: []string{"read", "app.read"}, Metadata: map[string]string{"level": "base"}, } childRole := &auth.Role{ Name: "extended_role", Description: "Extended role that inherits from base", Permissions: []string{"write"}, Inherits: []string{"base_role"}, Metadata: map[string]string{"level": "extended"}, } err := pm.AddRole(parentRole) require.NoError(t, err) err = pm.AddRole(childRole) require.NoError(t, err) // Verify roles were added roles := pm.ListRoles() roleNames := make(map[string]*auth.Role) for _, role := range roles { roleNames[role.Name] = role } assert.Contains(t, roleNames, "base_role") assert.Contains(t, roleNames, "extended_role") assert.Equal(t, []string{"base_role"}, roleNames["extended_role"].Inherits) }