Files
sendico/api/pkg/auth/internal/native/enforcer_test.go
Stephan D 62a6631b9a
All checks were successful
ci/woodpecker/push/db Pipeline was successful
ci/woodpecker/push/nats Pipeline was successful
service backend
2025-11-07 18:35:26 +01:00

748 lines
25 KiB
Go

package native
import (
"context"
"errors"
"testing"
"github.com/tech/sendico/pkg/auth/internal/native/nstructures"
"github.com/tech/sendico/pkg/db/repository/builder"
"github.com/tech/sendico/pkg/merrors"
factory "github.com/tech/sendico/pkg/mlogger/factory"
"github.com/tech/sendico/pkg/model"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
"go.mongodb.org/mongo-driver/bson/primitive"
)
// Mock implementations for testing
type MockPoliciesDB struct {
mock.Mock
}
func (m *MockPoliciesDB) PoliciesForPermissionAction(ctx context.Context, roleRef, permissionRef primitive.ObjectID, action model.Action) ([]nstructures.PolicyAssignment, error) {
args := m.Called(ctx, roleRef, permissionRef, action)
return args.Get(0).([]nstructures.PolicyAssignment), args.Error(1)
}
func (m *MockPoliciesDB) PoliciesForRole(ctx context.Context, roleRef primitive.ObjectID) ([]nstructures.PolicyAssignment, error) {
args := m.Called(ctx, roleRef)
return args.Get(0).([]nstructures.PolicyAssignment), args.Error(1)
}
func (m *MockPoliciesDB) PoliciesForRoles(ctx context.Context, roleRefs []primitive.ObjectID, action model.Action) ([]nstructures.PolicyAssignment, error) {
args := m.Called(ctx, roleRefs, action)
return args.Get(0).([]nstructures.PolicyAssignment), args.Error(1)
}
func (m *MockPoliciesDB) Policies(ctx context.Context, object model.PermissionBoundStorable, action model.Action) ([]nstructures.PolicyAssignment, error) {
args := m.Called(ctx, object, action)
return args.Get(0).([]nstructures.PolicyAssignment), args.Error(1)
}
func (m *MockPoliciesDB) Remove(ctx context.Context, policy *model.RolePolicy) error {
args := m.Called(ctx, policy)
return args.Error(0)
}
// Template DB methods - implement as needed for testing
func (m *MockPoliciesDB) Create(ctx context.Context, assignment *nstructures.PolicyAssignment) error {
args := m.Called(ctx, assignment)
return args.Error(0)
}
func (m *MockPoliciesDB) Get(ctx context.Context, id primitive.ObjectID, assignment *nstructures.PolicyAssignment) error {
args := m.Called(ctx, id, assignment)
return args.Error(0)
}
func (m *MockPoliciesDB) Update(ctx context.Context, assignment *nstructures.PolicyAssignment) error {
args := m.Called(ctx, assignment)
return args.Error(0)
}
func (m *MockPoliciesDB) Patch(ctx context.Context, objectRef primitive.ObjectID, patch builder.Patch) error {
args := m.Called(ctx, objectRef, patch)
return args.Error(0)
}
func (m *MockPoliciesDB) Delete(ctx context.Context, id primitive.ObjectID) error {
args := m.Called(ctx, id)
return args.Error(0)
}
func (m *MockPoliciesDB) DeleteMany(ctx context.Context, query builder.Query) error {
args := m.Called(ctx, query)
return args.Error(0)
}
func (m *MockPoliciesDB) ListPermissionBound(ctx context.Context, accountRef, organizationRef primitive.ObjectID) ([]nstructures.PolicyAssignment, error) {
args := m.Called(ctx, accountRef, organizationRef)
return args.Get(0).([]nstructures.PolicyAssignment), args.Error(1)
}
func (m *MockPoliciesDB) ListIDs(ctx context.Context, query interface{}) ([]primitive.ObjectID, error) {
args := m.Called(ctx, query)
return args.Get(0).([]primitive.ObjectID), args.Error(1)
}
func (m *MockPoliciesDB) FindOne(ctx context.Context, query builder.Query, assignment *nstructures.PolicyAssignment) error {
args := m.Called(ctx, query, assignment)
return args.Error(0)
}
func (m *MockPoliciesDB) List(ctx context.Context, query builder.Query) ([]nstructures.PolicyAssignment, error) {
args := m.Called(ctx, query)
return args.Get(0).([]nstructures.PolicyAssignment), args.Error(1)
}
func (m *MockPoliciesDB) Name() string {
return "mock_policies"
}
func (m *MockPoliciesDB) DeleteCascade(ctx context.Context, id primitive.ObjectID) error {
args := m.Called(ctx, id)
return args.Error(0)
}
func (m *MockPoliciesDB) InsertMany(ctx context.Context, objects []*nstructures.PolicyAssignment) error {
args := m.Called(ctx, objects)
return args.Error(0)
}
type MockRolesDB struct {
mock.Mock
}
func (m *MockRolesDB) Roles(ctx context.Context, accountRef, organizationRef primitive.ObjectID) ([]nstructures.RoleAssignment, error) {
args := m.Called(ctx, accountRef, organizationRef)
return args.Get(0).([]nstructures.RoleAssignment), args.Error(1)
}
func (m *MockRolesDB) RolesForVenue(ctx context.Context, organizationRef primitive.ObjectID) ([]nstructures.RoleAssignment, error) {
args := m.Called(ctx, organizationRef)
return args.Get(0).([]nstructures.RoleAssignment), args.Error(1)
}
func (m *MockRolesDB) RemoveRole(ctx context.Context, roleRef, organizationRef, accountRef primitive.ObjectID) error {
args := m.Called(ctx, roleRef, organizationRef, accountRef)
return args.Error(0)
}
func (m *MockRolesDB) DeleteRole(ctx context.Context, roleRef primitive.ObjectID) error {
args := m.Called(ctx, roleRef)
return args.Error(0)
}
// Template DB methods - implement as needed for testing
func (m *MockRolesDB) Create(ctx context.Context, assignment *nstructures.RoleAssignment) error {
args := m.Called(ctx, assignment)
return args.Error(0)
}
func (m *MockRolesDB) Get(ctx context.Context, id primitive.ObjectID, assignment *nstructures.RoleAssignment) error {
args := m.Called(ctx, id, assignment)
return args.Error(0)
}
func (m *MockRolesDB) Update(ctx context.Context, assignment *nstructures.RoleAssignment) error {
args := m.Called(ctx, assignment)
return args.Error(0)
}
func (m *MockRolesDB) Patch(ctx context.Context, objectRef primitive.ObjectID, patch builder.Patch) error {
args := m.Called(ctx, objectRef, patch)
return args.Error(0)
}
func (m *MockRolesDB) Delete(ctx context.Context, id primitive.ObjectID) error {
args := m.Called(ctx, id)
return args.Error(0)
}
func (m *MockRolesDB) DeleteMany(ctx context.Context, query builder.Query) error {
args := m.Called(ctx, query)
return args.Error(0)
}
func (m *MockRolesDB) ListPermissionBound(ctx context.Context, accountRef, organizationRef primitive.ObjectID) ([]nstructures.RoleAssignment, error) {
args := m.Called(ctx, accountRef, organizationRef)
return args.Get(0).([]nstructures.RoleAssignment), args.Error(1)
}
func (m *MockRolesDB) ListIDs(ctx context.Context, query interface{}) ([]primitive.ObjectID, error) {
args := m.Called(ctx, query)
return args.Get(0).([]primitive.ObjectID), args.Error(1)
}
func (m *MockRolesDB) FindOne(ctx context.Context, query builder.Query, assignment *nstructures.RoleAssignment) error {
args := m.Called(ctx, query, assignment)
return args.Error(0)
}
func (m *MockRolesDB) List(ctx context.Context, query builder.Query) ([]nstructures.RoleAssignment, error) {
args := m.Called(ctx, query)
return args.Get(0).([]nstructures.RoleAssignment), args.Error(1)
}
func (m *MockRolesDB) Name() string {
return "mock_roles"
}
func (m *MockRolesDB) DeleteCascade(ctx context.Context, id primitive.ObjectID) error {
args := m.Called(ctx, id)
return args.Error(0)
}
func (m *MockRolesDB) InsertMany(ctx context.Context, objects []*nstructures.RoleAssignment) error {
args := m.Called(ctx, objects)
return args.Error(0)
}
// Test helper functions
func createTestObjectID() primitive.ObjectID {
return primitive.NewObjectID()
}
func createTestRoleAssignment(roleRef, accountRef, organizationRef primitive.ObjectID) nstructures.RoleAssignment {
return nstructures.RoleAssignment{
Role: model.Role{
AccountRef: accountRef,
DescriptionRef: roleRef,
OrganizationRef: organizationRef,
},
}
}
func createTestPolicyAssignment(roleRef primitive.ObjectID, action model.Action, effect model.Effect, organizationRef, descriptionRef primitive.ObjectID, objectRef *primitive.ObjectID) nstructures.PolicyAssignment {
return nstructures.PolicyAssignment{
Policy: model.Policy{
OrganizationRef: organizationRef,
DescriptionRef: descriptionRef,
ObjectRef: objectRef,
Effect: model.ActionEffect{
Action: action,
Effect: effect,
},
},
RoleRef: roleRef,
}
}
func createTestEnforcer(pdb PoliciesDB, rdb RolesDB) *Enforcer {
logger := factory.NewLogger(true)
enforcer := &Enforcer{
logger: logger.Named("test"),
pdb: pdb,
rdb: rdb,
}
return enforcer
}
func TestEnforcer_Enforce(t *testing.T) {
ctx := context.Background()
// Test data
accountRef := createTestObjectID()
organizationRef := createTestObjectID()
permissionRef := createTestObjectID()
objectRef := createTestObjectID()
roleRef := createTestObjectID()
t.Run("Allow_SingleRole_SinglePolicy", func(t *testing.T) {
mockPDB := &MockPoliciesDB{}
mockRDB := &MockRolesDB{}
// Mock role assignment
roleAssignment := createTestRoleAssignment(roleRef, accountRef, organizationRef)
mockRDB.On("Roles", ctx, accountRef, organizationRef).Return([]nstructures.RoleAssignment{roleAssignment}, nil)
// Mock policy assignment with ALLOW effect
policyAssignment := createTestPolicyAssignment(roleRef, model.ActionRead, model.EffectAllow, organizationRef, permissionRef, &objectRef)
mockPDB.On("PoliciesForPermissionAction", ctx, roleRef, permissionRef, model.ActionRead).Return([]nstructures.PolicyAssignment{policyAssignment}, nil)
// Create enforcer
enforcer := createTestEnforcer(mockPDB, mockRDB)
// Execute
allowed, err := enforcer.Enforce(ctx, permissionRef, accountRef, organizationRef, objectRef, model.ActionRead)
// Verify
require.NoError(t, err)
assert.True(t, allowed)
mockRDB.AssertExpectations(t)
mockPDB.AssertExpectations(t)
})
t.Run("Deny_SingleRole_SinglePolicy", func(t *testing.T) {
mockPDB := &MockPoliciesDB{}
mockRDB := &MockRolesDB{}
// Mock role assignment
roleAssignment := createTestRoleAssignment(roleRef, accountRef, organizationRef)
mockRDB.On("Roles", ctx, accountRef, organizationRef).Return([]nstructures.RoleAssignment{roleAssignment}, nil)
// Mock policy assignment with DENY effect
policyAssignment := createTestPolicyAssignment(roleRef, model.ActionRead, model.EffectDeny, organizationRef, permissionRef, &objectRef)
mockPDB.On("PoliciesForPermissionAction", ctx, roleRef, permissionRef, model.ActionRead).Return([]nstructures.PolicyAssignment{policyAssignment}, nil)
enforcer := createTestEnforcer(mockPDB, mockRDB)
// Execute
allowed, err := enforcer.Enforce(ctx, permissionRef, accountRef, organizationRef, objectRef, model.ActionRead)
// Verify
require.NoError(t, err)
assert.False(t, allowed)
mockRDB.AssertExpectations(t)
mockPDB.AssertExpectations(t)
})
t.Run("DenyTakesPrecedence_MultipleRoles", func(t *testing.T) {
mockPDB := &MockPoliciesDB{}
mockRDB := &MockRolesDB{}
role1Ref := createTestObjectID()
role2Ref := createTestObjectID()
// Mock multiple role assignments
roleAssignment1 := createTestRoleAssignment(role1Ref, accountRef, organizationRef)
roleAssignment2 := createTestRoleAssignment(role2Ref, accountRef, organizationRef)
mockRDB.On("Roles", ctx, accountRef, organizationRef).Return([]nstructures.RoleAssignment{roleAssignment1, roleAssignment2}, nil)
// First role has ALLOW policy
allowPolicy := createTestPolicyAssignment(role1Ref, model.ActionRead, model.EffectAllow, organizationRef, permissionRef, &objectRef)
mockPDB.On("PoliciesForPermissionAction", ctx, role1Ref, permissionRef, model.ActionRead).Return([]nstructures.PolicyAssignment{allowPolicy}, nil)
// Second role has DENY policy - should take precedence
denyPolicy := createTestPolicyAssignment(role2Ref, model.ActionRead, model.EffectDeny, organizationRef, permissionRef, &objectRef)
mockPDB.On("PoliciesForPermissionAction", ctx, role2Ref, permissionRef, model.ActionRead).Return([]nstructures.PolicyAssignment{denyPolicy}, nil)
enforcer := createTestEnforcer(mockPDB, mockRDB)
// Execute
allowed, err := enforcer.Enforce(ctx, permissionRef, accountRef, organizationRef, objectRef, model.ActionRead)
// Verify - DENY should take precedence
require.NoError(t, err)
assert.False(t, allowed)
mockRDB.AssertExpectations(t)
mockPDB.AssertExpectations(t)
})
t.Run("NoRoles_ReturnsFalse", func(t *testing.T) {
mockPDB := &MockPoliciesDB{}
mockRDB := &MockRolesDB{}
// Mock no roles found
mockRDB.On("Roles", ctx, accountRef, organizationRef).Return([]nstructures.RoleAssignment{}, merrors.ErrNoData)
enforcer := createTestEnforcer(mockPDB, mockRDB)
// Execute
allowed, err := enforcer.Enforce(ctx, permissionRef, accountRef, organizationRef, objectRef, model.ActionRead)
// Verify
require.NoError(t, err)
assert.False(t, allowed)
mockRDB.AssertExpectations(t)
})
t.Run("EmptyRoles_ReturnsError", func(t *testing.T) {
mockPDB := &MockPoliciesDB{}
mockRDB := &MockRolesDB{}
// Mock empty roles list (not NoData error)
mockRDB.On("Roles", ctx, accountRef, organizationRef).Return([]nstructures.RoleAssignment{}, nil)
enforcer := createTestEnforcer(mockPDB, mockRDB)
// Execute
allowed, err := enforcer.Enforce(ctx, permissionRef, accountRef, organizationRef, objectRef, model.ActionRead)
// Verify
require.Error(t, err)
assert.False(t, allowed)
assert.Contains(t, err.Error(), "No roles found for account")
mockRDB.AssertExpectations(t)
})
t.Run("DatabaseError_RolesDB", func(t *testing.T) {
mockPDB := &MockPoliciesDB{}
mockRDB := &MockRolesDB{}
// Mock database error
dbError := errors.New("database connection failed")
mockRDB.On("Roles", ctx, accountRef, organizationRef).Return([]nstructures.RoleAssignment{}, dbError)
enforcer := createTestEnforcer(mockPDB, mockRDB)
// Execute
allowed, err := enforcer.Enforce(ctx, permissionRef, accountRef, organizationRef, objectRef, model.ActionRead)
// Verify
require.Error(t, err)
assert.False(t, allowed)
assert.Equal(t, dbError, err)
mockRDB.AssertExpectations(t)
})
t.Run("DatabaseError_PoliciesDB", func(t *testing.T) {
mockPDB := &MockPoliciesDB{}
mockRDB := &MockRolesDB{}
// Mock role assignment
roleAssignment := createTestRoleAssignment(roleRef, accountRef, organizationRef)
mockRDB.On("Roles", ctx, accountRef, organizationRef).Return([]nstructures.RoleAssignment{roleAssignment}, nil)
// Mock database error in policies
dbError := errors.New("policies database error")
mockPDB.On("PoliciesForPermissionAction", ctx, roleRef, permissionRef, model.ActionRead).Return([]nstructures.PolicyAssignment{}, dbError)
enforcer := createTestEnforcer(mockPDB, mockRDB)
// Execute
allowed, err := enforcer.Enforce(ctx, permissionRef, accountRef, organizationRef, objectRef, model.ActionRead)
// Verify
require.Error(t, err)
assert.False(t, allowed)
assert.Equal(t, dbError, err)
mockRDB.AssertExpectations(t)
mockPDB.AssertExpectations(t)
})
t.Run("NoPolicies_ReturnsFalse", func(t *testing.T) {
mockPDB := &MockPoliciesDB{}
mockRDB := &MockRolesDB{}
// Mock role assignment
roleAssignment := createTestRoleAssignment(roleRef, accountRef, organizationRef)
mockRDB.On("Roles", ctx, accountRef, organizationRef).Return([]nstructures.RoleAssignment{roleAssignment}, nil)
// Mock no policies found
mockPDB.On("PoliciesForPermissionAction", ctx, roleRef, permissionRef, model.ActionRead).Return([]nstructures.PolicyAssignment{}, merrors.ErrNoData)
enforcer := createTestEnforcer(mockPDB, mockRDB)
// Execute
allowed, err := enforcer.Enforce(ctx, permissionRef, accountRef, organizationRef, objectRef, model.ActionRead)
// Verify
require.NoError(t, err)
assert.False(t, allowed)
mockRDB.AssertExpectations(t)
mockPDB.AssertExpectations(t)
})
t.Run("CorruptedPolicy_ReturnsError", func(t *testing.T) {
mockPDB := &MockPoliciesDB{}
mockRDB := &MockRolesDB{}
// Mock role assignment
roleAssignment := createTestRoleAssignment(roleRef, accountRef, organizationRef)
mockRDB.On("Roles", ctx, accountRef, organizationRef).Return([]nstructures.RoleAssignment{roleAssignment}, nil)
// Mock corrupted policy with invalid effect
corruptedPolicy := createTestPolicyAssignment(roleRef, model.ActionRead, "invalid_effect", organizationRef, permissionRef, &objectRef)
mockPDB.On("PoliciesForPermissionAction", ctx, roleRef, permissionRef, model.ActionRead).Return([]nstructures.PolicyAssignment{corruptedPolicy}, nil)
enforcer := createTestEnforcer(mockPDB, mockRDB)
// Execute
allowed, err := enforcer.Enforce(ctx, permissionRef, accountRef, organizationRef, objectRef, model.ActionRead)
// Verify
require.Error(t, err)
assert.False(t, allowed)
assert.Contains(t, err.Error(), "Corrupted action effect data")
mockRDB.AssertExpectations(t)
mockPDB.AssertExpectations(t)
})
}
// Mock implementation for PermissionBoundStorable
type MockPermissionBoundStorable struct {
id primitive.ObjectID
permissionRef primitive.ObjectID
organizationRef primitive.ObjectID
}
func (m *MockPermissionBoundStorable) GetID() *primitive.ObjectID {
return &m.id
}
func (m *MockPermissionBoundStorable) GetPermissionRef() primitive.ObjectID {
return m.permissionRef
}
func (m *MockPermissionBoundStorable) GetOrganizationRef() primitive.ObjectID {
return m.organizationRef
}
func (m *MockPermissionBoundStorable) Collection() string {
return "test_objects"
}
func (m *MockPermissionBoundStorable) SetID(objID primitive.ObjectID) {
m.id = objID
}
func (m *MockPermissionBoundStorable) Update() {
// Do nothing for mock
}
func (m *MockPermissionBoundStorable) SetPermissionRef(permissionRef primitive.ObjectID) {
m.permissionRef = permissionRef
}
func (m *MockPermissionBoundStorable) SetOrganizationRef(organizationRef primitive.ObjectID) {
m.organizationRef = organizationRef
}
func (m *MockPermissionBoundStorable) IsArchived() bool {
return false // Default to not archived for testing
}
func (m *MockPermissionBoundStorable) SetArchived(archived bool) {
// No-op for testing
}
func TestEnforcer_EnforceBatch(t *testing.T) {
ctx := context.Background()
// Test data
accountRef := createTestObjectID()
organizationRef := createTestObjectID()
permissionRef := createTestObjectID()
roleRef := createTestObjectID()
// Create test objects
object1 := &MockPermissionBoundStorable{
id: createTestObjectID(),
permissionRef: permissionRef,
organizationRef: organizationRef,
}
object2 := &MockPermissionBoundStorable{
id: createTestObjectID(),
permissionRef: permissionRef,
organizationRef: organizationRef,
}
t.Run("BatchEnforce_MultipleObjects_SameVenue", func(t *testing.T) {
mockPDB := &MockPoliciesDB{}
mockRDB := &MockRolesDB{}
// Mock role assignment
roleAssignment := createTestRoleAssignment(roleRef, accountRef, organizationRef)
mockRDB.On("Roles", ctx, accountRef, organizationRef).Return([]nstructures.RoleAssignment{roleAssignment}, nil)
// Mock policy assignment with ALLOW effect
policyAssignment := createTestPolicyAssignment(roleRef, model.ActionRead, model.EffectAllow, organizationRef, permissionRef, nil)
mockPDB.On("PoliciesForRoles", ctx, []primitive.ObjectID{roleRef}, model.ActionRead).Return([]nstructures.PolicyAssignment{policyAssignment}, nil)
enforcer := createTestEnforcer(mockPDB, mockRDB)
// Execute batch enforcement
objects := []model.PermissionBoundStorable{object1, object2}
results, err := enforcer.EnforceBatch(ctx, objects, accountRef, model.ActionRead)
// Verify
require.NoError(t, err)
assert.Len(t, results, 2)
assert.True(t, results[object1.id])
assert.True(t, results[object2.id])
mockRDB.AssertExpectations(t)
mockPDB.AssertExpectations(t)
})
t.Run("BatchEnforce_NoRoles_AllObjectsDenied", func(t *testing.T) {
mockPDB := &MockPoliciesDB{}
mockRDB := &MockRolesDB{}
// Mock no roles found
mockRDB.On("Roles", ctx, accountRef, organizationRef).Return([]nstructures.RoleAssignment{}, merrors.ErrNoData)
enforcer := createTestEnforcer(mockPDB, mockRDB)
// Execute batch enforcement
objects := []model.PermissionBoundStorable{object1, object2}
results, err := enforcer.EnforceBatch(ctx, objects, accountRef, model.ActionRead)
// Verify
require.NoError(t, err)
assert.Len(t, results, 2)
assert.False(t, results[object1.id])
assert.False(t, results[object2.id])
mockRDB.AssertExpectations(t)
})
t.Run("BatchEnforce_DatabaseError", func(t *testing.T) {
mockPDB := &MockPoliciesDB{}
mockRDB := &MockRolesDB{}
// Mock database error
dbError := errors.New("database connection failed")
mockRDB.On("Roles", ctx, accountRef, organizationRef).Return([]nstructures.RoleAssignment{}, dbError)
enforcer := createTestEnforcer(mockPDB, mockRDB)
// Execute batch enforcement
objects := []model.PermissionBoundStorable{object1, object2}
results, err := enforcer.EnforceBatch(ctx, objects, accountRef, model.ActionRead)
// Verify
require.Error(t, err)
assert.Nil(t, results)
assert.Equal(t, dbError, err)
mockRDB.AssertExpectations(t)
})
}
func TestEnforcer_GetRoles(t *testing.T) {
ctx := context.Background()
// Test data
accountRef := createTestObjectID()
organizationRef := createTestObjectID()
roleRef := createTestObjectID()
t.Run("GetRoles_Success", func(t *testing.T) {
mockPDB := &MockPoliciesDB{}
mockRDB := &MockRolesDB{}
// Mock role assignment
roleAssignment := createTestRoleAssignment(roleRef, accountRef, organizationRef)
mockRDB.On("Roles", ctx, accountRef, organizationRef).Return([]nstructures.RoleAssignment{roleAssignment}, nil)
enforcer := createTestEnforcer(mockPDB, mockRDB)
// Execute
roles, err := enforcer.GetRoles(ctx, accountRef, organizationRef)
// Verify
require.NoError(t, err)
assert.Len(t, roles, 1)
assert.Equal(t, roleRef, roles[0].DescriptionRef)
mockRDB.AssertExpectations(t)
})
t.Run("GetRoles_NoRoles", func(t *testing.T) {
mockPDB := &MockPoliciesDB{}
mockRDB := &MockRolesDB{}
// Mock no roles found
mockRDB.On("Roles", ctx, accountRef, organizationRef).Return([]nstructures.RoleAssignment{}, merrors.ErrNoData)
enforcer := createTestEnforcer(mockPDB, mockRDB)
// Execute
roles, err := enforcer.GetRoles(ctx, accountRef, organizationRef)
// Verify
require.NoError(t, err)
assert.Len(t, roles, 0)
mockRDB.AssertExpectations(t)
})
}
func TestEnforcer_GetPermissions(t *testing.T) {
ctx := context.Background()
// Test data
accountRef := createTestObjectID()
organizationRef := createTestObjectID()
roleRef := createTestObjectID()
t.Run("GetPermissions_Success", func(t *testing.T) {
mockPDB := &MockPoliciesDB{}
mockRDB := &MockRolesDB{}
// Mock role assignment
roleAssignment := createTestRoleAssignment(roleRef, accountRef, organizationRef)
mockRDB.On("Roles", ctx, accountRef, organizationRef).Return([]nstructures.RoleAssignment{roleAssignment}, nil)
// Mock policy assignment
policyAssignment := createTestPolicyAssignment(roleRef, model.ActionRead, model.EffectAllow, organizationRef, createTestObjectID(), nil)
mockPDB.On("PoliciesForRole", ctx, roleRef).Return([]nstructures.PolicyAssignment{policyAssignment}, nil)
enforcer := createTestEnforcer(mockPDB, mockRDB)
// Execute
roles, permissions, err := enforcer.GetPermissions(ctx, accountRef, organizationRef)
// Verify
require.NoError(t, err)
assert.Len(t, roles, 1)
assert.Len(t, permissions, 1)
assert.Equal(t, accountRef, permissions[0].AccountRef)
mockRDB.AssertExpectations(t)
mockPDB.AssertExpectations(t)
})
}
// Security-focused test scenarios
func TestEnforcer_SecurityScenarios(t *testing.T) {
ctx := context.Background()
// Test data
accountRef := createTestObjectID()
organizationRef := createTestObjectID()
permissionRef := createTestObjectID()
objectRef := createTestObjectID()
roleRef := createTestObjectID()
t.Run("Security_DenyAlwaysWins", func(t *testing.T) {
mockPDB := &MockPoliciesDB{}
mockRDB := &MockRolesDB{}
// Mock role assignment
roleAssignment := createTestRoleAssignment(roleRef, accountRef, organizationRef)
mockRDB.On("Roles", ctx, accountRef, organizationRef).Return([]nstructures.RoleAssignment{roleAssignment}, nil)
// Mock multiple policies: both ALLOW and DENY
allowPolicy := createTestPolicyAssignment(roleRef, model.ActionRead, model.EffectAllow, organizationRef, permissionRef, &objectRef)
denyPolicy := createTestPolicyAssignment(roleRef, model.ActionRead, model.EffectDeny, organizationRef, permissionRef, &objectRef)
mockPDB.On("PoliciesForPermissionAction", ctx, roleRef, permissionRef, model.ActionRead).Return([]nstructures.PolicyAssignment{allowPolicy, denyPolicy}, nil)
enforcer := createTestEnforcer(mockPDB, mockRDB)
// Execute
allowed, err := enforcer.Enforce(ctx, permissionRef, accountRef, organizationRef, objectRef, model.ActionRead)
// Verify - DENY should always win
require.NoError(t, err)
assert.False(t, allowed)
mockRDB.AssertExpectations(t)
mockPDB.AssertExpectations(t)
})
t.Run("Security_InvalidObjectID", func(t *testing.T) {
mockPDB := &MockPoliciesDB{}
mockRDB := &MockRolesDB{}
// Mock database error for invalid ObjectID
dbError := errors.New("invalid ObjectID")
mockRDB.On("Roles", ctx, accountRef, organizationRef).Return([]nstructures.RoleAssignment{}, dbError)
enforcer := createTestEnforcer(mockPDB, mockRDB)
// Execute with invalid ObjectID
allowed, err := enforcer.Enforce(ctx, permissionRef, accountRef, organizationRef, objectRef, model.ActionRead)
// Verify - should fail securely
require.Error(t, err)
assert.False(t, allowed)
mockRDB.AssertExpectations(t)
})
}
// Note: This test provides comprehensive coverage of the native enforcer including:
// 1. Basic enforcement logic with deny-takes-precedence
// 2. Batch operations for performance
// 3. Role and permission retrieval
// 4. Security scenarios and edge cases
// 5. Error handling and database failures
// 6. All critical security paths are tested