UNPKG

rbac-engine

Version:

Role-based access control engine with policy-based permissions

308 lines 15.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const evaluator_1 = require("../../policy/evaluator"); const models_1 = require("../../models"); describe('Policy Evaluator', () => { describe('evaluateCondition', () => { it('should return true when all conditions match the context', () => { const condition = { department: 'engineering', country: 'USA' }; const context = { department: 'engineering', country: 'USA', role: 'developer' }; expect((0, evaluator_1.evaluateCondition)(condition, context)).toBe(true); }); it('should return false when any condition does not match the context', () => { const condition = { department: 'engineering', country: 'USA' }; const context = { department: 'engineering', country: 'Canada', role: 'developer' }; expect((0, evaluator_1.evaluateCondition)(condition, context)).toBe(false); }); it('should return false when context is missing required condition keys', () => { const condition = { department: 'engineering', country: 'USA' }; const context = { department: 'engineering' }; expect((0, evaluator_1.evaluateCondition)(condition, context)).toBe(false); }); }); describe('matches', () => { it('should return true for exact string match', () => { expect((0, evaluator_1.matches)('read', 'read')).toBe(true); }); it('should return false for non-matching strings', () => { expect((0, evaluator_1.matches)('read', 'write')).toBe(false); }); it('should handle wildcard at the beginning', () => { expect((0, evaluator_1.matches)('*:document', 'read:document')).toBe(true); expect((0, evaluator_1.matches)('*:document', 'write:document')).toBe(true); expect((0, evaluator_1.matches)('*:document', 'document')).toBe(false); }); it('should handle wildcard at the end', () => { expect((0, evaluator_1.matches)('document:*', 'document:read')).toBe(true); expect((0, evaluator_1.matches)('document:*', 'document:write')).toBe(true); expect((0, evaluator_1.matches)('document:*', 'document')).toBe(false); }); it('should handle wildcard in the middle', () => { expect((0, evaluator_1.matches)('document:*:sensitive', 'document:read:sensitive')).toBe(true); expect((0, evaluator_1.matches)('document:*:sensitive', 'document:write:sensitive')).toBe(true); expect((0, evaluator_1.matches)('document:*:sensitive', 'document:sensitive')).toBe(false); }); it('should handle multiple wildcards', () => { expect((0, evaluator_1.matches)('*:document:*', 'read:document:public')).toBe(true); expect((0, evaluator_1.matches)('*:document:*', 'write:document:secret')).toBe(true); expect((0, evaluator_1.matches)('*:document:*', 'document')).toBe(false); }); it('should check against array of patterns', () => { expect((0, evaluator_1.matches)(['read:*', 'write:document'], 'read:document')).toBe(true); expect((0, evaluator_1.matches)(['read:*', 'write:document'], 'write:document')).toBe(true); expect((0, evaluator_1.matches)(['read:*', 'write:document'], 'delete:document')).toBe(false); }); }); describe('isStatementActive', () => { const testDate = new Date(Date.UTC(2025, 4, 1, 12, 0, 0)); beforeEach(() => { jest.useFakeTimers().setSystemTime(testDate); }); afterEach(() => { jest.useRealTimers(); }); it('should return true when no date constraints are provided', () => { const statement = { Effect: models_1.Effect.Allow, Action: ['read'], Resource: ['document'] }; expect((0, evaluator_1.isStatementActive)(statement)).toBe(true); }); it('should return true when current time is after StartDate', () => { const statement = { Effect: models_1.Effect.Allow, Action: ['read'], Resource: ['document'], StartDate: '2025-04-01T00:00:00Z' }; expect((0, evaluator_1.isStatementActive)(statement)).toBe(true); }); it('should return false when current time is before StartDate', () => { const statement = { Effect: models_1.Effect.Allow, Action: ['read'], Resource: ['document'], StartDate: '2025-06-01T00:00:00Z' }; expect((0, evaluator_1.isStatementActive)(statement)).toBe(false); }); it('should return true when current time is before EndDate', () => { const statement = { Effect: models_1.Effect.Allow, Action: ['read'], Resource: ['document'], EndDate: '2025-06-01T00:00:00Z' }; expect((0, evaluator_1.isStatementActive)(statement)).toBe(true); }); it('should return false when current time is after EndDate', () => { const statement = { Effect: models_1.Effect.Allow, Action: ['read'], Resource: ['document'], EndDate: '2025-04-01T00:00:00Z' }; expect((0, evaluator_1.isStatementActive)(statement)).toBe(false); }); it('should return true when current time is within date range', () => { const statement = { Effect: models_1.Effect.Allow, Action: ['read'], Resource: ['document'], StartDate: '2025-04-01T00:00:00Z', EndDate: '2025-06-01T00:00:00Z' }; expect((0, evaluator_1.isStatementActive)(statement)).toBe(true); }); it('should return false when current time is outside date range (too early)', () => { const statement = { Effect: models_1.Effect.Allow, Action: ['read'], Resource: ['document'], StartDate: '2025-06-01T00:00:00Z', EndDate: '2025-07-01T00:00:00Z' }; expect((0, evaluator_1.isStatementActive)(statement)).toBe(false); }); it('should return false when current time is outside date range (too late)', () => { const statement = { Effect: models_1.Effect.Allow, Action: ['read'], Resource: ['document'], StartDate: '2025-01-01T00:00:00Z', EndDate: '2025-04-01T00:00:00Z' }; expect((0, evaluator_1.isStatementActive)(statement)).toBe(false); }); it('should return false when date format is invalid', () => { const statement = { Effect: models_1.Effect.Allow, Action: ['read'], Resource: ['document'], StartDate: 'invalid-date' }; expect((0, evaluator_1.isStatementActive)(statement)).toBe(false); }); }); describe('evaluate', () => { const createPolicy = (id, statements) => ({ id, document: { Version: '2023-10-17', Statement: statements } }); it('should return false when no policies are provided', () => { expect((0, evaluator_1.evaluate)([], 'read', 'document')).toBe(false); }); it('should return true when action and resource match an Allow policy', () => { const policy = createPolicy('p1', [{ Effect: models_1.Effect.Allow, Action: ['read'], Resource: ['document'] }]); expect((0, evaluator_1.evaluate)([policy], 'read', 'document')).toBe(true); }); it('should return false when action and resource match a Deny policy', () => { const policy = createPolicy('p1', [{ Effect: models_1.Effect.Deny, Action: ['read'], Resource: ['document'] }]); expect((0, evaluator_1.evaluate)([policy], 'read', 'document')).toBe(false); }); it('should handle wildcard patterns in actions and resources', () => { const policy = createPolicy('p1', [{ Effect: models_1.Effect.Allow, Action: ['read:*'], Resource: ['document:*'] }]); expect((0, evaluator_1.evaluate)([policy], 'read:metadata', 'document:report')).toBe(true); }); it('should enforce deny-override across multiple policies', () => { const allowPolicy = createPolicy('p1', [{ Effect: models_1.Effect.Allow, Action: ['read'], Resource: ['document'] }]); const denyPolicy = createPolicy('p2', [{ Effect: models_1.Effect.Deny, Action: ['read'], Resource: ['document'] }]); expect((0, evaluator_1.evaluate)([allowPolicy, denyPolicy], 'read', 'document')).toBe(false); expect((0, evaluator_1.evaluate)([denyPolicy, allowPolicy], 'read', 'document')).toBe(false); }); it('should evaluate conditions in statements', () => { const policy = createPolicy('p1', [{ Effect: models_1.Effect.Allow, Action: ['read'], Resource: ['document'], Condition: { department: 'engineering' } }]); expect((0, evaluator_1.evaluate)([policy], 'read', 'document', { department: 'engineering' })).toBe(true); expect((0, evaluator_1.evaluate)([policy], 'read', 'document', { department: 'marketing' })).toBe(false); }); it('should handle multiple statements in a policy', () => { const policy = createPolicy('p1', [ { Effect: models_1.Effect.Allow, Action: ['read'], Resource: ['document'] }, { Effect: models_1.Effect.Deny, Action: ['read'], Resource: ['document:secret'] } ]); expect((0, evaluator_1.evaluate)([policy], 'read', 'document')).toBe(true); expect((0, evaluator_1.evaluate)([policy], 'read', 'document:secret')).toBe(false); }); it('should handle multiple policies with different effects', () => { const policy1 = createPolicy('p1', [{ Effect: models_1.Effect.Allow, Action: ['read', 'write'], Resource: ['*'] }]); const policy2 = createPolicy('p2', [{ Effect: models_1.Effect.Deny, Action: ['write'], Resource: ['document:secret'] }]); expect((0, evaluator_1.evaluate)([policy1, policy2], 'read', 'document')).toBe(true); expect((0, evaluator_1.evaluate)([policy1, policy2], 'write', 'document')).toBe(true); expect((0, evaluator_1.evaluate)([policy1, policy2], 'write', 'document:secret')).toBe(false); }); describe('with date constraints', () => { const testDate = new Date(Date.UTC(2025, 4, 1, 12, 0, 0)); beforeEach(() => { jest.useFakeTimers().setSystemTime(testDate); }); afterEach(() => { jest.useRealTimers(); }); it('should allow access when policy is within active date range', () => { const policy = createPolicy('p1', [{ Effect: models_1.Effect.Allow, Action: ['read'], Resource: ['document'], StartDate: '2025-04-01T00:00:00Z', EndDate: '2025-06-01T00:00:00Z' }]); expect((0, evaluator_1.evaluate)([policy], 'read', 'document')).toBe(true); }); it('should deny access when policy has not yet become active', () => { const policy = createPolicy('p1', [{ Effect: models_1.Effect.Allow, Action: ['read'], Resource: ['document'], StartDate: '2025-06-01T00:00:00Z', }]); expect((0, evaluator_1.evaluate)([policy], 'read', 'document')).toBe(false); }); it('should deny access when policy has expired', () => { const policy = createPolicy('p1', [{ Effect: models_1.Effect.Allow, Action: ['read'], Resource: ['document'], EndDate: '2025-04-01T00:00:00Z', }]); expect((0, evaluator_1.evaluate)([policy], 'read', 'document')).toBe(false); }); it('should correctly handle mixed time-constrained and regular policies', () => { const expiredPolicy = createPolicy('p1', [{ Effect: models_1.Effect.Allow, Action: ['write'], Resource: ['document'], EndDate: '2025-04-01T00:00:00Z', }]); const futurePolicy = createPolicy('p2', [{ Effect: models_1.Effect.Allow, Action: ['delete'], Resource: ['document'], StartDate: '2025-06-01T00:00:00Z', }]); const activePolicy = createPolicy('p3', [{ Effect: models_1.Effect.Allow, Action: ['read'], Resource: ['document'], StartDate: '2025-04-01T00:00:00Z', EndDate: '2025-06-01T00:00:00Z', }]); const regularPolicy = createPolicy('p4', [{ Effect: models_1.Effect.Allow, Action: ['list'], Resource: ['document'], }]); const policies = [expiredPolicy, futurePolicy, activePolicy, regularPolicy]; expect((0, evaluator_1.evaluate)(policies, 'read', 'document')).toBe(true); expect((0, evaluator_1.evaluate)(policies, 'list', 'document')).toBe(true); expect((0, evaluator_1.evaluate)(policies, 'write', 'document')).toBe(false); expect((0, evaluator_1.evaluate)(policies, 'delete', 'document')).toBe(false); }); }); }); }); //# sourceMappingURL=evaluator.spec.js.map