test: add tests for findAndAuthenticateLdapUser

This commit is contained in:
Marc Littlemore 2024-12-09 12:07:45 +00:00
parent 4e1f8ae30f
commit f07bc7939a
No known key found for this signature in database

View file

@ -21,10 +21,12 @@ import { mockInstance, mockLogger } from '@test/mocking';
import { import {
getLdapIds, getLdapIds,
createFilter, createFilter,
escapeFilter,
resolveBinaryAttributes, resolveBinaryAttributes,
processUsers, processUsers,
mapLdapUserToDbUser, mapLdapUserToDbUser,
saveLdapSynchronization, saveLdapSynchronization,
resolveEntryBinaryAttributes,
} from '../helpers.ee'; } from '../helpers.ee';
// Mock ldapts client // Mock ldapts client
@ -45,6 +47,7 @@ jest.mock('../helpers.ee', () => ({
saveLdapSynchronization: jest.fn(), saveLdapSynchronization: jest.fn(),
resolveBinaryAttributes: jest.fn(), resolveBinaryAttributes: jest.fn(),
processUsers: jest.fn(), processUsers: jest.fn(),
resolveEntryBinaryAttributes: jest.fn(),
})); }));
jest.mock('n8n-workflow', () => ({ jest.mock('n8n-workflow', () => ({
@ -772,7 +775,277 @@ describe('LdapService', () => {
}); });
}); });
describe.skip('findAndAuthenticateLdapUser()', () => {}); describe('findAndAuthenticateLdapUser()', () => {
it('should search for expected admin login ID', async () => {
const settingsRepository = mock<SettingsRepository>({
findOneByOrFail: jest.fn().mockResolvedValue({
value: JSON.stringify(ldapConfig),
}),
});
const ldapService = new LdapService(mockLogger(), settingsRepository, mock(), mock());
const searchWithAdminBindingSpy = jest.spyOn(ldapService, 'searchWithAdminBinding');
Client.prototype.search = jest.fn().mockResolvedValue({ searchEntries: [] });
const mockedGetLdapIds = getLdapIds as jest.Mock;
mockedGetLdapIds.mockResolvedValue([]);
const expectedFilter = createFilter(
`(${ldapConfig.loginIdAttribute}=${escapeFilter('jdoe')})`,
ldapConfig.userFilter,
);
await ldapService.init();
await ldapService.findAndAuthenticateLdapUser(
'jdoe',
'fakePassword',
ldapConfig.loginIdAttribute,
ldapConfig.userFilter,
);
expect(searchWithAdminBindingSpy).toHaveBeenCalledTimes(1);
expect(searchWithAdminBindingSpy).toHaveBeenCalledWith(expectedFilter);
});
it('should emit expected error if admin search fails', async () => {
const settingsRepository = mock<SettingsRepository>({
findOneByOrFail: jest.fn().mockResolvedValue({
value: JSON.stringify(ldapConfig),
}),
});
const eventServiceMock = mock<EventService>({
emit: jest.fn(),
});
const ldapService = new LdapService(
mockLogger(),
settingsRepository,
mock(),
eventServiceMock,
);
Client.prototype.search = jest.fn().mockRejectedValue(new Error('Failed to find admin user'));
const mockedGetLdapIds = getLdapIds as jest.Mock;
mockedGetLdapIds.mockResolvedValue([]);
await ldapService.init();
const result = await ldapService.findAndAuthenticateLdapUser(
'jdoe',
'fakePassword',
ldapConfig.loginIdAttribute,
ldapConfig.userFilter,
);
expect(eventServiceMock.emit).toBeCalledTimes(1);
expect(eventServiceMock.emit).toHaveBeenCalledWith('ldap-login-sync-failed', {
error: 'Failed to find admin user',
});
expect(result).toBeUndefined();
});
it('should return undefined if no user is found', async () => {
const settingsRepository = mock<SettingsRepository>({
findOneByOrFail: jest.fn().mockResolvedValue({
value: JSON.stringify(ldapConfig),
}),
});
const ldapService = new LdapService(mockLogger(), settingsRepository, mock(), mock());
Client.prototype.search = jest.fn().mockResolvedValue({ searchEntries: [] });
const mockedGetLdapIds = getLdapIds as jest.Mock;
mockedGetLdapIds.mockResolvedValue([]);
await ldapService.init();
const result = await ldapService.findAndAuthenticateLdapUser(
'jdoe',
'fakePassword',
ldapConfig.loginIdAttribute,
ldapConfig.userFilter,
);
expect(result).toBeUndefined();
});
it('should validate found user', async () => {
const settingsRepository = mock<SettingsRepository>({
findOneByOrFail: jest.fn().mockResolvedValue({
value: JSON.stringify(ldapConfig),
}),
});
const foundUsers = [
{
dn: 'uid=jdoe,ou=users,dc=example,dc=com',
cn: ['John Doe'],
mail: ['jdoe@example.com'],
uid: ['jdoe'],
},
];
const ldapService = new LdapService(mockLogger(), settingsRepository, mock(), mock());
Client.prototype.search = jest.fn().mockResolvedValue({ searchEntries: [...foundUsers] });
const validUserSpy = jest.spyOn(ldapService, 'validUser');
const mockedGetLdapIds = getLdapIds as jest.Mock;
mockedGetLdapIds.mockResolvedValue([]);
await ldapService.init();
await ldapService.findAndAuthenticateLdapUser(
'jdoe',
'fakePassword',
ldapConfig.loginIdAttribute,
ldapConfig.userFilter,
);
expect(validUserSpy).toBeCalledTimes(1);
expect(validUserSpy).toHaveBeenCalledWith(foundUsers[0].dn, 'fakePassword');
});
it('should validate last user if more than one is found', async () => {
const settingsRepository = mock<SettingsRepository>({
findOneByOrFail: jest.fn().mockResolvedValue({
value: JSON.stringify(ldapConfig),
}),
});
const foundUsers = [
{
dn: 'uid=jdoe,ou=users,dc=example,dc=com',
cn: ['John Doe'],
mail: ['jdoe@example.com'],
uid: ['jdoe'],
},
{
dn: 'uid=janedoe,ou=users,dc=example,dc=com',
cn: ['Jane Doe'],
mail: ['jane.doe@example.com'],
uid: ['janedoe'],
},
];
const ldapService = new LdapService(mockLogger(), settingsRepository, mock(), mock());
Client.prototype.search = jest.fn().mockResolvedValue({ searchEntries: [...foundUsers] });
const validUserSpy = jest.spyOn(ldapService, 'validUser');
const mockedGetLdapIds = getLdapIds as jest.Mock;
mockedGetLdapIds.mockResolvedValue([]);
await ldapService.init();
await ldapService.findAndAuthenticateLdapUser(
'jdoe',
'fakePassword',
ldapConfig.loginIdAttribute,
ldapConfig.userFilter,
);
expect(validUserSpy).toBeCalledTimes(1);
expect(validUserSpy).toHaveBeenCalledWith(foundUsers[1].dn, 'fakePassword');
});
it('should return undefined if invalid user is found', async () => {
const settingsRepository = mock<SettingsRepository>({
findOneByOrFail: jest.fn().mockResolvedValue({
value: JSON.stringify(ldapConfig),
}),
});
const foundUsers = [
{
dn: 'uid=jdoe,ou=users,dc=example,dc=com',
cn: ['John Doe'],
mail: ['jdoe@example.com'],
uid: ['jdoe'],
},
];
const ldapService = new LdapService(mockLogger(), settingsRepository, mock(), mock());
Client.prototype.search = jest.fn().mockResolvedValue({ searchEntries: [...foundUsers] });
const validUserSpy = jest
.spyOn(ldapService, 'validUser')
.mockRejectedValue(new Error('Failed to validate user'));
const mockedGetLdapIds = getLdapIds as jest.Mock;
mockedGetLdapIds.mockResolvedValue([]);
await ldapService.init();
const result = await ldapService.findAndAuthenticateLdapUser(
'jdoe',
'fakePassword',
ldapConfig.loginIdAttribute,
ldapConfig.userFilter,
);
expect(validUserSpy).toHaveBeenCalledTimes(1);
expect(result).toBeUndefined();
});
it('should resolve binary attributes for found user', async () => {
const settingsRepository = mock<SettingsRepository>({
findOneByOrFail: jest.fn().mockResolvedValue({
value: JSON.stringify(ldapConfig),
}),
});
const foundUsers = [
{
dn: 'uid=jdoe,ou=users,dc=example,dc=com',
cn: ['John Doe'],
mail: ['jdoe@example.com'],
uid: ['jdoe'],
},
];
const ldapService = new LdapService(mockLogger(), settingsRepository, mock(), mock());
Client.prototype.search = jest.fn().mockResolvedValue({ searchEntries: [...foundUsers] });
const mockedGetLdapIds = getLdapIds as jest.Mock;
mockedGetLdapIds.mockResolvedValue([]);
await ldapService.init();
await ldapService.findAndAuthenticateLdapUser(
'jdoe',
'fakePassword',
ldapConfig.loginIdAttribute,
ldapConfig.userFilter,
);
expect(resolveEntryBinaryAttributes).toHaveBeenCalledTimes(1);
expect(resolveEntryBinaryAttributes).toHaveBeenCalledWith(foundUsers[0]);
});
it('should return found user', async () => {
const settingsRepository = mock<SettingsRepository>({
findOneByOrFail: jest.fn().mockResolvedValue({
value: JSON.stringify(ldapConfig),
}),
});
const foundUsers = [
{
dn: 'uid=jdoe,ou=users,dc=example,dc=com',
cn: ['John Doe'],
mail: ['jdoe@example.com'],
uid: ['jdoe'],
},
];
const ldapService = new LdapService(mockLogger(), settingsRepository, mock(), mock());
Client.prototype.search = jest.fn().mockResolvedValue({ searchEntries: [...foundUsers] });
const mockedGetLdapIds = getLdapIds as jest.Mock;
mockedGetLdapIds.mockResolvedValue([]);
await ldapService.init();
const result = await ldapService.findAndAuthenticateLdapUser(
'jdoe',
'fakePassword',
ldapConfig.loginIdAttribute,
ldapConfig.userFilter,
);
expect(result).toEqual(foundUsers[0]);
});
});
describe('testConnection()', () => { describe('testConnection()', () => {
it('should throw expected error if init() is not called first', async () => { it('should throw expected error if init() is not called first', async () => {
@ -1322,7 +1595,7 @@ describe('LdapService', () => {
const clearIntervalSpy = jest.spyOn(global, 'clearInterval'); const clearIntervalSpy = jest.spyOn(global, 'clearInterval');
await ldapService.init(); await ldapService.init();
await ldapService.stopSync(); ldapService.stopSync();
expect(clearIntervalSpy).toHaveBeenCalledTimes(1); expect(clearIntervalSpy).toHaveBeenCalledTimes(1);
}); });