UNPKG

rbac-engine

Version:

Role-based access control engine with policy-based permissions

268 lines 11.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const core_1 = require("../../core"); const builders_1 = require("../../builders"); const models_1 = require("../../models"); jest.mock('../../policy/evaluator', () => ({ evaluate: jest.fn() })); jest.mock('../../db/factory', () => ({ createRepository: jest.fn() })); describe('AccessControl Builder Integration', () => { let accessControl; let mockRepository; beforeEach(() => { mockRepository = { setupTables: jest.fn().mockResolvedValue(undefined), createUser: jest.fn(), getUser: jest.fn(), createRole: jest.fn(), getRole: jest.fn(), assignRoleToUser: jest.fn(), createPolicy: jest.fn(), updatePolicy: jest.fn(), updateRole: jest.fn(), deletePolicy: jest.fn(), deleteRole: jest.fn(), attachPolicyToRole: jest.fn(), attachPolicyToUser: jest.fn(), getUserPolicies: jest.fn(), getRolePolicies: jest.fn(), detachPolicyFromRole: jest.fn(), detachPolicyFromUser: jest.fn(), removeRoleFromUser: jest.fn() }; const { createRepository } = require('../../db/factory'); createRepository.mockReturnValue(mockRepository); accessControl = new core_1.AccessControl({}, jest.fn()); }); describe('Backward Compatibility', () => { it('should accept traditional Policy objects', async () => { const traditionalPolicy = { id: 'traditional-policy', document: { Version: '2023-11-15', Statement: [ { Effect: models_1.Effect.Allow, Action: ['read', 'write'], Resource: ['document/*'], Condition: { department: 'engineering' } } ] } }; mockRepository.createPolicy.mockResolvedValue(traditionalPolicy); const result = await accessControl.createPolicy(traditionalPolicy); expect(mockRepository.createPolicy).toHaveBeenCalledWith(traditionalPolicy); expect(result).toEqual(traditionalPolicy); }); it('should handle complex traditional policies with multiple statements', async () => { const complexTraditionalPolicy = { id: 'complex-traditional', document: { Version: '2023-11-15', Statement: [ { Effect: models_1.Effect.Allow, Action: ['read'], Resource: ['document/*'] }, { Effect: models_1.Effect.Deny, Action: ['delete'], Resource: ['document/confidential/*'] } ] } }; mockRepository.createPolicy.mockResolvedValue(complexTraditionalPolicy); const result = await accessControl.createPolicy(complexTraditionalPolicy); expect(mockRepository.createPolicy).toHaveBeenCalledWith(complexTraditionalPolicy); expect(result).toEqual(complexTraditionalPolicy); }); }); describe('Builder Pattern Integration', () => { it('should accept PolicyBuilder instances and convert them to Policy objects', async () => { const policyBuilder = new builders_1.PolicyBuilder('builder-policy') .version('2023-11-15') .allow(['read', 'write']) .on(['document/*']) .when({ department: 'engineering' }); const expectedPolicy = { id: 'builder-policy', document: { Version: '2023-11-15', Statement: [ { Effect: models_1.Effect.Allow, Action: ['read', 'write'], Resource: ['document/*'], Condition: { department: 'engineering' } } ] } }; mockRepository.createPolicy.mockResolvedValue(expectedPolicy); const result = await accessControl.createPolicy(policyBuilder); expect(mockRepository.createPolicy).toHaveBeenCalledWith(expectedPolicy); expect(result).toEqual(expectedPolicy); }); it('should handle complex builder policies with multiple statements', async () => { const policyBuilder = new builders_1.PolicyBuilder('complex-builder') .version('2023-11-15') .statement(new builders_1.StatementBuilder() .allow(['read', 'list']) .on(['document/*']) .when({ department: 'engineering' })) .statement(new builders_1.StatementBuilder() .deny(['delete']) .on(['document/confidential/*'])); const expectedPolicy = { id: 'complex-builder', document: { Version: '2023-11-15', Statement: [ { Effect: models_1.Effect.Allow, Action: ['read', 'list'], Resource: ['document/*'], Condition: { department: 'engineering' } }, { Effect: models_1.Effect.Deny, Action: ['delete'], Resource: ['document/confidential/*'] } ] } }; mockRepository.createPolicy.mockResolvedValue(expectedPolicy); const result = await accessControl.createPolicy(policyBuilder); expect(mockRepository.createPolicy).toHaveBeenCalledWith(expectedPolicy); expect(result).toEqual(expectedPolicy); }); it('should handle time-based builder policies', async () => { const policyBuilder = new builders_1.PolicyBuilder('time-based-policy') .allow(['read', 'write']) .on(['project/*']) .activeFrom('2025-01-01T00:00:00Z') .activeUntil('2025-12-31T23:59:59Z'); const expectedPolicy = { id: 'time-based-policy', document: { Version: '2023-11-15', Statement: [ { Effect: models_1.Effect.Allow, Action: ['read', 'write'], Resource: ['project/*'], StartDate: '2025-01-01T00:00:00Z', EndDate: '2025-12-31T23:59:59Z' } ] } }; mockRepository.createPolicy.mockResolvedValue(expectedPolicy); const result = await accessControl.createPolicy(policyBuilder); expect(mockRepository.createPolicy).toHaveBeenCalledWith(expectedPolicy); expect(result).toEqual(expectedPolicy); }); }); describe('Error Handling', () => { it('should propagate builder validation errors', async () => { const invalidBuilder = new builders_1.PolicyBuilder('invalid-policy') .allow(['read']); await expect(accessControl.createPolicy(invalidBuilder)) .rejects .toThrow('Invalid policy configuration'); }); it('should handle repository errors for both traditional and builder approaches', async () => { const repositoryError = new Error('Database connection failed'); mockRepository.createPolicy.mockRejectedValue(repositoryError); const traditionalPolicy = { id: 'test-policy', document: { Version: '2023-11-15', Statement: [ { Effect: models_1.Effect.Allow, Action: ['read'], Resource: ['document/*'] } ] } }; await expect(accessControl.createPolicy(traditionalPolicy)) .rejects .toThrow('Database connection failed'); const builderPolicy = new builders_1.PolicyBuilder('test-policy-2') .allow(['read']) .on(['document/*']); await expect(accessControl.createPolicy(builderPolicy)) .rejects .toThrow('Database connection failed'); }); }); describe('Type Safety', () => { it('should provide proper TypeScript types for both approaches', async () => { const traditionalPolicy = { id: 'type-test-1', document: { Version: '2023-11-15', Statement: [] } }; const builderPolicy = new builders_1.PolicyBuilder('type-test-2') .allow(['read']) .on(['document/*']); mockRepository.createPolicy.mockResolvedValue(traditionalPolicy); const result1 = accessControl.createPolicy(traditionalPolicy); const result2 = accessControl.createPolicy(builderPolicy); expect(result1).toBeDefined(); expect(result2).toBeDefined(); }); }); describe('Real-world Usage Patterns', () => { it('should support creating policies with both approaches in the same application', async () => { const simplePolicy = { id: 'simple-policy', document: { Version: '2023-11-15', Statement: [ { Effect: models_1.Effect.Allow, Action: ['read'], Resource: ['public/*'] } ] } }; const complexPolicy = new builders_1.PolicyBuilder('complex-policy') .statement(new builders_1.StatementBuilder() .allow(['read', 'write']) .on(['document/*']) .when({ department: 'engineering' }) .activeFrom('2025-01-01T00:00:00Z')) .statement(new builders_1.StatementBuilder() .deny(['delete']) .on(['document/production/*'])); mockRepository.createPolicy .mockResolvedValueOnce(simplePolicy) .mockResolvedValueOnce({ id: 'complex-policy', document: { Version: '2023-11-15', Statement: complexPolicy.build().document.Statement } }); const result1 = await accessControl.createPolicy(simplePolicy); const result2 = await accessControl.createPolicy(complexPolicy); expect(result1.id).toBe('simple-policy'); expect(result2.id).toBe('complex-policy'); expect(mockRepository.createPolicy).toHaveBeenCalledTimes(2); }); }); }); //# sourceMappingURL=integration.spec.js.map