| const mongoose = require('mongoose'); |
| const { RoleBits, createModels } = require('@librechat/data-schemas'); |
| const { MongoMemoryServer } = require('mongodb-memory-server'); |
| const { |
| ResourceType, |
| AccessRoleIds, |
| PrincipalType, |
| PrincipalModel, |
| } = require('librechat-data-provider'); |
| const { |
| bulkUpdateResourcePermissions, |
| getEffectivePermissions, |
| findAccessibleResources, |
| getAvailableRoles, |
| grantPermission, |
| checkPermission, |
| } = require('./PermissionService'); |
| const { findRoleByIdentifier, getUserPrincipals, seedDefaultRoles } = require('~/models'); |
|
|
| |
| jest.mock('@librechat/data-schemas', () => ({ |
| ...jest.requireActual('@librechat/data-schemas'), |
| getTransactionSupport: jest.fn().mockResolvedValue(false), |
| createModels: jest.requireActual('@librechat/data-schemas').createModels, |
| })); |
|
|
| |
| jest.mock('~/server/services/GraphApiService', () => ({ |
| getGroupMembers: jest.fn().mockResolvedValue([]), |
| })); |
|
|
| |
| jest.mock('~/config', () => ({ |
| logger: { |
| error: jest.fn(), |
| }, |
| })); |
|
|
| let mongoServer; |
| let AclEntry; |
|
|
| beforeAll(async () => { |
| mongoServer = await MongoMemoryServer.create(); |
| const mongoUri = mongoServer.getUri(); |
| await mongoose.connect(mongoUri); |
|
|
| |
| createModels(mongoose); |
|
|
| |
| const dbModels = require('~/db/models'); |
| Object.assign(mongoose.models, dbModels); |
|
|
| AclEntry = dbModels.AclEntry; |
|
|
| |
| await seedDefaultRoles(); |
| }); |
|
|
| afterAll(async () => { |
| await mongoose.disconnect(); |
| await mongoServer.stop(); |
| }); |
|
|
| beforeEach(async () => { |
| |
| await AclEntry.deleteMany({}); |
| }); |
|
|
| |
| jest.mock('~/models', () => ({ |
| ...jest.requireActual('~/models'), |
| getUserPrincipals: jest.fn(), |
| })); |
|
|
| describe('PermissionService', () => { |
| |
| const userId = new mongoose.Types.ObjectId(); |
| const groupId = new mongoose.Types.ObjectId(); |
| const resourceId = new mongoose.Types.ObjectId(); |
| const grantedById = new mongoose.Types.ObjectId(); |
| const roleResourceId = new mongoose.Types.ObjectId(); |
|
|
| describe('grantPermission', () => { |
| test('should grant permission to a user with a role', async () => { |
| const entry = await grantPermission({ |
| principalType: PrincipalType.USER, |
| principalId: userId, |
| resourceType: ResourceType.AGENT, |
| resourceId, |
| accessRoleId: AccessRoleIds.AGENT_VIEWER, |
| grantedBy: grantedById, |
| }); |
|
|
| expect(entry).toBeDefined(); |
| expect(entry.principalType).toBe(PrincipalType.USER); |
| expect(entry.principalId.toString()).toBe(userId.toString()); |
| expect(entry.principalModel).toBe(PrincipalModel.USER); |
| expect(entry.resourceType).toBe(ResourceType.AGENT); |
| expect(entry.resourceId.toString()).toBe(resourceId.toString()); |
|
|
| |
| const role = await findRoleByIdentifier(AccessRoleIds.AGENT_VIEWER); |
| expect(entry.permBits).toBe(role.permBits); |
| expect(entry.roleId.toString()).toBe(role._id.toString()); |
| expect(entry.grantedBy.toString()).toBe(grantedById.toString()); |
| expect(entry.grantedAt).toBeInstanceOf(Date); |
| }); |
|
|
| test('should grant permission to a group with a role', async () => { |
| const entry = await grantPermission({ |
| principalType: PrincipalType.GROUP, |
| principalId: groupId, |
| resourceType: ResourceType.AGENT, |
| resourceId, |
| accessRoleId: AccessRoleIds.AGENT_EDITOR, |
| grantedBy: grantedById, |
| }); |
|
|
| expect(entry).toBeDefined(); |
| expect(entry.principalType).toBe(PrincipalType.GROUP); |
| expect(entry.principalId.toString()).toBe(groupId.toString()); |
| expect(entry.principalModel).toBe(PrincipalModel.GROUP); |
|
|
| |
| const role = await findRoleByIdentifier(AccessRoleIds.AGENT_EDITOR); |
| expect(entry.permBits).toBe(role.permBits); |
| expect(entry.roleId.toString()).toBe(role._id.toString()); |
| }); |
|
|
| test('should grant public permission with a role', async () => { |
| const entry = await grantPermission({ |
| principalType: PrincipalType.PUBLIC, |
| principalId: null, |
| resourceType: ResourceType.AGENT, |
| resourceId, |
| accessRoleId: AccessRoleIds.AGENT_VIEWER, |
| grantedBy: grantedById, |
| }); |
|
|
| expect(entry).toBeDefined(); |
| expect(entry.principalType).toBe(PrincipalType.PUBLIC); |
| expect(entry.principalId).toBeUndefined(); |
| expect(entry.principalModel).toBeUndefined(); |
|
|
| |
| const role = await findRoleByIdentifier(AccessRoleIds.AGENT_VIEWER); |
| expect(entry.permBits).toBe(role.permBits); |
| expect(entry.roleId.toString()).toBe(role._id.toString()); |
| }); |
|
|
| test('should throw error for invalid principal type', async () => { |
| await expect( |
| grantPermission({ |
| principalType: 'invalid', |
| principalId: userId, |
| resourceType: ResourceType.AGENT, |
| resourceId, |
| accessRoleId: AccessRoleIds.AGENT_VIEWER, |
| grantedBy: grantedById, |
| }), |
| ).rejects.toThrow('Invalid principal type: invalid'); |
| }); |
|
|
| test('should throw error for missing principalId with user type', async () => { |
| await expect( |
| grantPermission({ |
| principalType: PrincipalType.USER, |
| principalId: null, |
| resourceType: ResourceType.AGENT, |
| resourceId, |
| accessRoleId: AccessRoleIds.AGENT_VIEWER, |
| grantedBy: grantedById, |
| }), |
| ).rejects.toThrow('Principal ID is required for user, group, and role principals'); |
| }); |
|
|
| test('should throw error for non-existent role', async () => { |
| await expect( |
| grantPermission({ |
| principalType: PrincipalType.USER, |
| principalId: userId, |
| resourceType: ResourceType.AGENT, |
| resourceId, |
| accessRoleId: 'non_existent_role', |
| grantedBy: grantedById, |
| }), |
| ).rejects.toThrow('Role non_existent_role not found'); |
| }); |
|
|
| test('should throw error for role-resource type mismatch', async () => { |
| await expect( |
| grantPermission({ |
| principalType: PrincipalType.USER, |
| principalId: userId, |
| resourceType: ResourceType.AGENT, |
| resourceId, |
| accessRoleId: AccessRoleIds.PROMPTGROUP_VIEWER, |
| grantedBy: grantedById, |
| }), |
| ).rejects.toThrow('Role promptGroup_viewer is for promptGroup resources, not agent'); |
| }); |
|
|
| test('should update existing permission when granting to same principal and resource', async () => { |
| |
| await grantPermission({ |
| principalType: PrincipalType.USER, |
| principalId: userId, |
| resourceType: ResourceType.AGENT, |
| resourceId, |
| accessRoleId: AccessRoleIds.AGENT_VIEWER, |
| grantedBy: grantedById, |
| }); |
|
|
| |
| const updated = await grantPermission({ |
| principalType: PrincipalType.USER, |
| principalId: userId, |
| resourceType: ResourceType.AGENT, |
| resourceId, |
| accessRoleId: AccessRoleIds.AGENT_EDITOR, |
| grantedBy: grantedById, |
| }); |
|
|
| const editorRole = await findRoleByIdentifier(AccessRoleIds.AGENT_EDITOR); |
| expect(updated.permBits).toBe(editorRole.permBits); |
| expect(updated.roleId.toString()).toBe(editorRole._id.toString()); |
|
|
| |
| const entries = await AclEntry.find({ |
| principalType: PrincipalType.USER, |
| principalId: userId, |
| resourceType: ResourceType.AGENT, |
| resourceId, |
| }); |
| expect(entries).toHaveLength(1); |
| }); |
| }); |
|
|
| describe('checkPermission', () => { |
| let otherResourceId; |
|
|
| beforeEach(async () => { |
| |
| getUserPrincipals.mockReset(); |
|
|
| |
| await grantPermission({ |
| principalType: PrincipalType.USER, |
| principalId: userId, |
| resourceType: ResourceType.AGENT, |
| resourceId, |
| accessRoleId: AccessRoleIds.AGENT_VIEWER, |
| grantedBy: grantedById, |
| }); |
|
|
| otherResourceId = new mongoose.Types.ObjectId(); |
| await grantPermission({ |
| principalType: PrincipalType.GROUP, |
| principalId: groupId, |
| resourceType: ResourceType.AGENT, |
| resourceId: otherResourceId, |
| accessRoleId: AccessRoleIds.AGENT_EDITOR, |
| grantedBy: grantedById, |
| }); |
| }); |
|
|
| test('should check permission for user principal', async () => { |
| |
| getUserPrincipals.mockResolvedValue([ |
| { principalType: PrincipalType.USER, principalId: userId }, |
| ]); |
|
|
| const hasViewPermission = await checkPermission({ |
| userId, |
| resourceType: ResourceType.AGENT, |
| resourceId, |
| requiredPermission: 1, |
| }); |
|
|
| expect(hasViewPermission).toBe(true); |
|
|
| |
| const hasEditPermission = await checkPermission({ |
| userId, |
| resourceType: ResourceType.AGENT, |
| resourceId, |
| requiredPermission: 3, |
| }); |
|
|
| expect(hasEditPermission).toBe(false); |
| }); |
|
|
| test('should check permission for user and group principals', async () => { |
| |
| getUserPrincipals.mockResolvedValue([ |
| { principalType: PrincipalType.USER, principalId: userId }, |
| { principalType: PrincipalType.GROUP, principalId: groupId }, |
| ]); |
|
|
| |
| const hasViewOnOriginal = await checkPermission({ |
| userId, |
| resourceType: ResourceType.AGENT, |
| resourceId, |
| requiredPermission: 1, |
| }); |
|
|
| expect(hasViewOnOriginal).toBe(true); |
|
|
| |
| const hasViewOnOther = await checkPermission({ |
| userId, |
| resourceType: ResourceType.AGENT, |
| resourceId: otherResourceId, |
| requiredPermission: 1, |
| }); |
|
|
| |
| expect(hasViewOnOther).toBe(true); |
| }); |
|
|
| test('should check permission for public access', async () => { |
| const publicResourceId = new mongoose.Types.ObjectId(); |
|
|
| |
| await grantPermission({ |
| principalType: PrincipalType.PUBLIC, |
| principalId: null, |
| resourceType: ResourceType.AGENT, |
| resourceId: publicResourceId, |
| accessRoleId: AccessRoleIds.AGENT_VIEWER, |
| grantedBy: grantedById, |
| }); |
|
|
| |
| getUserPrincipals.mockResolvedValue([ |
| { principalType: PrincipalType.USER, principalId: userId }, |
| { principalType: PrincipalType.GROUP, principalId: groupId }, |
| { principalType: PrincipalType.PUBLIC }, |
| ]); |
|
|
| const hasPublicAccess = await checkPermission({ |
| userId, |
| resourceType: ResourceType.AGENT, |
| resourceId: publicResourceId, |
| requiredPermission: 1, |
| }); |
|
|
| expect(hasPublicAccess).toBe(true); |
| }); |
|
|
| test('should return false for invalid permission bits', async () => { |
| getUserPrincipals.mockResolvedValue([ |
| { principalType: PrincipalType.USER, principalId: userId }, |
| ]); |
|
|
| await expect( |
| checkPermission({ |
| userId, |
| resourceType: ResourceType.AGENT, |
| resourceId, |
| requiredPermission: 'invalid', |
| }), |
| ).rejects.toThrow('requiredPermission must be a positive number'); |
|
|
| const nonExistentResource = await checkPermission({ |
| userId, |
| resourceType: ResourceType.AGENT, |
| resourceId: new mongoose.Types.ObjectId(), |
| requiredPermission: 1, |
| }); |
|
|
| expect(nonExistentResource).toBe(false); |
| }); |
|
|
| test('should return false if user has no principals', async () => { |
| getUserPrincipals.mockResolvedValue([]); |
|
|
| const hasPermission = await checkPermission({ |
| userId, |
| resourceType: ResourceType.AGENT, |
| resourceId, |
| requiredPermission: 1, |
| }); |
|
|
| expect(hasPermission).toBe(false); |
| }); |
| }); |
|
|
| describe('getEffectivePermissions', () => { |
| beforeEach(async () => { |
| |
| getUserPrincipals.mockReset(); |
|
|
| |
| await grantPermission({ |
| principalType: PrincipalType.USER, |
| principalId: userId, |
| resourceType: ResourceType.AGENT, |
| resourceId, |
| accessRoleId: AccessRoleIds.AGENT_VIEWER, |
| grantedBy: grantedById, |
| }); |
|
|
| await grantPermission({ |
| principalType: PrincipalType.GROUP, |
| principalId: groupId, |
| resourceType: ResourceType.AGENT, |
| resourceId, |
| accessRoleId: AccessRoleIds.AGENT_EDITOR, |
| grantedBy: grantedById, |
| }); |
|
|
| |
| const publicResourceId = new mongoose.Types.ObjectId(); |
| await grantPermission({ |
| principalType: PrincipalType.PUBLIC, |
| principalId: null, |
| resourceType: ResourceType.AGENT, |
| resourceId: publicResourceId, |
| accessRoleId: AccessRoleIds.AGENT_VIEWER, |
| grantedBy: grantedById, |
| }); |
|
|
| |
| const parentResourceId = new mongoose.Types.ObjectId(); |
| const childResourceId = new mongoose.Types.ObjectId(); |
|
|
| await grantPermission({ |
| principalType: PrincipalType.USER, |
| principalId: userId, |
| resourceType: ResourceType.PROMPTGROUP, |
| resourceId: parentResourceId, |
| accessRoleId: AccessRoleIds.PROMPTGROUP_VIEWER, |
| grantedBy: grantedById, |
| }); |
|
|
| await AclEntry.create({ |
| principalType: PrincipalType.USER, |
| principalId: userId, |
| principalModel: PrincipalModel.USER, |
| resourceType: ResourceType.AGENT, |
| resourceId: childResourceId, |
| permBits: RoleBits.VIEWER, |
| roleId: (await findRoleByIdentifier(AccessRoleIds.AGENT_VIEWER))._id, |
| grantedBy: grantedById, |
| grantedAt: new Date(), |
| inheritedFrom: parentResourceId, |
| }); |
| }); |
|
|
| test('should get effective permissions from multiple sources', async () => { |
| |
| getUserPrincipals.mockResolvedValue([ |
| { principalType: PrincipalType.USER, principalId: userId }, |
| { principalType: PrincipalType.GROUP, principalId: groupId }, |
| ]); |
|
|
| const effective = await getEffectivePermissions({ |
| userId, |
| resourceType: ResourceType.AGENT, |
| resourceId, |
| }); |
|
|
| |
| |
| expect(effective).toBe(RoleBits.EDITOR); |
| }); |
|
|
| test('should get effective permissions from inherited permissions', async () => { |
| |
| const inheritedEntry = await AclEntry.findOne({ inheritedFrom: { $exists: true } }); |
| const childResourceId = inheritedEntry.resourceId; |
|
|
| |
| getUserPrincipals.mockResolvedValue([ |
| { principalType: PrincipalType.USER, principalId: userId }, |
| ]); |
|
|
| const effective = await getEffectivePermissions({ |
| userId, |
| resourceType: ResourceType.AGENT, |
| resourceId: childResourceId, |
| }); |
|
|
| |
| expect(effective).toBe(RoleBits.VIEWER); |
| }); |
|
|
| test('should return 0 for non-existent permissions', async () => { |
| getUserPrincipals.mockResolvedValue([{ principalType: 'user', principalId: userId }]); |
|
|
| const nonExistentResource = new mongoose.Types.ObjectId(); |
| const effective = await getEffectivePermissions({ |
| userId, |
| resourceType: ResourceType.AGENT, |
| resourceId: nonExistentResource, |
| }); |
|
|
| |
| expect(effective).toBe(0); |
| }); |
|
|
| test('should return 0 if user has no principals', async () => { |
| getUserPrincipals.mockResolvedValue([]); |
|
|
| const effective = await getEffectivePermissions({ |
| userId, |
| resourceType: ResourceType.AGENT, |
| resourceId, |
| }); |
|
|
| |
| expect(effective).toBe(0); |
| }); |
| }); |
|
|
| describe('findAccessibleResources', () => { |
| beforeEach(async () => { |
| |
| getUserPrincipals.mockReset(); |
|
|
| |
| const resource1 = new mongoose.Types.ObjectId(); |
| const resource2 = new mongoose.Types.ObjectId(); |
| const resource3 = new mongoose.Types.ObjectId(); |
|
|
| |
| await grantPermission({ |
| principalType: PrincipalType.USER, |
| principalId: userId, |
| resourceType: ResourceType.AGENT, |
| resourceId: resource1, |
| accessRoleId: AccessRoleIds.AGENT_VIEWER, |
| grantedBy: grantedById, |
| }); |
|
|
| |
| await grantPermission({ |
| principalType: PrincipalType.USER, |
| principalId: userId, |
| resourceType: ResourceType.AGENT, |
| resourceId: resource2, |
| accessRoleId: AccessRoleIds.AGENT_EDITOR, |
| grantedBy: grantedById, |
| }); |
|
|
| |
| await grantPermission({ |
| principalType: PrincipalType.GROUP, |
| principalId: groupId, |
| resourceType: ResourceType.AGENT, |
| resourceId: resource3, |
| accessRoleId: AccessRoleIds.AGENT_VIEWER, |
| grantedBy: grantedById, |
| }); |
| }); |
|
|
| test('should find resources user can view', async () => { |
| |
| getUserPrincipals.mockResolvedValue([ |
| { principalType: PrincipalType.USER, principalId: userId }, |
| ]); |
|
|
| const viewableResources = await findAccessibleResources({ |
| userId, |
| resourceType: ResourceType.AGENT, |
| requiredPermissions: 1, |
| }); |
|
|
| |
| expect(viewableResources).toHaveLength(2); |
| }); |
|
|
| test('should find resources user can edit', async () => { |
| |
| getUserPrincipals.mockResolvedValue([ |
| { principalType: PrincipalType.USER, principalId: userId }, |
| ]); |
|
|
| const editableResources = await findAccessibleResources({ |
| userId, |
| resourceType: ResourceType.AGENT, |
| requiredPermissions: 3, |
| }); |
|
|
| |
| expect(editableResources).toHaveLength(1); |
| }); |
|
|
| test('should find resources accessible via group membership', async () => { |
| |
| getUserPrincipals.mockResolvedValue([ |
| { principalType: PrincipalType.USER, principalId: userId }, |
| { principalType: PrincipalType.GROUP, principalId: groupId }, |
| ]); |
|
|
| const viewableResources = await findAccessibleResources({ |
| userId, |
| resourceType: ResourceType.AGENT, |
| requiredPermissions: 1, |
| }); |
|
|
| |
| expect(viewableResources).toHaveLength(3); |
| }); |
|
|
| test('should return empty array for invalid permissions', async () => { |
| getUserPrincipals.mockResolvedValue([ |
| { principalType: PrincipalType.USER, principalId: userId }, |
| ]); |
|
|
| await expect( |
| findAccessibleResources({ |
| userId, |
| resourceType: ResourceType.AGENT, |
| requiredPermissions: 'invalid', |
| }), |
| ).rejects.toThrow('requiredPermissions must be a positive number'); |
|
|
| const nonExistentType = await findAccessibleResources({ |
| userId, |
| resourceType: 'non_existent_type', |
| requiredPermissions: 1, |
| }); |
|
|
| expect(nonExistentType).toEqual([]); |
| }); |
|
|
| test('should return empty array if user has no principals', async () => { |
| getUserPrincipals.mockResolvedValue([]); |
|
|
| const resources = await findAccessibleResources({ |
| userId, |
| resourceType: ResourceType.AGENT, |
| requiredPermissions: 1, |
| }); |
|
|
| expect(resources).toEqual([]); |
| }); |
| }); |
|
|
| describe('getAvailableRoles', () => { |
| test('should get all roles for a resource type', async () => { |
| const roles = await getAvailableRoles({ |
| resourceType: ResourceType.AGENT, |
| }); |
|
|
| expect(roles).toHaveLength(3); |
| expect(roles.map((r) => r.accessRoleId).sort()).toEqual( |
| [AccessRoleIds.AGENT_EDITOR, AccessRoleIds.AGENT_OWNER, AccessRoleIds.AGENT_VIEWER].sort(), |
| ); |
| }); |
|
|
| test('should throw error for non-existent resource type', async () => { |
| await expect( |
| getAvailableRoles({ |
| resourceType: 'non_existent_type', |
| }), |
| ).rejects.toThrow('Invalid resourceType: non_existent_type. Valid types: agent, promptGroup'); |
| }); |
| }); |
|
|
| describe('bulkUpdateResourcePermissions', () => { |
| const otherUserId = new mongoose.Types.ObjectId(); |
|
|
| beforeEach(async () => { |
| |
| await seedDefaultRoles(); |
| |
| await grantPermission({ |
| principalType: PrincipalType.USER, |
| principalId: userId, |
| resourceType: ResourceType.AGENT, |
| resourceId, |
| accessRoleId: AccessRoleIds.AGENT_VIEWER, |
| grantedBy: grantedById, |
| }); |
|
|
| await grantPermission({ |
| principalType: PrincipalType.GROUP, |
| principalId: groupId, |
| resourceType: ResourceType.AGENT, |
| resourceId, |
| accessRoleId: AccessRoleIds.AGENT_EDITOR, |
| grantedBy: grantedById, |
| }); |
|
|
| await grantPermission({ |
| principalType: PrincipalType.PUBLIC, |
| principalId: null, |
| resourceType: ResourceType.AGENT, |
| resourceId, |
| accessRoleId: AccessRoleIds.AGENT_VIEWER, |
| grantedBy: grantedById, |
| }); |
| }); |
|
|
| test('should grant new permissions in bulk', async () => { |
| const newResourceId = new mongoose.Types.ObjectId(); |
| const updatedPrincipals = [ |
| { |
| type: PrincipalType.USER, |
| id: userId, |
| accessRoleId: AccessRoleIds.AGENT_VIEWER, |
| }, |
| { |
| type: PrincipalType.USER, |
| id: otherUserId, |
| accessRoleId: AccessRoleIds.AGENT_EDITOR, |
| }, |
| { |
| type: PrincipalType.GROUP, |
| id: groupId, |
| accessRoleId: AccessRoleIds.AGENT_OWNER, |
| }, |
| ]; |
|
|
| const results = await bulkUpdateResourcePermissions({ |
| resourceType: ResourceType.AGENT, |
| resourceId: newResourceId, |
| updatedPrincipals, |
| grantedBy: grantedById, |
| }); |
|
|
| expect(results.granted).toHaveLength(3); |
| expect(results.updated).toHaveLength(0); |
| expect(results.revoked).toHaveLength(0); |
| expect(results.errors).toHaveLength(0); |
|
|
| |
| const aclEntries = await AclEntry.find({ |
| resourceType: ResourceType.AGENT, |
| resourceId: newResourceId, |
| }); |
| expect(aclEntries).toHaveLength(3); |
| }); |
|
|
| test('should update existing permissions in bulk', async () => { |
| const updatedPrincipals = [ |
| { |
| type: PrincipalType.USER, |
| id: userId, |
| accessRoleId: AccessRoleIds.AGENT_EDITOR, |
| }, |
| { |
| type: PrincipalType.GROUP, |
| id: groupId, |
| accessRoleId: AccessRoleIds.AGENT_OWNER, |
| }, |
| { |
| type: PrincipalType.PUBLIC, |
| accessRoleId: AccessRoleIds.AGENT_VIEWER, |
| }, |
| ]; |
|
|
| const results = await bulkUpdateResourcePermissions({ |
| resourceType: ResourceType.AGENT, |
| resourceId, |
| updatedPrincipals, |
| grantedBy: grantedById, |
| }); |
|
|
| |
| expect(results.granted).toHaveLength(3); |
| expect(results.updated).toHaveLength(0); |
| expect(results.revoked).toHaveLength(0); |
| expect(results.errors).toHaveLength(0); |
|
|
| |
| const userEntry = await AclEntry.findOne({ |
| principalType: PrincipalType.USER, |
| principalId: userId, |
| resourceType: ResourceType.AGENT, |
| resourceId, |
| }).populate('roleId', 'accessRoleId'); |
| expect(userEntry.roleId.accessRoleId).toBe(AccessRoleIds.AGENT_EDITOR); |
|
|
| const groupEntry = await AclEntry.findOne({ |
| principalType: PrincipalType.GROUP, |
| principalId: groupId, |
| resourceType: ResourceType.AGENT, |
| resourceId, |
| }).populate('roleId', 'accessRoleId'); |
| expect(groupEntry.roleId.accessRoleId).toBe(AccessRoleIds.AGENT_OWNER); |
| }); |
|
|
| test('should revoke specified permissions', async () => { |
| const revokedPrincipals = [ |
| { |
| type: PrincipalType.GROUP, |
| id: groupId, |
| }, |
| { |
| type: PrincipalType.PUBLIC, |
| }, |
| ]; |
|
|
| const results = await bulkUpdateResourcePermissions({ |
| resourceType: ResourceType.AGENT, |
| resourceId, |
| revokedPrincipals, |
| grantedBy: grantedById, |
| }); |
|
|
| expect(results.granted).toHaveLength(0); |
| expect(results.updated).toHaveLength(0); |
| expect(results.revoked).toHaveLength(2); |
| expect(results.errors).toHaveLength(0); |
|
|
| |
| const remainingEntries = await AclEntry.find({ |
| resourceType: ResourceType.AGENT, |
| resourceId, |
| }); |
| expect(remainingEntries).toHaveLength(1); |
| expect(remainingEntries[0].principalType).toBe(PrincipalType.USER); |
| expect(remainingEntries[0].principalId.toString()).toBe(userId.toString()); |
| }); |
|
|
| test('should handle mixed operations (grant, update, revoke)', async () => { |
| const updatedPrincipals = [ |
| { |
| type: PrincipalType.USER, |
| id: userId, |
| accessRoleId: AccessRoleIds.AGENT_OWNER, |
| }, |
| { |
| type: PrincipalType.USER, |
| id: otherUserId, |
| accessRoleId: AccessRoleIds.AGENT_VIEWER, |
| }, |
| ]; |
|
|
| const revokedPrincipals = [ |
| { |
| type: PrincipalType.GROUP, |
| id: groupId, |
| }, |
| { |
| type: PrincipalType.PUBLIC, |
| }, |
| ]; |
|
|
| const results = await bulkUpdateResourcePermissions({ |
| resourceType: ResourceType.AGENT, |
| resourceId, |
| updatedPrincipals, |
| revokedPrincipals, |
| grantedBy: grantedById, |
| }); |
|
|
| expect(results.granted).toHaveLength(2); |
| expect(results.updated).toHaveLength(0); |
| expect(results.revoked).toHaveLength(2); |
| expect(results.errors).toHaveLength(0); |
|
|
| |
| const finalEntries = await AclEntry.find({ |
| resourceType: ResourceType.AGENT, |
| resourceId, |
| }).populate('roleId', 'accessRoleId'); |
|
|
| expect(finalEntries).toHaveLength(2); |
|
|
| const userEntry = finalEntries.find((e) => e.principalId.toString() === userId.toString()); |
| expect(userEntry.roleId.accessRoleId).toBe(AccessRoleIds.AGENT_OWNER); |
|
|
| const otherUserEntry = finalEntries.find( |
| (e) => e.principalId.toString() === otherUserId.toString(), |
| ); |
| expect(otherUserEntry.roleId.accessRoleId).toBe(AccessRoleIds.AGENT_VIEWER); |
| }); |
|
|
| test('should handle errors for invalid roles gracefully', async () => { |
| const updatedPrincipals = [ |
| { |
| type: PrincipalType.USER, |
| id: userId, |
| accessRoleId: AccessRoleIds.AGENT_VIEWER, |
| }, |
| { |
| type: PrincipalType.USER, |
| id: otherUserId, |
| accessRoleId: 'non_existent_role', |
| }, |
| { |
| type: PrincipalType.GROUP, |
| id: groupId, |
| accessRoleId: AccessRoleIds.PROMPTGROUP_VIEWER, |
| }, |
| ]; |
|
|
| const results = await bulkUpdateResourcePermissions({ |
| resourceType: ResourceType.AGENT, |
| resourceId, |
| updatedPrincipals, |
| grantedBy: grantedById, |
| }); |
|
|
| expect(results.granted).toHaveLength(1); |
| expect(results.updated).toHaveLength(0); |
| expect(results.revoked).toHaveLength(0); |
| expect(results.errors).toHaveLength(2); |
|
|
| |
| expect(results.errors[0].error).toContain('Role non_existent_role not found'); |
| expect(results.errors[1].error).toContain('Role promptGroup_viewer not found'); |
| }); |
|
|
| test('should handle empty arrays (no operations)', async () => { |
| const results = await bulkUpdateResourcePermissions({ |
| resourceType: ResourceType.AGENT, |
| resourceId, |
| updatedPrincipals: [], |
| revokedPrincipals: [], |
| grantedBy: grantedById, |
| }); |
|
|
| expect(results.granted).toHaveLength(0); |
| expect(results.updated).toHaveLength(0); |
| expect(results.revoked).toHaveLength(0); |
| expect(results.errors).toHaveLength(0); |
|
|
| |
| const remainingEntries = await AclEntry.find({ |
| resourceType: ResourceType.AGENT, |
| resourceId, |
| }); |
| expect(remainingEntries).toHaveLength(3); |
| }); |
|
|
| test('should throw error for invalid updatedPrincipals array', async () => { |
| await expect( |
| bulkUpdateResourcePermissions({ |
| resourceType: ResourceType.AGENT, |
| resourceId, |
| updatedPrincipals: 'not an array', |
| grantedBy: grantedById, |
| }), |
| ).rejects.toThrow('updatedPrincipals must be an array'); |
| }); |
|
|
| test('should throw error for invalid resource ID', async () => { |
| await expect( |
| bulkUpdateResourcePermissions({ |
| resourceType: ResourceType.AGENT, |
| resourceId: 'invalid-id', |
| permissions: [], |
| grantedBy: grantedById, |
| }), |
| ).rejects.toThrow('Invalid resource ID: invalid-id'); |
| }); |
|
|
| test('should handle public permissions correctly', async () => { |
| const updatedPrincipals = [ |
| { |
| type: PrincipalType.PUBLIC, |
| accessRoleId: AccessRoleIds.AGENT_EDITOR, |
| }, |
| { |
| type: PrincipalType.USER, |
| id: otherUserId, |
| accessRoleId: AccessRoleIds.AGENT_VIEWER, |
| }, |
| ]; |
|
|
| const revokedPrincipals = [ |
| { |
| type: PrincipalType.USER, |
| id: userId, |
| }, |
| { |
| type: PrincipalType.GROUP, |
| id: groupId, |
| }, |
| ]; |
|
|
| const results = await bulkUpdateResourcePermissions({ |
| resourceType: ResourceType.AGENT, |
| resourceId, |
| updatedPrincipals, |
| revokedPrincipals, |
| grantedBy: grantedById, |
| }); |
|
|
| expect(results.granted).toHaveLength(2); |
| expect(results.updated).toHaveLength(0); |
| expect(results.revoked).toHaveLength(2); |
| expect(results.errors).toHaveLength(0); |
|
|
| |
| const publicEntry = await AclEntry.findOne({ |
| principalType: PrincipalType.PUBLIC, |
| resourceType: ResourceType.AGENT, |
| resourceId, |
| }).populate('roleId', 'accessRoleId'); |
|
|
| expect(publicEntry).toBeDefined(); |
| expect(publicEntry.roleId.accessRoleId).toBe(AccessRoleIds.AGENT_EDITOR); |
| }); |
|
|
| test('should grant permission to a role', async () => { |
| const entry = await grantPermission({ |
| principalType: PrincipalType.ROLE, |
| principalId: 'admin', |
| resourceType: ResourceType.AGENT, |
| resourceId: roleResourceId, |
| accessRoleId: AccessRoleIds.AGENT_EDITOR, |
| grantedBy: grantedById, |
| }); |
|
|
| expect(entry).toBeDefined(); |
| expect(entry.principalType).toBe(PrincipalType.ROLE); |
| expect(entry.principalId).toBe('admin'); |
| expect(entry.principalModel).toBe(PrincipalModel.ROLE); |
| expect(entry.resourceType).toBe(ResourceType.AGENT); |
| expect(entry.resourceId.toString()).toBe(roleResourceId.toString()); |
|
|
| |
| const role = await findRoleByIdentifier(AccessRoleIds.AGENT_EDITOR); |
| expect(entry.permBits).toBe(role.permBits); |
| expect(entry.roleId.toString()).toBe(role._id.toString()); |
| }); |
|
|
| test('should check permissions for user with role', async () => { |
| |
| await grantPermission({ |
| principalType: PrincipalType.ROLE, |
| principalId: 'admin', |
| resourceType: ResourceType.AGENT, |
| resourceId: roleResourceId, |
| accessRoleId: AccessRoleIds.AGENT_EDITOR, |
| grantedBy: grantedById, |
| }); |
|
|
| |
| getUserPrincipals.mockResolvedValue([ |
| { principalType: PrincipalType.USER, principalId: userId }, |
| { principalType: PrincipalType.ROLE, principalId: 'admin' }, |
| { principalType: PrincipalType.PUBLIC }, |
| ]); |
|
|
| const hasPermission = await checkPermission({ |
| userId, |
| resourceType: ResourceType.AGENT, |
| resourceId: roleResourceId, |
| requiredPermission: 1, |
| }); |
|
|
| expect(hasPermission).toBe(true); |
|
|
| |
| getUserPrincipals.mockResolvedValue([ |
| { principalType: PrincipalType.USER, principalId: userId }, |
| { principalType: PrincipalType.PUBLIC }, |
| ]); |
|
|
| const hasNoPermission = await checkPermission({ |
| userId, |
| resourceType: ResourceType.AGENT, |
| resourceId: roleResourceId, |
| requiredPermission: 1, |
| }); |
|
|
| expect(hasNoPermission).toBe(false); |
| }); |
|
|
| test('should optimize permission checks when role is provided', async () => { |
| const testUserId = new mongoose.Types.ObjectId(); |
| const testResourceId = new mongoose.Types.ObjectId(); |
|
|
| |
| const User = mongoose.models.User; |
| await User.create({ |
| _id: testUserId, |
| email: 'editor@test.com', |
| emailVerified: true, |
| provider: 'local', |
| role: 'EDITOR', |
| }); |
|
|
| |
| await grantPermission({ |
| principalType: PrincipalType.ROLE, |
| principalId: 'EDITOR', |
| resourceType: ResourceType.AGENT, |
| resourceId: testResourceId, |
| accessRoleId: AccessRoleIds.AGENT_EDITOR, |
| grantedBy: grantedById, |
| }); |
|
|
| |
| getUserPrincipals.mockResolvedValue([ |
| { principalType: PrincipalType.USER, principalId: testUserId }, |
| { principalType: PrincipalType.ROLE, principalId: 'EDITOR' }, |
| { principalType: PrincipalType.PUBLIC }, |
| ]); |
|
|
| |
| const hasPermissionWithRole = await checkPermission({ |
| userId: testUserId, |
| role: 'EDITOR', |
| resourceType: ResourceType.AGENT, |
| resourceId: testResourceId, |
| requiredPermission: 1, |
| }); |
|
|
| expect(hasPermissionWithRole).toBe(true); |
| expect(getUserPrincipals).toHaveBeenCalledWith({ userId: testUserId, role: 'EDITOR' }); |
|
|
| |
| getUserPrincipals.mockResolvedValue([ |
| { principalType: PrincipalType.USER, principalId: testUserId }, |
| { principalType: PrincipalType.ROLE, principalId: 'EDITOR' }, |
| { principalType: PrincipalType.PUBLIC }, |
| ]); |
|
|
| const hasPermissionWithoutRole = await checkPermission({ |
| userId: testUserId, |
| resourceType: ResourceType.AGENT, |
| resourceId: testResourceId, |
| requiredPermission: 1, |
| }); |
|
|
| expect(hasPermissionWithoutRole).toBe(true); |
| expect(getUserPrincipals).toHaveBeenCalledWith({ userId: testUserId, role: undefined }); |
|
|
| |
| getUserPrincipals.mockClear(); |
|
|
| const effectiveWithRole = await getEffectivePermissions({ |
| userId: testUserId, |
| role: 'EDITOR', |
| resourceType: ResourceType.AGENT, |
| resourceId: testResourceId, |
| }); |
|
|
| expect(effectiveWithRole).toBe(3); |
| expect(getUserPrincipals).toHaveBeenCalledWith({ userId: testUserId, role: 'EDITOR' }); |
|
|
| |
| getUserPrincipals.mockClear(); |
|
|
| const accessibleWithRole = await findAccessibleResources({ |
| userId: testUserId, |
| role: 'EDITOR', |
| resourceType: ResourceType.AGENT, |
| requiredPermissions: 1, |
| }); |
|
|
| expect(accessibleWithRole.map((id) => id.toString())).toContain(testResourceId.toString()); |
| expect(getUserPrincipals).toHaveBeenCalledWith({ userId: testUserId, role: 'EDITOR' }); |
| }); |
|
|
| test('should handle role changes dynamically', async () => { |
| const testUserId = new mongoose.Types.ObjectId(); |
| const testResourceId = new mongoose.Types.ObjectId(); |
|
|
| |
| await grantPermission({ |
| principalType: PrincipalType.ROLE, |
| principalId: 'ADMIN', |
| resourceType: ResourceType.AGENT, |
| resourceId: testResourceId, |
| accessRoleId: AccessRoleIds.AGENT_OWNER, |
| grantedBy: grantedById, |
| }); |
|
|
| |
| getUserPrincipals.mockResolvedValue([ |
| { principalType: PrincipalType.USER, principalId: testUserId }, |
| { principalType: PrincipalType.ROLE, principalId: 'ADMIN' }, |
| { principalType: PrincipalType.PUBLIC }, |
| ]); |
|
|
| const hasAdminAccess = await checkPermission({ |
| userId: testUserId, |
| role: 'ADMIN', |
| resourceType: ResourceType.AGENT, |
| resourceId: testResourceId, |
| requiredPermission: 7, |
| }); |
|
|
| expect(hasAdminAccess).toBe(true); |
| expect(getUserPrincipals).toHaveBeenCalledWith({ userId: testUserId, role: 'ADMIN' }); |
|
|
| |
| getUserPrincipals.mockClear(); |
| getUserPrincipals.mockResolvedValue([ |
| { principalType: PrincipalType.USER, principalId: testUserId }, |
| { principalType: PrincipalType.ROLE, principalId: 'USER' }, |
| { principalType: PrincipalType.PUBLIC }, |
| ]); |
|
|
| const hasUserAccess = await checkPermission({ |
| userId: testUserId, |
| role: 'USER', |
| resourceType: ResourceType.AGENT, |
| resourceId: testResourceId, |
| requiredPermission: 1, |
| }); |
|
|
| expect(hasUserAccess).toBe(false); |
| expect(getUserPrincipals).toHaveBeenCalledWith({ userId: testUserId, role: 'USER' }); |
|
|
| |
| getUserPrincipals.mockClear(); |
| getUserPrincipals.mockResolvedValue([ |
| { principalType: PrincipalType.USER, principalId: testUserId }, |
| { principalType: PrincipalType.ROLE, principalId: 'EDITOR' }, |
| { principalType: PrincipalType.PUBLIC }, |
| ]); |
|
|
| const hasEditorAccess = await checkPermission({ |
| userId: testUserId, |
| role: 'EDITOR', |
| resourceType: ResourceType.AGENT, |
| resourceId: testResourceId, |
| requiredPermission: 1, |
| }); |
|
|
| expect(hasEditorAccess).toBe(false); |
| expect(getUserPrincipals).toHaveBeenCalledWith({ userId: testUserId, role: 'EDITOR' }); |
| }); |
|
|
| test('should work with different resource types', async () => { |
| |
| const promptGroupResourceId = new mongoose.Types.ObjectId(); |
| const updatedPrincipals = [ |
| { |
| type: PrincipalType.USER, |
| id: userId, |
| accessRoleId: AccessRoleIds.PROMPTGROUP_VIEWER, |
| }, |
| { |
| type: PrincipalType.GROUP, |
| id: groupId, |
| accessRoleId: AccessRoleIds.PROMPTGROUP_EDITOR, |
| }, |
| ]; |
|
|
| const results = await bulkUpdateResourcePermissions({ |
| resourceType: ResourceType.PROMPTGROUP, |
| resourceId: promptGroupResourceId, |
| updatedPrincipals, |
| grantedBy: grantedById, |
| }); |
|
|
| expect(results.granted).toHaveLength(2); |
| expect(results.updated).toHaveLength(0); |
| expect(results.revoked).toHaveLength(0); |
| expect(results.errors).toHaveLength(0); |
|
|
| |
| const promptGroupEntries = await AclEntry.find({ |
| resourceType: ResourceType.PROMPTGROUP, |
| resourceId: promptGroupResourceId, |
| }); |
| expect(promptGroupEntries).toHaveLength(2); |
| expect(promptGroupEntries.every((e) => e.resourceType === ResourceType.PROMPTGROUP)).toBe( |
| true, |
| ); |
| }); |
| }); |
|
|
| describe('String vs ObjectId Edge Cases', () => { |
| const stringUserId = new mongoose.Types.ObjectId().toString(); |
| const objectIdUserId = new mongoose.Types.ObjectId(); |
| const stringGroupId = new mongoose.Types.ObjectId().toString(); |
| const objectIdGroupId = new mongoose.Types.ObjectId(); |
| const testResourceId = new mongoose.Types.ObjectId(); |
|
|
| beforeEach(async () => { |
| |
| await AclEntry.deleteMany({}); |
| getUserPrincipals.mockReset(); |
| }); |
|
|
| test('should handle string userId in grantPermission', async () => { |
| const entry = await grantPermission({ |
| principalType: PrincipalType.USER, |
| principalId: stringUserId, |
| resourceType: ResourceType.AGENT, |
| resourceId: testResourceId, |
| accessRoleId: AccessRoleIds.AGENT_VIEWER, |
| grantedBy: grantedById, |
| }); |
|
|
| expect(entry).toBeDefined(); |
| expect(entry.principalType).toBe(PrincipalType.USER); |
| |
| expect(entry.principalId).toBeInstanceOf(mongoose.Types.ObjectId); |
| expect(entry.principalId.toString()).toBe(stringUserId); |
| }); |
|
|
| test('should handle string groupId in grantPermission', async () => { |
| const entry = await grantPermission({ |
| principalType: PrincipalType.GROUP, |
| principalId: stringGroupId, |
| resourceType: ResourceType.AGENT, |
| resourceId: testResourceId, |
| accessRoleId: AccessRoleIds.AGENT_EDITOR, |
| grantedBy: grantedById, |
| }); |
|
|
| expect(entry).toBeDefined(); |
| expect(entry.principalType).toBe(PrincipalType.GROUP); |
| |
| expect(entry.principalId).toBeInstanceOf(mongoose.Types.ObjectId); |
| expect(entry.principalId.toString()).toBe(stringGroupId); |
| }); |
|
|
| test('should handle string roleId in grantPermission for ROLE type', async () => { |
| const roleString = 'moderator'; |
|
|
| const entry = await grantPermission({ |
| principalType: PrincipalType.ROLE, |
| principalId: roleString, |
| resourceType: ResourceType.AGENT, |
| resourceId: testResourceId, |
| accessRoleId: AccessRoleIds.AGENT_VIEWER, |
| grantedBy: grantedById, |
| }); |
|
|
| expect(entry).toBeDefined(); |
| expect(entry.principalType).toBe(PrincipalType.ROLE); |
| |
| expect(typeof entry.principalId).toBe('string'); |
| expect(entry.principalId).toBe(roleString); |
| expect(entry.principalModel).toBe(PrincipalModel.ROLE); |
| }); |
|
|
| test('should check permissions correctly when permission granted with string userId', async () => { |
| |
| await grantPermission({ |
| principalType: PrincipalType.USER, |
| principalId: stringUserId, |
| resourceType: ResourceType.AGENT, |
| resourceId: testResourceId, |
| accessRoleId: AccessRoleIds.AGENT_EDITOR, |
| grantedBy: grantedById, |
| }); |
|
|
| |
| getUserPrincipals.mockResolvedValue([ |
| { |
| principalType: PrincipalType.USER, |
| principalId: new mongoose.Types.ObjectId(stringUserId), |
| }, |
| { principalType: PrincipalType.PUBLIC }, |
| ]); |
|
|
| |
| const hasPermission = await checkPermission({ |
| userId: stringUserId, |
| resourceType: ResourceType.AGENT, |
| resourceId: testResourceId, |
| requiredPermission: 1, |
| }); |
|
|
| expect(hasPermission).toBe(true); |
| expect(getUserPrincipals).toHaveBeenCalledWith({ userId: stringUserId, role: undefined }); |
| }); |
|
|
| test('should check permissions correctly when permission granted with ObjectId', async () => { |
| |
| await grantPermission({ |
| principalType: PrincipalType.USER, |
| principalId: objectIdUserId, |
| resourceType: ResourceType.AGENT, |
| resourceId: testResourceId, |
| accessRoleId: AccessRoleIds.AGENT_OWNER, |
| grantedBy: grantedById, |
| }); |
|
|
| |
| getUserPrincipals.mockResolvedValue([ |
| { principalType: PrincipalType.USER, principalId: objectIdUserId }, |
| { principalType: PrincipalType.PUBLIC }, |
| ]); |
|
|
| |
| const hasPermission = await checkPermission({ |
| userId: objectIdUserId, |
| resourceType: ResourceType.AGENT, |
| resourceId: testResourceId, |
| requiredPermission: 7, |
| }); |
|
|
| expect(hasPermission).toBe(true); |
| expect(getUserPrincipals).toHaveBeenCalledWith({ userId: objectIdUserId, role: undefined }); |
| }); |
|
|
| test('should handle bulkUpdateResourcePermissions with string IDs', async () => { |
| const updatedPrincipals = [ |
| { |
| type: PrincipalType.USER, |
| id: stringUserId, |
| accessRoleId: AccessRoleIds.AGENT_VIEWER, |
| }, |
| { |
| type: PrincipalType.GROUP, |
| id: stringGroupId, |
| accessRoleId: AccessRoleIds.AGENT_EDITOR, |
| }, |
| { |
| type: PrincipalType.ROLE, |
| id: 'admin', |
| accessRoleId: AccessRoleIds.AGENT_OWNER, |
| }, |
| ]; |
|
|
| const results = await bulkUpdateResourcePermissions({ |
| resourceType: ResourceType.AGENT, |
| resourceId: testResourceId, |
| updatedPrincipals, |
| grantedBy: grantedById, |
| }); |
|
|
| expect(results.granted).toHaveLength(3); |
| expect(results.errors).toHaveLength(0); |
|
|
| |
| const userEntry = await AclEntry.findOne({ |
| principalType: PrincipalType.USER, |
| resourceType: ResourceType.AGENT, |
| resourceId: testResourceId, |
| }); |
| expect(userEntry.principalId).toBeInstanceOf(mongoose.Types.ObjectId); |
| expect(userEntry.principalId.toString()).toBe(stringUserId); |
|
|
| |
| const groupEntry = await AclEntry.findOne({ |
| principalType: PrincipalType.GROUP, |
| resourceType: ResourceType.AGENT, |
| resourceId: testResourceId, |
| }); |
| expect(groupEntry.principalId).toBeInstanceOf(mongoose.Types.ObjectId); |
| expect(groupEntry.principalId.toString()).toBe(stringGroupId); |
|
|
| |
| const roleEntry = await AclEntry.findOne({ |
| principalType: PrincipalType.ROLE, |
| resourceType: ResourceType.AGENT, |
| resourceId: testResourceId, |
| }); |
| expect(typeof roleEntry.principalId).toBe('string'); |
| expect(roleEntry.principalId).toBe('admin'); |
| }); |
|
|
| test('should handle revoking permissions with string IDs in bulkUpdateResourcePermissions', async () => { |
| |
| await grantPermission({ |
| principalType: PrincipalType.USER, |
| principalId: objectIdUserId, |
| resourceType: ResourceType.AGENT, |
| resourceId: testResourceId, |
| accessRoleId: AccessRoleIds.AGENT_OWNER, |
| grantedBy: grantedById, |
| }); |
|
|
| await grantPermission({ |
| principalType: PrincipalType.GROUP, |
| principalId: objectIdGroupId, |
| resourceType: ResourceType.AGENT, |
| resourceId: testResourceId, |
| accessRoleId: AccessRoleIds.AGENT_EDITOR, |
| grantedBy: grantedById, |
| }); |
|
|
| |
| const revokedPrincipals = [ |
| { |
| type: PrincipalType.USER, |
| id: objectIdUserId.toString(), |
| }, |
| { |
| type: PrincipalType.GROUP, |
| id: objectIdGroupId.toString(), |
| }, |
| ]; |
|
|
| const results = await bulkUpdateResourcePermissions({ |
| resourceType: ResourceType.AGENT, |
| resourceId: testResourceId, |
| revokedPrincipals, |
| grantedBy: grantedById, |
| }); |
|
|
| expect(results.revoked).toHaveLength(2); |
| expect(results.errors).toHaveLength(0); |
|
|
| |
| const remainingEntries = await AclEntry.find({ |
| resourceType: ResourceType.AGENT, |
| resourceId: testResourceId, |
| }); |
| expect(remainingEntries).toHaveLength(0); |
| }); |
|
|
| test('should find accessible resources when permissions granted with mixed ID types', async () => { |
| const resource1 = new mongoose.Types.ObjectId(); |
| const resource2 = new mongoose.Types.ObjectId(); |
| const resource3 = new mongoose.Types.ObjectId(); |
|
|
| |
| await grantPermission({ |
| principalType: PrincipalType.USER, |
| principalId: stringUserId, |
| resourceType: ResourceType.AGENT, |
| resourceId: resource1, |
| accessRoleId: AccessRoleIds.AGENT_VIEWER, |
| grantedBy: grantedById, |
| }); |
|
|
| |
| await grantPermission({ |
| principalType: PrincipalType.USER, |
| principalId: new mongoose.Types.ObjectId(stringUserId), |
| resourceType: ResourceType.AGENT, |
| resourceId: resource2, |
| accessRoleId: AccessRoleIds.AGENT_EDITOR, |
| grantedBy: grantedById, |
| }); |
|
|
| |
| await grantPermission({ |
| principalType: PrincipalType.ROLE, |
| principalId: 'admin', |
| resourceType: ResourceType.AGENT, |
| resourceId: resource3, |
| accessRoleId: AccessRoleIds.AGENT_OWNER, |
| grantedBy: grantedById, |
| }); |
|
|
| |
| getUserPrincipals.mockResolvedValue([ |
| { |
| principalType: PrincipalType.USER, |
| principalId: new mongoose.Types.ObjectId(stringUserId), |
| }, |
| { principalType: PrincipalType.ROLE, principalId: 'admin' }, |
| { principalType: PrincipalType.PUBLIC }, |
| ]); |
|
|
| const accessibleResources = await findAccessibleResources({ |
| userId: stringUserId, |
| role: 'admin', |
| resourceType: ResourceType.AGENT, |
| requiredPermissions: 1, |
| }); |
|
|
| |
| expect(accessibleResources).toHaveLength(3); |
| const resourceIds = accessibleResources.map((id) => id.toString()); |
| expect(resourceIds).toContain(resource1.toString()); |
| expect(resourceIds).toContain(resource2.toString()); |
| expect(resourceIds).toContain(resource3.toString()); |
| }); |
|
|
| test('should get effective permissions with mixed ID types', async () => { |
| |
| await grantPermission({ |
| principalType: PrincipalType.USER, |
| principalId: stringUserId, |
| resourceType: ResourceType.AGENT, |
| resourceId: testResourceId, |
| accessRoleId: AccessRoleIds.AGENT_VIEWER, |
| grantedBy: grantedById, |
| }); |
|
|
| |
| await grantPermission({ |
| principalType: PrincipalType.GROUP, |
| principalId: stringGroupId, |
| resourceType: ResourceType.AGENT, |
| resourceId: testResourceId, |
| accessRoleId: AccessRoleIds.AGENT_EDITOR, |
| grantedBy: grantedById, |
| }); |
|
|
| |
| getUserPrincipals.mockResolvedValue([ |
| { |
| principalType: PrincipalType.USER, |
| principalId: new mongoose.Types.ObjectId(stringUserId), |
| }, |
| { |
| principalType: PrincipalType.GROUP, |
| principalId: new mongoose.Types.ObjectId(stringGroupId), |
| }, |
| { principalType: PrincipalType.PUBLIC }, |
| ]); |
|
|
| const effectivePermissions = await getEffectivePermissions({ |
| userId: stringUserId, |
| resourceType: ResourceType.AGENT, |
| resourceId: testResourceId, |
| }); |
|
|
| |
| expect(effectivePermissions).toBe(3); |
| }); |
| }); |
| }); |
|
|