| | const { logger } = require('@librechat/data-schemas'); |
| | const { PrincipalType, PermissionTypes, Permissions } = require('librechat-data-provider'); |
| | const { checkPeoplePickerAccess } = require('./checkPeoplePickerAccess'); |
| | const { getRoleByName } = require('~/models/Role'); |
| |
|
| | jest.mock('~/models/Role'); |
| | jest.mock('@librechat/data-schemas', () => ({ |
| | ...jest.requireActual('@librechat/data-schemas'), |
| | logger: { |
| | error: jest.fn(), |
| | }, |
| | })); |
| |
|
| | describe('checkPeoplePickerAccess', () => { |
| | let req, res, next; |
| |
|
| | beforeEach(() => { |
| | req = { |
| | user: { id: 'user123', role: 'USER' }, |
| | query: {}, |
| | }; |
| | res = { |
| | status: jest.fn().mockReturnThis(), |
| | json: jest.fn(), |
| | }; |
| | next = jest.fn(); |
| | jest.clearAllMocks(); |
| | }); |
| |
|
| | it('should return 401 if user is not authenticated', async () => { |
| | req.user = null; |
| |
|
| | await checkPeoplePickerAccess(req, res, next); |
| |
|
| | expect(res.status).toHaveBeenCalledWith(401); |
| | expect(res.json).toHaveBeenCalledWith({ |
| | error: 'Unauthorized', |
| | message: 'Authentication required', |
| | }); |
| | expect(next).not.toHaveBeenCalled(); |
| | }); |
| |
|
| | it('should return 403 if role has no permissions', async () => { |
| | getRoleByName.mockResolvedValue(null); |
| |
|
| | await checkPeoplePickerAccess(req, res, next); |
| |
|
| | expect(res.status).toHaveBeenCalledWith(403); |
| | expect(res.json).toHaveBeenCalledWith({ |
| | error: 'Forbidden', |
| | message: 'No permissions configured for user role', |
| | }); |
| | expect(next).not.toHaveBeenCalled(); |
| | }); |
| |
|
| | it('should allow access when searching for users with VIEW_USERS permission', async () => { |
| | req.query.type = PrincipalType.USER; |
| | getRoleByName.mockResolvedValue({ |
| | permissions: { |
| | [PermissionTypes.PEOPLE_PICKER]: { |
| | [Permissions.VIEW_USERS]: true, |
| | [Permissions.VIEW_GROUPS]: false, |
| | [Permissions.VIEW_ROLES]: false, |
| | }, |
| | }, |
| | }); |
| |
|
| | await checkPeoplePickerAccess(req, res, next); |
| |
|
| | expect(next).toHaveBeenCalled(); |
| | expect(res.status).not.toHaveBeenCalled(); |
| | }); |
| |
|
| | it('should deny access when searching for users without VIEW_USERS permission', async () => { |
| | req.query.type = PrincipalType.USER; |
| | getRoleByName.mockResolvedValue({ |
| | permissions: { |
| | [PermissionTypes.PEOPLE_PICKER]: { |
| | [Permissions.VIEW_USERS]: false, |
| | [Permissions.VIEW_GROUPS]: true, |
| | [Permissions.VIEW_ROLES]: true, |
| | }, |
| | }, |
| | }); |
| |
|
| | await checkPeoplePickerAccess(req, res, next); |
| |
|
| | expect(res.status).toHaveBeenCalledWith(403); |
| | expect(res.json).toHaveBeenCalledWith({ |
| | error: 'Forbidden', |
| | message: 'Insufficient permissions to search for users', |
| | }); |
| | expect(next).not.toHaveBeenCalled(); |
| | }); |
| |
|
| | it('should allow access when searching for groups with VIEW_GROUPS permission', async () => { |
| | req.query.type = PrincipalType.GROUP; |
| | getRoleByName.mockResolvedValue({ |
| | permissions: { |
| | [PermissionTypes.PEOPLE_PICKER]: { |
| | [Permissions.VIEW_USERS]: false, |
| | [Permissions.VIEW_GROUPS]: true, |
| | [Permissions.VIEW_ROLES]: false, |
| | }, |
| | }, |
| | }); |
| |
|
| | await checkPeoplePickerAccess(req, res, next); |
| |
|
| | expect(next).toHaveBeenCalled(); |
| | expect(res.status).not.toHaveBeenCalled(); |
| | }); |
| |
|
| | it('should deny access when searching for groups without VIEW_GROUPS permission', async () => { |
| | req.query.type = PrincipalType.GROUP; |
| | getRoleByName.mockResolvedValue({ |
| | permissions: { |
| | [PermissionTypes.PEOPLE_PICKER]: { |
| | [Permissions.VIEW_USERS]: true, |
| | [Permissions.VIEW_GROUPS]: false, |
| | [Permissions.VIEW_ROLES]: true, |
| | }, |
| | }, |
| | }); |
| |
|
| | await checkPeoplePickerAccess(req, res, next); |
| |
|
| | expect(res.status).toHaveBeenCalledWith(403); |
| | expect(res.json).toHaveBeenCalledWith({ |
| | error: 'Forbidden', |
| | message: 'Insufficient permissions to search for groups', |
| | }); |
| | expect(next).not.toHaveBeenCalled(); |
| | }); |
| |
|
| | it('should allow access when searching for roles with VIEW_ROLES permission', async () => { |
| | req.query.type = PrincipalType.ROLE; |
| | getRoleByName.mockResolvedValue({ |
| | permissions: { |
| | [PermissionTypes.PEOPLE_PICKER]: { |
| | [Permissions.VIEW_USERS]: false, |
| | [Permissions.VIEW_GROUPS]: false, |
| | [Permissions.VIEW_ROLES]: true, |
| | }, |
| | }, |
| | }); |
| |
|
| | await checkPeoplePickerAccess(req, res, next); |
| |
|
| | expect(next).toHaveBeenCalled(); |
| | expect(res.status).not.toHaveBeenCalled(); |
| | }); |
| |
|
| | it('should deny access when searching for roles without VIEW_ROLES permission', async () => { |
| | req.query.type = PrincipalType.ROLE; |
| | getRoleByName.mockResolvedValue({ |
| | permissions: { |
| | [PermissionTypes.PEOPLE_PICKER]: { |
| | [Permissions.VIEW_USERS]: true, |
| | [Permissions.VIEW_GROUPS]: true, |
| | [Permissions.VIEW_ROLES]: false, |
| | }, |
| | }, |
| | }); |
| |
|
| | await checkPeoplePickerAccess(req, res, next); |
| |
|
| | expect(res.status).toHaveBeenCalledWith(403); |
| | expect(res.json).toHaveBeenCalledWith({ |
| | error: 'Forbidden', |
| | message: 'Insufficient permissions to search for roles', |
| | }); |
| | expect(next).not.toHaveBeenCalled(); |
| | }); |
| |
|
| | it('should allow mixed search when user has at least one permission', async () => { |
| | |
| | req.query.type = undefined; |
| | getRoleByName.mockResolvedValue({ |
| | permissions: { |
| | [PermissionTypes.PEOPLE_PICKER]: { |
| | [Permissions.VIEW_USERS]: false, |
| | [Permissions.VIEW_GROUPS]: false, |
| | [Permissions.VIEW_ROLES]: true, |
| | }, |
| | }, |
| | }); |
| |
|
| | await checkPeoplePickerAccess(req, res, next); |
| |
|
| | expect(next).toHaveBeenCalled(); |
| | expect(res.status).not.toHaveBeenCalled(); |
| | }); |
| |
|
| | it('should deny mixed search when user has no permissions', async () => { |
| | |
| | req.query.type = undefined; |
| | getRoleByName.mockResolvedValue({ |
| | permissions: { |
| | [PermissionTypes.PEOPLE_PICKER]: { |
| | [Permissions.VIEW_USERS]: false, |
| | [Permissions.VIEW_GROUPS]: false, |
| | [Permissions.VIEW_ROLES]: false, |
| | }, |
| | }, |
| | }); |
| |
|
| | await checkPeoplePickerAccess(req, res, next); |
| |
|
| | expect(res.status).toHaveBeenCalledWith(403); |
| | expect(res.json).toHaveBeenCalledWith({ |
| | error: 'Forbidden', |
| | message: 'Insufficient permissions to search for users, groups, or roles', |
| | }); |
| | expect(next).not.toHaveBeenCalled(); |
| | }); |
| |
|
| | it('should handle errors gracefully', async () => { |
| | const error = new Error('Database error'); |
| | getRoleByName.mockRejectedValue(error); |
| |
|
| | await checkPeoplePickerAccess(req, res, next); |
| |
|
| | expect(logger.error).toHaveBeenCalledWith( |
| | '[checkPeoplePickerAccess][user123] checkPeoplePickerAccess error for req.query.type = undefined', |
| | error, |
| | ); |
| | expect(res.status).toHaveBeenCalledWith(500); |
| | expect(res.json).toHaveBeenCalledWith({ |
| | error: 'Internal Server Error', |
| | message: 'Failed to check permissions', |
| | }); |
| | expect(next).not.toHaveBeenCalled(); |
| | }); |
| |
|
| | it('should handle missing permissions object gracefully', async () => { |
| | req.query.type = PrincipalType.USER; |
| | getRoleByName.mockResolvedValue({ |
| | permissions: {}, |
| | }); |
| |
|
| | await checkPeoplePickerAccess(req, res, next); |
| |
|
| | expect(res.status).toHaveBeenCalledWith(403); |
| | expect(res.json).toHaveBeenCalledWith({ |
| | error: 'Forbidden', |
| | message: 'Insufficient permissions to search for users', |
| | }); |
| | expect(next).not.toHaveBeenCalled(); |
| | }); |
| | }); |
| |
|