mirror of
https://github.com/danny-avila/LibreChat.git
synced 2025-12-23 03:40:14 +01:00
ci: add unit tests for access control middleware
- Introduced tests for the `canAccessAgentResource` middleware to validate permission checks for agent resources. - Implemented tests for various scenarios including user roles, ACL entries, and permission levels. - Added tests for the `checkAccess` function to ensure proper permission handling based on user roles and permissions. - Utilized MongoDB in-memory server for isolated test environments.
This commit is contained in:
parent
004f128aec
commit
f897420586
2 changed files with 635 additions and 0 deletions
|
|
@ -0,0 +1,384 @@
|
||||||
|
const mongoose = require('mongoose');
|
||||||
|
const { MongoMemoryServer } = require('mongodb-memory-server');
|
||||||
|
const { canAccessAgentResource } = require('./canAccessAgentResource');
|
||||||
|
const { User, Role, AclEntry } = require('~/db/models');
|
||||||
|
const { createAgent } = require('~/models/Agent');
|
||||||
|
|
||||||
|
describe('canAccessAgentResource middleware', () => {
|
||||||
|
let mongoServer;
|
||||||
|
let req, res, next;
|
||||||
|
let testUser;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
mongoServer = await MongoMemoryServer.create();
|
||||||
|
const mongoUri = mongoServer.getUri();
|
||||||
|
await mongoose.connect(mongoUri);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await mongoose.disconnect();
|
||||||
|
await mongoServer.stop();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await mongoose.connection.dropDatabase();
|
||||||
|
await Role.create({
|
||||||
|
name: 'test-role',
|
||||||
|
permissions: {
|
||||||
|
AGENTS: {
|
||||||
|
USE: true,
|
||||||
|
CREATE: true,
|
||||||
|
SHARED_GLOBAL: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create a test user
|
||||||
|
testUser = await User.create({
|
||||||
|
email: 'test@example.com',
|
||||||
|
name: 'Test User',
|
||||||
|
username: 'testuser',
|
||||||
|
role: 'test-role',
|
||||||
|
});
|
||||||
|
|
||||||
|
req = {
|
||||||
|
user: { id: testUser._id.toString(), role: 'test-role' },
|
||||||
|
params: {},
|
||||||
|
};
|
||||||
|
res = {
|
||||||
|
status: jest.fn().mockReturnThis(),
|
||||||
|
json: jest.fn(),
|
||||||
|
};
|
||||||
|
next = jest.fn();
|
||||||
|
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('middleware factory', () => {
|
||||||
|
test('should throw error if requiredPermission is not provided', () => {
|
||||||
|
expect(() => canAccessAgentResource({})).toThrow(
|
||||||
|
'canAccessAgentResource: requiredPermission is required and must be a number',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should throw error if requiredPermission is not a number', () => {
|
||||||
|
expect(() => canAccessAgentResource({ requiredPermission: '1' })).toThrow(
|
||||||
|
'canAccessAgentResource: requiredPermission is required and must be a number',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should create middleware with default resourceIdParam', () => {
|
||||||
|
const middleware = canAccessAgentResource({ requiredPermission: 1 });
|
||||||
|
expect(typeof middleware).toBe('function');
|
||||||
|
expect(middleware.length).toBe(3); // Express middleware signature
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should create middleware with custom resourceIdParam', () => {
|
||||||
|
const middleware = canAccessAgentResource({
|
||||||
|
requiredPermission: 2,
|
||||||
|
resourceIdParam: 'agent_id',
|
||||||
|
});
|
||||||
|
expect(typeof middleware).toBe('function');
|
||||||
|
expect(middleware.length).toBe(3);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('permission checking with real agents', () => {
|
||||||
|
test('should allow access when user is the agent author', async () => {
|
||||||
|
// Create an agent owned by the test user
|
||||||
|
const agent = await createAgent({
|
||||||
|
id: `agent_${Date.now()}`,
|
||||||
|
name: 'Test Agent',
|
||||||
|
provider: 'openai',
|
||||||
|
model: 'gpt-4',
|
||||||
|
author: testUser._id,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create ACL entry for the author (owner permissions)
|
||||||
|
await AclEntry.create({
|
||||||
|
principalType: 'user',
|
||||||
|
principalId: testUser._id,
|
||||||
|
principalModel: 'User',
|
||||||
|
resourceType: 'agent',
|
||||||
|
resourceId: agent._id,
|
||||||
|
permBits: 15, // All permissions (1+2+4+8)
|
||||||
|
grantedBy: testUser._id,
|
||||||
|
});
|
||||||
|
|
||||||
|
req.params.id = agent.id;
|
||||||
|
|
||||||
|
const middleware = canAccessAgentResource({ requiredPermission: 1 }); // VIEW permission
|
||||||
|
await middleware(req, res, next);
|
||||||
|
|
||||||
|
expect(next).toHaveBeenCalled();
|
||||||
|
expect(res.status).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should deny access when user is not the author and has no ACL entry', async () => {
|
||||||
|
// Create an agent owned by a different user
|
||||||
|
const otherUser = await User.create({
|
||||||
|
email: 'other@example.com',
|
||||||
|
name: 'Other User',
|
||||||
|
username: 'otheruser',
|
||||||
|
role: 'test-role',
|
||||||
|
});
|
||||||
|
|
||||||
|
const agent = await createAgent({
|
||||||
|
id: `agent_${Date.now()}`,
|
||||||
|
name: 'Other User Agent',
|
||||||
|
provider: 'openai',
|
||||||
|
model: 'gpt-4',
|
||||||
|
author: otherUser._id,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create ACL entry for the other user (owner)
|
||||||
|
await AclEntry.create({
|
||||||
|
principalType: 'user',
|
||||||
|
principalId: otherUser._id,
|
||||||
|
principalModel: 'User',
|
||||||
|
resourceType: 'agent',
|
||||||
|
resourceId: agent._id,
|
||||||
|
permBits: 15, // All permissions
|
||||||
|
grantedBy: otherUser._id,
|
||||||
|
});
|
||||||
|
|
||||||
|
req.params.id = agent.id;
|
||||||
|
|
||||||
|
const middleware = canAccessAgentResource({ requiredPermission: 1 }); // VIEW permission
|
||||||
|
await middleware(req, res, next);
|
||||||
|
|
||||||
|
expect(next).not.toHaveBeenCalled();
|
||||||
|
expect(res.status).toHaveBeenCalledWith(403);
|
||||||
|
expect(res.json).toHaveBeenCalledWith({
|
||||||
|
error: 'Forbidden',
|
||||||
|
message: 'Insufficient permissions to access this agent',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should allow access when user has ACL entry with sufficient permissions', async () => {
|
||||||
|
// Create an agent owned by a different user
|
||||||
|
const otherUser = await User.create({
|
||||||
|
email: 'other2@example.com',
|
||||||
|
name: 'Other User 2',
|
||||||
|
username: 'otheruser2',
|
||||||
|
role: 'test-role',
|
||||||
|
});
|
||||||
|
|
||||||
|
const agent = await createAgent({
|
||||||
|
id: `agent_${Date.now()}`,
|
||||||
|
name: 'Shared Agent',
|
||||||
|
provider: 'openai',
|
||||||
|
model: 'gpt-4',
|
||||||
|
author: otherUser._id,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create ACL entry granting view permission to test user
|
||||||
|
await AclEntry.create({
|
||||||
|
principalType: 'user',
|
||||||
|
principalId: testUser._id,
|
||||||
|
principalModel: 'User',
|
||||||
|
resourceType: 'agent',
|
||||||
|
resourceId: agent._id,
|
||||||
|
permBits: 1, // VIEW permission
|
||||||
|
grantedBy: otherUser._id,
|
||||||
|
});
|
||||||
|
|
||||||
|
req.params.id = agent.id;
|
||||||
|
|
||||||
|
const middleware = canAccessAgentResource({ requiredPermission: 1 }); // VIEW permission
|
||||||
|
await middleware(req, res, next);
|
||||||
|
|
||||||
|
expect(next).toHaveBeenCalled();
|
||||||
|
expect(res.status).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should deny access when ACL permissions are insufficient', async () => {
|
||||||
|
// Create an agent owned by a different user
|
||||||
|
const otherUser = await User.create({
|
||||||
|
email: 'other3@example.com',
|
||||||
|
name: 'Other User 3',
|
||||||
|
username: 'otheruser3',
|
||||||
|
role: 'test-role',
|
||||||
|
});
|
||||||
|
|
||||||
|
const agent = await createAgent({
|
||||||
|
id: `agent_${Date.now()}`,
|
||||||
|
name: 'Limited Access Agent',
|
||||||
|
provider: 'openai',
|
||||||
|
model: 'gpt-4',
|
||||||
|
author: otherUser._id,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create ACL entry granting only view permission
|
||||||
|
await AclEntry.create({
|
||||||
|
principalType: 'user',
|
||||||
|
principalId: testUser._id,
|
||||||
|
principalModel: 'User',
|
||||||
|
resourceType: 'agent',
|
||||||
|
resourceId: agent._id,
|
||||||
|
permBits: 1, // VIEW permission only
|
||||||
|
grantedBy: otherUser._id,
|
||||||
|
});
|
||||||
|
|
||||||
|
req.params.id = agent.id;
|
||||||
|
|
||||||
|
const middleware = canAccessAgentResource({ requiredPermission: 2 }); // EDIT permission required
|
||||||
|
await middleware(req, res, next);
|
||||||
|
|
||||||
|
expect(next).not.toHaveBeenCalled();
|
||||||
|
expect(res.status).toHaveBeenCalledWith(403);
|
||||||
|
expect(res.json).toHaveBeenCalledWith({
|
||||||
|
error: 'Forbidden',
|
||||||
|
message: 'Insufficient permissions to access this agent',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should handle non-existent agent', async () => {
|
||||||
|
req.params.id = 'agent_nonexistent';
|
||||||
|
|
||||||
|
const middleware = canAccessAgentResource({ requiredPermission: 1 });
|
||||||
|
await middleware(req, res, next);
|
||||||
|
|
||||||
|
expect(next).not.toHaveBeenCalled();
|
||||||
|
expect(res.status).toHaveBeenCalledWith(404);
|
||||||
|
expect(res.json).toHaveBeenCalledWith({
|
||||||
|
error: 'Not Found',
|
||||||
|
message: 'agent not found',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should use custom resourceIdParam', async () => {
|
||||||
|
const agent = await createAgent({
|
||||||
|
id: `agent_${Date.now()}`,
|
||||||
|
name: 'Custom Param Agent',
|
||||||
|
provider: 'openai',
|
||||||
|
model: 'gpt-4',
|
||||||
|
author: testUser._id,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create ACL entry for the author
|
||||||
|
await AclEntry.create({
|
||||||
|
principalType: 'user',
|
||||||
|
principalId: testUser._id,
|
||||||
|
principalModel: 'User',
|
||||||
|
resourceType: 'agent',
|
||||||
|
resourceId: agent._id,
|
||||||
|
permBits: 15, // All permissions
|
||||||
|
grantedBy: testUser._id,
|
||||||
|
});
|
||||||
|
|
||||||
|
req.params.agent_id = agent.id; // Using custom param name
|
||||||
|
|
||||||
|
const middleware = canAccessAgentResource({
|
||||||
|
requiredPermission: 1,
|
||||||
|
resourceIdParam: 'agent_id',
|
||||||
|
});
|
||||||
|
await middleware(req, res, next);
|
||||||
|
|
||||||
|
expect(next).toHaveBeenCalled();
|
||||||
|
expect(res.status).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('permission levels', () => {
|
||||||
|
let agent;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
agent = await createAgent({
|
||||||
|
id: `agent_${Date.now()}`,
|
||||||
|
name: 'Permission Test Agent',
|
||||||
|
provider: 'openai',
|
||||||
|
model: 'gpt-4',
|
||||||
|
author: testUser._id,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create ACL entry with all permissions for the owner
|
||||||
|
await AclEntry.create({
|
||||||
|
principalType: 'user',
|
||||||
|
principalId: testUser._id,
|
||||||
|
principalModel: 'User',
|
||||||
|
resourceType: 'agent',
|
||||||
|
resourceId: agent._id,
|
||||||
|
permBits: 15, // All permissions (1+2+4+8)
|
||||||
|
grantedBy: testUser._id,
|
||||||
|
});
|
||||||
|
|
||||||
|
req.params.id = agent.id;
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should support view permission (1)', async () => {
|
||||||
|
const middleware = canAccessAgentResource({ requiredPermission: 1 });
|
||||||
|
await middleware(req, res, next);
|
||||||
|
expect(next).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should support edit permission (2)', async () => {
|
||||||
|
const middleware = canAccessAgentResource({ requiredPermission: 2 });
|
||||||
|
await middleware(req, res, next);
|
||||||
|
expect(next).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should support delete permission (4)', async () => {
|
||||||
|
const middleware = canAccessAgentResource({ requiredPermission: 4 });
|
||||||
|
await middleware(req, res, next);
|
||||||
|
expect(next).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should support share permission (8)', async () => {
|
||||||
|
const middleware = canAccessAgentResource({ requiredPermission: 8 });
|
||||||
|
await middleware(req, res, next);
|
||||||
|
expect(next).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should support combined permissions', async () => {
|
||||||
|
const viewAndEdit = 1 | 2; // 3
|
||||||
|
const middleware = canAccessAgentResource({ requiredPermission: viewAndEdit });
|
||||||
|
await middleware(req, res, next);
|
||||||
|
expect(next).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('integration with agent operations', () => {
|
||||||
|
test('should work with agent CRUD operations', async () => {
|
||||||
|
const agentId = `agent_${Date.now()}`;
|
||||||
|
|
||||||
|
// Create agent
|
||||||
|
const agent = await createAgent({
|
||||||
|
id: agentId,
|
||||||
|
name: 'Integration Test Agent',
|
||||||
|
provider: 'openai',
|
||||||
|
model: 'gpt-4',
|
||||||
|
author: testUser._id,
|
||||||
|
description: 'Testing integration',
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create ACL entry for the author
|
||||||
|
await AclEntry.create({
|
||||||
|
principalType: 'user',
|
||||||
|
principalId: testUser._id,
|
||||||
|
principalModel: 'User',
|
||||||
|
resourceType: 'agent',
|
||||||
|
resourceId: agent._id,
|
||||||
|
permBits: 15, // All permissions
|
||||||
|
grantedBy: testUser._id,
|
||||||
|
});
|
||||||
|
|
||||||
|
req.params.id = agentId;
|
||||||
|
|
||||||
|
// Test view access
|
||||||
|
const viewMiddleware = canAccessAgentResource({ requiredPermission: 1 });
|
||||||
|
await viewMiddleware(req, res, next);
|
||||||
|
expect(next).toHaveBeenCalled();
|
||||||
|
jest.clearAllMocks();
|
||||||
|
|
||||||
|
// Update the agent
|
||||||
|
const { updateAgent } = require('~/models/Agent');
|
||||||
|
await updateAgent({ id: agentId }, { description: 'Updated description' });
|
||||||
|
|
||||||
|
// Test edit access
|
||||||
|
const editMiddleware = canAccessAgentResource({ requiredPermission: 2 });
|
||||||
|
await editMiddleware(req, res, next);
|
||||||
|
expect(next).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
251
api/server/middleware/roles/access.spec.js
Normal file
251
api/server/middleware/roles/access.spec.js
Normal file
|
|
@ -0,0 +1,251 @@
|
||||||
|
const mongoose = require('mongoose');
|
||||||
|
const { MongoMemoryServer } = require('mongodb-memory-server');
|
||||||
|
const { checkAccess, generateCheckAccess } = require('./access');
|
||||||
|
const { PermissionTypes, Permissions } = require('librechat-data-provider');
|
||||||
|
const { Role } = require('~/db/models');
|
||||||
|
|
||||||
|
// Mock only the logger
|
||||||
|
jest.mock('~/config', () => ({
|
||||||
|
logger: {
|
||||||
|
warn: jest.fn(),
|
||||||
|
error: jest.fn(),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('Access Middleware', () => {
|
||||||
|
let mongoServer;
|
||||||
|
let req, res, next;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
mongoServer = await MongoMemoryServer.create();
|
||||||
|
const mongoUri = mongoServer.getUri();
|
||||||
|
await mongoose.connect(mongoUri);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
await mongoose.disconnect();
|
||||||
|
await mongoServer.stop();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await mongoose.connection.dropDatabase();
|
||||||
|
|
||||||
|
// Create test roles
|
||||||
|
await Role.create({
|
||||||
|
name: 'user',
|
||||||
|
permissions: {
|
||||||
|
[PermissionTypes.AGENTS]: {
|
||||||
|
[Permissions.USE]: true,
|
||||||
|
[Permissions.CREATE]: false,
|
||||||
|
[Permissions.SHARED_GLOBAL]: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await Role.create({
|
||||||
|
name: 'admin',
|
||||||
|
permissions: {
|
||||||
|
[PermissionTypes.AGENTS]: {
|
||||||
|
[Permissions.USE]: true,
|
||||||
|
[Permissions.CREATE]: true,
|
||||||
|
[Permissions.SHARED_GLOBAL]: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
req = {
|
||||||
|
user: { id: 'user123', role: 'user' },
|
||||||
|
body: {},
|
||||||
|
};
|
||||||
|
res = {
|
||||||
|
status: jest.fn().mockReturnThis(),
|
||||||
|
json: jest.fn(),
|
||||||
|
};
|
||||||
|
next = jest.fn();
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('checkAccess', () => {
|
||||||
|
test('should return false if user is not provided', async () => {
|
||||||
|
const result = await checkAccess(null, PermissionTypes.AGENTS, [Permissions.USE]);
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should return true if user has required permission', async () => {
|
||||||
|
const result = await checkAccess(req.user, PermissionTypes.AGENTS, [Permissions.USE]);
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should return false if user lacks required permission', async () => {
|
||||||
|
const result = await checkAccess(req.user, PermissionTypes.AGENTS, [Permissions.CREATE]);
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should return true if user has any of multiple permissions', async () => {
|
||||||
|
const result = await checkAccess(req.user, PermissionTypes.AGENTS, [
|
||||||
|
Permissions.USE,
|
||||||
|
Permissions.CREATE,
|
||||||
|
]);
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should check body properties when permission is not directly granted', async () => {
|
||||||
|
// User role doesn't have CREATE permission, but bodyProps allows it
|
||||||
|
const bodyProps = {
|
||||||
|
[Permissions.CREATE]: ['agentId', 'name'],
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkObject = { agentId: 'agent123' };
|
||||||
|
|
||||||
|
const result = await checkAccess(
|
||||||
|
req.user,
|
||||||
|
PermissionTypes.AGENTS,
|
||||||
|
[Permissions.CREATE],
|
||||||
|
bodyProps,
|
||||||
|
checkObject,
|
||||||
|
);
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should return false if role is not found', async () => {
|
||||||
|
req.user.role = 'nonexistent';
|
||||||
|
const result = await checkAccess(req.user, PermissionTypes.AGENTS, [Permissions.USE]);
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should return false if role has no permissions for the requested type', async () => {
|
||||||
|
await Role.create({
|
||||||
|
name: 'limited',
|
||||||
|
permissions: {
|
||||||
|
// Explicitly set AGENTS permissions to false
|
||||||
|
[PermissionTypes.AGENTS]: {
|
||||||
|
[Permissions.USE]: false,
|
||||||
|
[Permissions.CREATE]: false,
|
||||||
|
[Permissions.SHARED_GLOBAL]: false,
|
||||||
|
},
|
||||||
|
// Has permissions for other types
|
||||||
|
[PermissionTypes.PROMPTS]: {
|
||||||
|
[Permissions.USE]: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
req.user.role = 'limited';
|
||||||
|
|
||||||
|
const result = await checkAccess(req.user, PermissionTypes.AGENTS, [Permissions.USE]);
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should handle admin role with all permissions', async () => {
|
||||||
|
req.user.role = 'admin';
|
||||||
|
|
||||||
|
const createResult = await checkAccess(req.user, PermissionTypes.AGENTS, [
|
||||||
|
Permissions.CREATE,
|
||||||
|
]);
|
||||||
|
expect(createResult).toBe(true);
|
||||||
|
|
||||||
|
const shareResult = await checkAccess(req.user, PermissionTypes.AGENTS, [
|
||||||
|
Permissions.SHARED_GLOBAL,
|
||||||
|
]);
|
||||||
|
expect(shareResult).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('generateCheckAccess', () => {
|
||||||
|
test('should call next() when user has required permission', async () => {
|
||||||
|
const middleware = generateCheckAccess(PermissionTypes.AGENTS, [Permissions.USE]);
|
||||||
|
await middleware(req, res, next);
|
||||||
|
|
||||||
|
expect(next).toHaveBeenCalled();
|
||||||
|
expect(res.status).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should return 403 when user lacks permission', async () => {
|
||||||
|
const middleware = generateCheckAccess(PermissionTypes.AGENTS, [Permissions.CREATE]);
|
||||||
|
await middleware(req, res, next);
|
||||||
|
|
||||||
|
expect(next).not.toHaveBeenCalled();
|
||||||
|
expect(res.status).toHaveBeenCalledWith(403);
|
||||||
|
expect(res.json).toHaveBeenCalledWith({ message: 'Forbidden: Insufficient permissions' });
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should check body properties when configured', async () => {
|
||||||
|
req.body = { agentId: 'agent123', description: 'test' };
|
||||||
|
|
||||||
|
const bodyProps = {
|
||||||
|
[Permissions.CREATE]: ['agentId'],
|
||||||
|
};
|
||||||
|
|
||||||
|
const middleware = generateCheckAccess(
|
||||||
|
PermissionTypes.AGENTS,
|
||||||
|
[Permissions.CREATE],
|
||||||
|
bodyProps,
|
||||||
|
);
|
||||||
|
await middleware(req, res, next);
|
||||||
|
|
||||||
|
expect(next).toHaveBeenCalled();
|
||||||
|
expect(res.status).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should handle database errors gracefully', async () => {
|
||||||
|
// Create a user with an invalid role that will cause getRoleByName to fail
|
||||||
|
req.user.role = { invalid: 'object' }; // This will cause an error when querying
|
||||||
|
|
||||||
|
const middleware = generateCheckAccess(PermissionTypes.AGENTS, [Permissions.USE]);
|
||||||
|
await middleware(req, res, next);
|
||||||
|
|
||||||
|
expect(next).not.toHaveBeenCalled();
|
||||||
|
expect(res.status).toHaveBeenCalledWith(500);
|
||||||
|
expect(res.json).toHaveBeenCalledWith({
|
||||||
|
message: expect.stringContaining('Server error:'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should work with multiple permission types', async () => {
|
||||||
|
req.user.role = 'admin';
|
||||||
|
|
||||||
|
const middleware = generateCheckAccess(PermissionTypes.AGENTS, [
|
||||||
|
Permissions.USE,
|
||||||
|
Permissions.CREATE,
|
||||||
|
Permissions.SHARED_GLOBAL,
|
||||||
|
]);
|
||||||
|
await middleware(req, res, next);
|
||||||
|
|
||||||
|
expect(next).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should handle missing user gracefully', async () => {
|
||||||
|
req.user = null;
|
||||||
|
|
||||||
|
const middleware = generateCheckAccess(PermissionTypes.AGENTS, [Permissions.USE]);
|
||||||
|
await middleware(req, res, next);
|
||||||
|
|
||||||
|
expect(next).not.toHaveBeenCalled();
|
||||||
|
expect(res.status).toHaveBeenCalledWith(500);
|
||||||
|
expect(res.json).toHaveBeenCalledWith({
|
||||||
|
message: expect.stringContaining('Server error:'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should handle role with no AGENTS permissions', async () => {
|
||||||
|
await Role.create({
|
||||||
|
name: 'noaccess',
|
||||||
|
permissions: {
|
||||||
|
// Explicitly set AGENTS with all permissions false
|
||||||
|
[PermissionTypes.AGENTS]: {
|
||||||
|
[Permissions.USE]: false,
|
||||||
|
[Permissions.CREATE]: false,
|
||||||
|
[Permissions.SHARED_GLOBAL]: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
req.user.role = 'noaccess';
|
||||||
|
|
||||||
|
const middleware = generateCheckAccess(PermissionTypes.AGENTS, [Permissions.USE]);
|
||||||
|
await middleware(req, res, next);
|
||||||
|
|
||||||
|
expect(next).not.toHaveBeenCalled();
|
||||||
|
expect(res.status).toHaveBeenCalledWith(403);
|
||||||
|
expect(res.json).toHaveBeenCalledWith({ message: 'Forbidden: Insufficient permissions' });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Add table
Add a link
Reference in a new issue