| | const mongoose = require('mongoose'); |
| | const { MongoMemoryServer } = require('mongodb-memory-server'); |
| | const { |
| | SystemRoles, |
| | Permissions, |
| | roleDefaults, |
| | PermissionTypes, |
| | } = require('librechat-data-provider'); |
| | const { getRoleByName, updateAccessPermissions } = require('~/models/Role'); |
| | const getLogStores = require('~/cache/getLogStores'); |
| | const { initializeRoles } = require('~/models'); |
| | const { Role } = require('~/db/models'); |
| |
|
| | |
| | jest.mock('~/cache/getLogStores', () => |
| | jest.fn().mockReturnValue({ |
| | get: jest.fn(), |
| | set: jest.fn(), |
| | del: jest.fn(), |
| | }), |
| | ); |
| |
|
| | let mongoServer; |
| |
|
| | 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 Role.deleteMany({}); |
| | getLogStores.mockClear(); |
| | }); |
| |
|
| | describe('updateAccessPermissions', () => { |
| | it('should update permissions when changes are needed', async () => { |
| | await new Role({ |
| | name: SystemRoles.USER, |
| | permissions: { |
| | [PermissionTypes.PROMPTS]: { |
| | CREATE: true, |
| | USE: true, |
| | SHARED_GLOBAL: false, |
| | }, |
| | }, |
| | }).save(); |
| |
|
| | await updateAccessPermissions(SystemRoles.USER, { |
| | [PermissionTypes.PROMPTS]: { |
| | CREATE: true, |
| | USE: true, |
| | SHARED_GLOBAL: true, |
| | }, |
| | }); |
| |
|
| | const updatedRole = await getRoleByName(SystemRoles.USER); |
| | expect(updatedRole.permissions[PermissionTypes.PROMPTS]).toEqual({ |
| | CREATE: true, |
| | USE: true, |
| | SHARED_GLOBAL: true, |
| | }); |
| | }); |
| |
|
| | it('should not update permissions when no changes are needed', async () => { |
| | await new Role({ |
| | name: SystemRoles.USER, |
| | permissions: { |
| | [PermissionTypes.PROMPTS]: { |
| | CREATE: true, |
| | USE: true, |
| | SHARED_GLOBAL: false, |
| | }, |
| | }, |
| | }).save(); |
| |
|
| | await updateAccessPermissions(SystemRoles.USER, { |
| | [PermissionTypes.PROMPTS]: { |
| | CREATE: true, |
| | USE: true, |
| | SHARED_GLOBAL: false, |
| | }, |
| | }); |
| |
|
| | const updatedRole = await getRoleByName(SystemRoles.USER); |
| | expect(updatedRole.permissions[PermissionTypes.PROMPTS]).toEqual({ |
| | CREATE: true, |
| | USE: true, |
| | SHARED_GLOBAL: false, |
| | }); |
| | }); |
| |
|
| | it('should handle non-existent roles', async () => { |
| | await updateAccessPermissions('NON_EXISTENT_ROLE', { |
| | [PermissionTypes.PROMPTS]: { CREATE: true }, |
| | }); |
| | const role = await Role.findOne({ name: 'NON_EXISTENT_ROLE' }); |
| | expect(role).toBeNull(); |
| | }); |
| |
|
| | it('should update only specified permissions', async () => { |
| | await new Role({ |
| | name: SystemRoles.USER, |
| | permissions: { |
| | [PermissionTypes.PROMPTS]: { |
| | CREATE: true, |
| | USE: true, |
| | SHARED_GLOBAL: false, |
| | }, |
| | }, |
| | }).save(); |
| |
|
| | await updateAccessPermissions(SystemRoles.USER, { |
| | [PermissionTypes.PROMPTS]: { SHARED_GLOBAL: true }, |
| | }); |
| |
|
| | const updatedRole = await getRoleByName(SystemRoles.USER); |
| | expect(updatedRole.permissions[PermissionTypes.PROMPTS]).toEqual({ |
| | CREATE: true, |
| | USE: true, |
| | SHARED_GLOBAL: true, |
| | }); |
| | }); |
| |
|
| | it('should handle partial updates', async () => { |
| | await new Role({ |
| | name: SystemRoles.USER, |
| | permissions: { |
| | [PermissionTypes.PROMPTS]: { |
| | CREATE: true, |
| | USE: true, |
| | SHARED_GLOBAL: false, |
| | }, |
| | }, |
| | }).save(); |
| |
|
| | await updateAccessPermissions(SystemRoles.USER, { |
| | [PermissionTypes.PROMPTS]: { USE: false }, |
| | }); |
| |
|
| | const updatedRole = await getRoleByName(SystemRoles.USER); |
| | expect(updatedRole.permissions[PermissionTypes.PROMPTS]).toEqual({ |
| | CREATE: true, |
| | USE: false, |
| | SHARED_GLOBAL: false, |
| | }); |
| | }); |
| |
|
| | it('should update multiple permission types at once', async () => { |
| | await new Role({ |
| | name: SystemRoles.USER, |
| | permissions: { |
| | [PermissionTypes.PROMPTS]: { CREATE: true, USE: true, SHARED_GLOBAL: false }, |
| | [PermissionTypes.BOOKMARKS]: { USE: true }, |
| | }, |
| | }).save(); |
| |
|
| | await updateAccessPermissions(SystemRoles.USER, { |
| | [PermissionTypes.PROMPTS]: { USE: false, SHARED_GLOBAL: true }, |
| | [PermissionTypes.BOOKMARKS]: { USE: false }, |
| | }); |
| |
|
| | const updatedRole = await getRoleByName(SystemRoles.USER); |
| | expect(updatedRole.permissions[PermissionTypes.PROMPTS]).toEqual({ |
| | CREATE: true, |
| | USE: false, |
| | SHARED_GLOBAL: true, |
| | }); |
| | expect(updatedRole.permissions[PermissionTypes.BOOKMARKS]).toEqual({ USE: false }); |
| | }); |
| |
|
| | it('should handle updates for a single permission type', async () => { |
| | await new Role({ |
| | name: SystemRoles.USER, |
| | permissions: { |
| | [PermissionTypes.PROMPTS]: { CREATE: true, USE: true, SHARED_GLOBAL: false }, |
| | }, |
| | }).save(); |
| |
|
| | await updateAccessPermissions(SystemRoles.USER, { |
| | [PermissionTypes.PROMPTS]: { USE: false, SHARED_GLOBAL: true }, |
| | }); |
| |
|
| | const updatedRole = await getRoleByName(SystemRoles.USER); |
| | expect(updatedRole.permissions[PermissionTypes.PROMPTS]).toEqual({ |
| | CREATE: true, |
| | USE: false, |
| | SHARED_GLOBAL: true, |
| | }); |
| | }); |
| |
|
| | it('should update MULTI_CONVO permissions', async () => { |
| | await new Role({ |
| | name: SystemRoles.USER, |
| | permissions: { |
| | [PermissionTypes.MULTI_CONVO]: { USE: false }, |
| | }, |
| | }).save(); |
| |
|
| | await updateAccessPermissions(SystemRoles.USER, { |
| | [PermissionTypes.MULTI_CONVO]: { USE: true }, |
| | }); |
| |
|
| | const updatedRole = await getRoleByName(SystemRoles.USER); |
| | expect(updatedRole.permissions[PermissionTypes.MULTI_CONVO]).toEqual({ USE: true }); |
| | }); |
| |
|
| | it('should update MULTI_CONVO permissions along with other permission types', async () => { |
| | await new Role({ |
| | name: SystemRoles.USER, |
| | permissions: { |
| | [PermissionTypes.PROMPTS]: { CREATE: true, USE: true, SHARED_GLOBAL: false }, |
| | [PermissionTypes.MULTI_CONVO]: { USE: false }, |
| | }, |
| | }).save(); |
| |
|
| | await updateAccessPermissions(SystemRoles.USER, { |
| | [PermissionTypes.PROMPTS]: { SHARED_GLOBAL: true }, |
| | [PermissionTypes.MULTI_CONVO]: { USE: true }, |
| | }); |
| |
|
| | const updatedRole = await getRoleByName(SystemRoles.USER); |
| | expect(updatedRole.permissions[PermissionTypes.PROMPTS]).toEqual({ |
| | CREATE: true, |
| | USE: true, |
| | SHARED_GLOBAL: true, |
| | }); |
| | expect(updatedRole.permissions[PermissionTypes.MULTI_CONVO]).toEqual({ USE: true }); |
| | }); |
| |
|
| | it('should not update MULTI_CONVO permissions when no changes are needed', async () => { |
| | await new Role({ |
| | name: SystemRoles.USER, |
| | permissions: { |
| | [PermissionTypes.MULTI_CONVO]: { USE: true }, |
| | }, |
| | }).save(); |
| |
|
| | await updateAccessPermissions(SystemRoles.USER, { |
| | [PermissionTypes.MULTI_CONVO]: { USE: true }, |
| | }); |
| |
|
| | const updatedRole = await getRoleByName(SystemRoles.USER); |
| | expect(updatedRole.permissions[PermissionTypes.MULTI_CONVO]).toEqual({ USE: true }); |
| | }); |
| | }); |
| |
|
| | describe('initializeRoles', () => { |
| | beforeEach(async () => { |
| | await Role.deleteMany({}); |
| | }); |
| |
|
| | it('should create default roles if they do not exist', async () => { |
| | await initializeRoles(); |
| |
|
| | const adminRole = await getRoleByName(SystemRoles.ADMIN); |
| | const userRole = await getRoleByName(SystemRoles.USER); |
| |
|
| | expect(adminRole).toBeTruthy(); |
| | expect(userRole).toBeTruthy(); |
| |
|
| | |
| | Object.values(PermissionTypes).forEach((permType) => { |
| | expect(adminRole.permissions[permType]).toBeDefined(); |
| | expect(userRole.permissions[permType]).toBeDefined(); |
| | }); |
| |
|
| | |
| | expect(adminRole.permissions[PermissionTypes.PROMPTS].SHARED_GLOBAL).toBe(true); |
| | expect(adminRole.permissions[PermissionTypes.BOOKMARKS].USE).toBe(true); |
| | expect(adminRole.permissions[PermissionTypes.AGENTS].CREATE).toBe(true); |
| | }); |
| |
|
| | it('should not modify existing permissions for existing roles', async () => { |
| | const customUserRole = { |
| | name: SystemRoles.USER, |
| | permissions: { |
| | [PermissionTypes.PROMPTS]: { |
| | [Permissions.USE]: false, |
| | [Permissions.CREATE]: true, |
| | [Permissions.SHARED_GLOBAL]: true, |
| | }, |
| | [PermissionTypes.BOOKMARKS]: { [Permissions.USE]: false }, |
| | }, |
| | }; |
| |
|
| | await new Role(customUserRole).save(); |
| | await initializeRoles(); |
| |
|
| | const userRole = await getRoleByName(SystemRoles.USER); |
| | expect(userRole.permissions[PermissionTypes.PROMPTS]).toEqual( |
| | customUserRole.permissions[PermissionTypes.PROMPTS], |
| | ); |
| | expect(userRole.permissions[PermissionTypes.BOOKMARKS]).toEqual( |
| | customUserRole.permissions[PermissionTypes.BOOKMARKS], |
| | ); |
| | expect(userRole.permissions[PermissionTypes.AGENTS]).toBeDefined(); |
| | }); |
| |
|
| | it('should add new permission types to existing roles', async () => { |
| | const partialUserRole = { |
| | name: SystemRoles.USER, |
| | permissions: { |
| | [PermissionTypes.PROMPTS]: |
| | roleDefaults[SystemRoles.USER].permissions[PermissionTypes.PROMPTS], |
| | [PermissionTypes.BOOKMARKS]: |
| | roleDefaults[SystemRoles.USER].permissions[PermissionTypes.BOOKMARKS], |
| | }, |
| | }; |
| |
|
| | await new Role(partialUserRole).save(); |
| | await initializeRoles(); |
| |
|
| | const userRole = await getRoleByName(SystemRoles.USER); |
| | expect(userRole.permissions[PermissionTypes.AGENTS]).toBeDefined(); |
| | expect(userRole.permissions[PermissionTypes.AGENTS].CREATE).toBeDefined(); |
| | expect(userRole.permissions[PermissionTypes.AGENTS].USE).toBeDefined(); |
| | expect(userRole.permissions[PermissionTypes.AGENTS].SHARED_GLOBAL).toBeDefined(); |
| | }); |
| |
|
| | it('should handle multiple runs without duplicating or modifying data', async () => { |
| | await initializeRoles(); |
| | await initializeRoles(); |
| |
|
| | const adminRoles = await Role.find({ name: SystemRoles.ADMIN }); |
| | const userRoles = await Role.find({ name: SystemRoles.USER }); |
| |
|
| | expect(adminRoles).toHaveLength(1); |
| | expect(userRoles).toHaveLength(1); |
| |
|
| | const adminPerms = adminRoles[0].toObject().permissions; |
| | const userPerms = userRoles[0].toObject().permissions; |
| | Object.values(PermissionTypes).forEach((permType) => { |
| | expect(adminPerms[permType]).toBeDefined(); |
| | expect(userPerms[permType]).toBeDefined(); |
| | }); |
| | }); |
| |
|
| | it('should update roles with missing permission types from roleDefaults', async () => { |
| | const partialAdminRole = { |
| | name: SystemRoles.ADMIN, |
| | permissions: { |
| | [PermissionTypes.PROMPTS]: { |
| | [Permissions.USE]: false, |
| | [Permissions.CREATE]: false, |
| | [Permissions.SHARED_GLOBAL]: false, |
| | }, |
| | [PermissionTypes.BOOKMARKS]: |
| | roleDefaults[SystemRoles.ADMIN].permissions[PermissionTypes.BOOKMARKS], |
| | }, |
| | }; |
| |
|
| | await new Role(partialAdminRole).save(); |
| | await initializeRoles(); |
| |
|
| | const adminRole = await getRoleByName(SystemRoles.ADMIN); |
| | expect(adminRole.permissions[PermissionTypes.PROMPTS]).toEqual( |
| | partialAdminRole.permissions[PermissionTypes.PROMPTS], |
| | ); |
| | expect(adminRole.permissions[PermissionTypes.AGENTS]).toBeDefined(); |
| | expect(adminRole.permissions[PermissionTypes.AGENTS].CREATE).toBeDefined(); |
| | expect(adminRole.permissions[PermissionTypes.AGENTS].USE).toBeDefined(); |
| | expect(adminRole.permissions[PermissionTypes.AGENTS].SHARED_GLOBAL).toBeDefined(); |
| | }); |
| |
|
| | it('should include MULTI_CONVO permissions when creating default roles', async () => { |
| | await initializeRoles(); |
| |
|
| | const adminRole = await getRoleByName(SystemRoles.ADMIN); |
| | const userRole = await getRoleByName(SystemRoles.USER); |
| |
|
| | expect(adminRole.permissions[PermissionTypes.MULTI_CONVO]).toBeDefined(); |
| | expect(userRole.permissions[PermissionTypes.MULTI_CONVO]).toBeDefined(); |
| | expect(adminRole.permissions[PermissionTypes.MULTI_CONVO].USE).toBe( |
| | roleDefaults[SystemRoles.ADMIN].permissions[PermissionTypes.MULTI_CONVO].USE, |
| | ); |
| | expect(userRole.permissions[PermissionTypes.MULTI_CONVO].USE).toBe( |
| | roleDefaults[SystemRoles.USER].permissions[PermissionTypes.MULTI_CONVO].USE, |
| | ); |
| | }); |
| |
|
| | it('should add MULTI_CONVO permissions to existing roles without them', async () => { |
| | const partialUserRole = { |
| | name: SystemRoles.USER, |
| | permissions: { |
| | [PermissionTypes.PROMPTS]: |
| | roleDefaults[SystemRoles.USER].permissions[PermissionTypes.PROMPTS], |
| | [PermissionTypes.BOOKMARKS]: |
| | roleDefaults[SystemRoles.USER].permissions[PermissionTypes.BOOKMARKS], |
| | }, |
| | }; |
| |
|
| | await new Role(partialUserRole).save(); |
| | await initializeRoles(); |
| |
|
| | const userRole = await getRoleByName(SystemRoles.USER); |
| | expect(userRole.permissions[PermissionTypes.MULTI_CONVO]).toBeDefined(); |
| | expect(userRole.permissions[PermissionTypes.MULTI_CONVO].USE).toBeDefined(); |
| | }); |
| | }); |
| |
|