Skip to content

Commit

Permalink
test: add tests for findAndAuthenticateLdapUser
Browse files Browse the repository at this point in the history
  • Loading branch information
MarcL committed Dec 9, 2024
1 parent 5514632 commit db0f9e7
Showing 1 changed file with 275 additions and 2 deletions.
277 changes: 275 additions & 2 deletions packages/cli/src/ldap/__tests__/ldap.service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@ import { mockInstance, mockLogger } from '@test/mocking';
import {
getLdapIds,
createFilter,
escapeFilter,
resolveBinaryAttributes,
processUsers,
mapLdapUserToDbUser,
saveLdapSynchronization,
resolveEntryBinaryAttributes,
} from '../helpers.ee';

// Mock ldapts client
Expand All @@ -45,6 +47,7 @@ jest.mock('../helpers.ee', () => ({
saveLdapSynchronization: jest.fn(),
resolveBinaryAttributes: jest.fn(),
processUsers: jest.fn(),
resolveEntryBinaryAttributes: jest.fn(),
}));

jest.mock('n8n-workflow', () => ({
Expand Down Expand Up @@ -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()', () => {
it('should throw expected error if init() is not called first', async () => {
Expand Down Expand Up @@ -1322,7 +1595,7 @@ describe('LdapService', () => {
const clearIntervalSpy = jest.spyOn(global, 'clearInterval');

await ldapService.init();
await ldapService.stopSync();
ldapService.stopSync();

expect(clearIntervalSpy).toHaveBeenCalledTimes(1);
});
Expand Down

0 comments on commit db0f9e7

Please sign in to comment.