rbac-engine
Version:
Role-based access control engine with policy-based permissions
310 lines • 14 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const core_1 = require("../../core");
const models_1 = require("../../models");
const evaluator_1 = require("../../policy/evaluator");
jest.mock('../../policy/evaluator', () => ({
evaluate: jest.fn()
}));
jest.mock('../../db/factory', () => ({
createRepository: jest.fn()
}));
describe('AccessControl', () => {
const mockRepository = {
setupTables: jest.fn().mockResolvedValue(undefined),
createUser: jest.fn(),
getUser: jest.fn(),
createRole: jest.fn(),
getRole: jest.fn(),
assignRoleToUser: jest.fn(),
createPolicy: jest.fn(),
attachPolicyToRole: jest.fn().mockResolvedValue(undefined),
attachPolicyToUser: jest.fn().mockResolvedValue(undefined),
getUserPolicies: jest.fn(),
getRolePolicies: jest.fn(),
updateRole: jest.fn(),
updatePolicy: jest.fn(),
deletePolicy: jest.fn().mockResolvedValue(undefined),
deleteRole: jest.fn().mockResolvedValue(undefined),
detachPolicyFromRole: jest.fn().mockResolvedValue(undefined),
detachPolicyFromUser: jest.fn().mockResolvedValue(undefined),
removeRoleFromUser: jest.fn().mockResolvedValue(undefined)
};
class MockRepositoryClass {
constructor(_client) {
this.setupTables = mockRepository.setupTables;
this.createUser = mockRepository.createUser;
this.getUser = mockRepository.getUser;
this.createRole = mockRepository.createRole;
this.getRole = mockRepository.getRole;
this.assignRoleToUser = mockRepository.assignRoleToUser;
this.createPolicy = mockRepository.createPolicy;
this.attachPolicyToRole = mockRepository.attachPolicyToRole;
this.attachPolicyToUser = mockRepository.attachPolicyToUser;
this.getUserPolicies = mockRepository.getUserPolicies;
this.getRolePolicies = mockRepository.getRolePolicies;
this.updateRole = mockRepository.updateRole;
this.updatePolicy = mockRepository.updatePolicy;
this.deletePolicy = mockRepository.deletePolicy;
this.deleteRole = mockRepository.deleteRole;
this.detachPolicyFromRole = mockRepository.detachPolicyFromRole;
this.detachPolicyFromUser = mockRepository.detachPolicyFromUser;
this.removeRoleFromUser = mockRepository.removeRoleFromUser;
}
}
const mockCreateRepository = require('../../db/factory').createRepository;
mockCreateRepository.mockImplementation((_client, _repoConstructor) => mockRepository);
const mockClient = { type: 'mock' };
let accessControl;
beforeEach(() => {
jest.clearAllMocks();
accessControl = new core_1.AccessControl(mockClient, MockRepositoryClass);
});
describe('init', () => {
it('should call setupTables on the repository', async () => {
await accessControl.init();
expect(mockRepository.setupTables).toHaveBeenCalledTimes(1);
});
});
describe('createUser', () => {
it('should create a user via the repository', async () => {
const mockUser = { id: 'u1', name: 'Test User' };
mockRepository.createUser.mockResolvedValue(mockUser);
const result = await accessControl.createUser(mockUser);
expect(mockRepository.createUser).toHaveBeenCalledWith(mockUser);
expect(result).toEqual(mockUser);
});
});
describe('createRole', () => {
it('should create a role via the repository', async () => {
const mockRole = { id: 'r1', name: 'Admin' };
mockRepository.createRole.mockResolvedValue(mockRole);
const result = await accessControl.createRole(mockRole);
expect(mockRepository.createRole).toHaveBeenCalledWith(mockRole);
expect(result).toEqual(mockRole);
});
});
describe('assignRoleToUser', () => {
it('should assign a role to a user via the repository', async () => {
const userId = 'u1';
const roleId = 'r1';
const updatedUser = {
id: userId,
name: 'Test User',
roles: [roleId]
};
mockRepository.assignRoleToUser.mockResolvedValue(updatedUser);
const result = await accessControl.assignRoleToUser(userId, roleId);
expect(mockRepository.assignRoleToUser).toHaveBeenCalledWith(userId, roleId);
expect(result).toEqual(updatedUser);
});
});
describe('createPolicy', () => {
it('should create a policy via the repository', async () => {
const mockPolicy = {
id: 'p1',
document: {
Version: '2023-10-17',
Statement: [{
Effect: models_1.Effect.Allow,
Action: ['read'],
Resource: ['document']
}]
}
};
mockRepository.createPolicy.mockResolvedValue(mockPolicy);
const result = await accessControl.createPolicy(mockPolicy);
expect(mockRepository.createPolicy).toHaveBeenCalledWith(mockPolicy);
expect(result).toEqual(mockPolicy);
});
});
describe('attachPolicyToRole', () => {
it('should attach a policy to a role via the repository', async () => {
const policyId = 'p1';
const roleId = 'r1';
await accessControl.attachPolicyToRole(policyId, roleId);
expect(mockRepository.attachPolicyToRole).toHaveBeenCalledWith(policyId, roleId);
});
});
describe('getUser', () => {
it('should retrieve a user via the repository', async () => {
const userId = 'u1';
const mockUser = { id: userId, name: 'Test User' };
mockRepository.getUser.mockResolvedValue(mockUser);
const result = await accessControl.getUser(userId);
expect(mockRepository.getUser).toHaveBeenCalledWith(userId);
expect(result).toEqual(mockUser);
});
});
describe('getRole', () => {
it('should retrieve a role via the repository', async () => {
const roleId = 'r1';
const mockRole = { id: roleId, name: 'Admin' };
mockRepository.getRole.mockResolvedValue(mockRole);
const result = await accessControl.getRole(roleId);
expect(mockRepository.getRole).toHaveBeenCalledWith(roleId);
expect(result).toEqual(mockRole);
});
});
describe('getUserPolicies', () => {
it('should retrieve user policies via the repository', async () => {
const userId = 'u1';
const mockPolicies = [
{
id: 'p1',
document: {
Version: '2023-10-17',
Statement: [{
Effect: models_1.Effect.Allow,
Action: ['read'],
Resource: ['document']
}]
}
}
];
mockRepository.getUserPolicies.mockResolvedValue(mockPolicies);
const result = await accessControl.getUserPolicies(userId);
expect(mockRepository.getUserPolicies).toHaveBeenCalledWith(userId);
expect(result).toEqual(mockPolicies);
});
});
describe('hasAccess', () => {
it('should evaluate policies from user and roles to determine access', async () => {
const userId = 'u1';
const action = 'read';
const resource = 'document';
const context = { department: 'engineering' };
const user = { id: userId, name: 'Test User', roles: ['r1', 'r2'] };
const userPolicies = [
{
id: 'p1',
document: {
Version: '2023-10-17',
Statement: [{
Effect: models_1.Effect.Allow,
Action: ['read'],
Resource: ['document']
}]
}
}
];
const role1Policies = [
{
id: 'p2',
document: {
Version: '2023-10-17',
Statement: [{
Effect: models_1.Effect.Allow,
Action: ['write'],
Resource: ['document']
}]
}
}
];
const role2Policies = [
{
id: 'p3',
document: {
Version: '2023-10-17',
Statement: [{
Effect: models_1.Effect.Deny,
Action: ['delete'],
Resource: ['document']
}]
}
}
];
mockRepository.getUser.mockResolvedValue(user);
mockRepository.getUserPolicies.mockResolvedValue(userPolicies);
mockRepository.getRolePolicies
.mockResolvedValueOnce(role1Policies)
.mockResolvedValueOnce(role2Policies);
evaluator_1.evaluate.mockReturnValue(true);
const result = await accessControl.hasAccess(userId, action, resource, context);
expect(mockRepository.getUser).toHaveBeenCalledWith(userId);
expect(mockRepository.getUserPolicies).toHaveBeenCalledWith(userId);
expect(mockRepository.getRolePolicies).toHaveBeenCalledWith('r1');
expect(mockRepository.getRolePolicies).toHaveBeenCalledWith('r2');
expect(evaluator_1.evaluate).toHaveBeenCalledWith([...userPolicies, ...role1Policies, ...role2Policies], action, resource, context);
expect(result).toBe(true);
});
it('should return false when evaluate returns false', async () => {
const userId = 'u1';
const user = { id: userId, name: 'Test User', roles: [] };
const userPolicies = [];
mockRepository.getUser.mockResolvedValue(user);
mockRepository.getUserPolicies.mockResolvedValue(userPolicies);
evaluator_1.evaluate.mockReturnValue(false);
const result = await accessControl.hasAccess(userId, 'read', 'document');
expect(evaluator_1.evaluate).toHaveBeenCalledWith([], 'read', 'document', {});
expect(result).toBe(false);
});
});
describe('updateRole', () => {
it('should update a role via the repository', async () => {
const mockRole = { id: 'r1', name: 'Updated Admin' };
mockRepository.updateRole.mockResolvedValue(mockRole);
const result = await accessControl.updateRole(mockRole);
expect(mockRepository.updateRole).toHaveBeenCalledWith(mockRole);
expect(result).toEqual(mockRole);
});
});
describe('updatePolicy', () => {
it('should update a policy via the repository', async () => {
const mockPolicy = {
id: 'p1',
document: {
Version: '2023-10-17',
Statement: [{
Effect: models_1.Effect.Allow,
Action: ['read', 'write'],
Resource: ['document']
}]
}
};
mockRepository.updatePolicy.mockResolvedValue(mockPolicy);
const result = await accessControl.updatePolicy(mockPolicy);
expect(mockRepository.updatePolicy).toHaveBeenCalledWith(mockPolicy);
expect(result).toEqual(mockPolicy);
});
});
describe('deletePolicy', () => {
it('should delete a policy via the repository', async () => {
const policyId = 'p1';
await accessControl.deletePolicy(policyId);
expect(mockRepository.deletePolicy).toHaveBeenCalledWith(policyId);
});
});
describe('deleteRole', () => {
it('should delete a role via the repository', async () => {
const roleId = 'r1';
await accessControl.deleteRole(roleId);
expect(mockRepository.deleteRole).toHaveBeenCalledWith(roleId);
});
});
describe('detachPolicyFromRole', () => {
it('should detach a policy from a role via the repository', async () => {
const policyId = 'p1';
const roleId = 'r1';
await accessControl.detachPolicyFromRole(policyId, roleId);
expect(mockRepository.detachPolicyFromRole).toHaveBeenCalledWith(policyId, roleId);
});
});
describe('detachPolicyFromUser', () => {
it('should detach a policy from a user via the repository', async () => {
const policyId = 'p1';
const userId = 'u1';
await accessControl.detachPolicyFromUser(policyId, userId);
expect(mockRepository.detachPolicyFromUser).toHaveBeenCalledWith(policyId, userId);
});
});
describe('removeRoleFromUser', () => {
it('should remove a role from a user via the repository', async () => {
const userId = 'u1';
const roleId = 'r1';
await accessControl.removeRoleFromUser(userId, roleId);
expect(mockRepository.removeRoleFromUser).toHaveBeenCalledWith(userId, roleId);
});
});
});
//# sourceMappingURL=access-control.spec.js.map