mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
tests: final ldap tests
This commit is contained in:
parent
3ae6edc194
commit
4e1f8ae30f
|
@ -2,6 +2,7 @@ import { QueryFailedError } from '@n8n/typeorm';
|
||||||
import { mock } from 'jest-mock-extended';
|
import { mock } from 'jest-mock-extended';
|
||||||
import { Client } from 'ldapts';
|
import { Client } from 'ldapts';
|
||||||
import type { Cipher } from 'n8n-core';
|
import type { Cipher } from 'n8n-core';
|
||||||
|
import { randomString } from 'n8n-workflow';
|
||||||
|
|
||||||
import config from '@/config';
|
import config from '@/config';
|
||||||
import { AuthIdentityRepository } from '@/databases/repositories/auth-identity.repository';
|
import { AuthIdentityRepository } from '@/databases/repositories/auth-identity.repository';
|
||||||
|
@ -23,6 +24,7 @@ import {
|
||||||
resolveBinaryAttributes,
|
resolveBinaryAttributes,
|
||||||
processUsers,
|
processUsers,
|
||||||
mapLdapUserToDbUser,
|
mapLdapUserToDbUser,
|
||||||
|
saveLdapSynchronization,
|
||||||
} from '../helpers.ee';
|
} from '../helpers.ee';
|
||||||
|
|
||||||
// Mock ldapts client
|
// Mock ldapts client
|
||||||
|
@ -45,6 +47,11 @@ jest.mock('../helpers.ee', () => ({
|
||||||
processUsers: jest.fn(),
|
processUsers: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
jest.mock('n8n-workflow', () => ({
|
||||||
|
...jest.requireActual('n8n-workflow'),
|
||||||
|
randomString: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
describe('LdapService', () => {
|
describe('LdapService', () => {
|
||||||
const ldapConfig: LdapConfig = {
|
const ldapConfig: LdapConfig = {
|
||||||
loginEnabled: true,
|
loginEnabled: true,
|
||||||
|
@ -892,7 +899,12 @@ describe('LdapService', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe.only('runSync()', () => {
|
describe('runSync()', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const mockedRandomString = randomString as jest.Mock;
|
||||||
|
mockedRandomString.mockReturnValue('nonRandomPassword');
|
||||||
|
});
|
||||||
|
|
||||||
it('should search for users with expected parameters', async () => {
|
it('should search for users with expected parameters', async () => {
|
||||||
const settingsRepository = mock<SettingsRepository>({
|
const settingsRepository = mock<SettingsRepository>({
|
||||||
findOneByOrFail: jest.fn().mockResolvedValue({
|
findOneByOrFail: jest.fn().mockResolvedValue({
|
||||||
|
@ -907,7 +919,7 @@ describe('LdapService', () => {
|
||||||
const mockedGetLdapIds = getLdapIds as jest.Mock;
|
const mockedGetLdapIds = getLdapIds as jest.Mock;
|
||||||
mockedGetLdapIds.mockResolvedValue([]);
|
mockedGetLdapIds.mockResolvedValue([]);
|
||||||
|
|
||||||
const expectedParameter = createFilter(
|
const expectedFilter = createFilter(
|
||||||
`(${ldapConfig.loginIdAttribute}=*)`,
|
`(${ldapConfig.loginIdAttribute}=*)`,
|
||||||
ldapConfig.userFilter,
|
ldapConfig.userFilter,
|
||||||
);
|
);
|
||||||
|
@ -916,7 +928,7 @@ describe('LdapService', () => {
|
||||||
await ldapService.runSync('dry');
|
await ldapService.runSync('dry');
|
||||||
|
|
||||||
expect(searchWithAdminBindingSpy).toHaveBeenCalledTimes(1);
|
expect(searchWithAdminBindingSpy).toHaveBeenCalledTimes(1);
|
||||||
expect(searchWithAdminBindingSpy).toHaveBeenCalledWith(expectedParameter);
|
expect(searchWithAdminBindingSpy).toHaveBeenCalledWith(expectedFilter);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should resolve binary attributes for users', async () => {
|
it('should resolve binary attributes for users', async () => {
|
||||||
|
@ -965,45 +977,222 @@ describe('LdapService', () => {
|
||||||
await expect(ldapService.runSync('dry')).rejects.toThrowError('Error finding users');
|
await expect(ldapService.runSync('dry')).rejects.toThrowError('Error finding users');
|
||||||
});
|
});
|
||||||
|
|
||||||
it.skip('should process users if mode is "live"', async () => {
|
it('should process expected users if mode is "live"', async () => {
|
||||||
const settingsRepository = mock<SettingsRepository>({
|
const settingsRepository = mock<SettingsRepository>({
|
||||||
findOneByOrFail: jest.fn().mockResolvedValue({
|
findOneByOrFail: jest.fn().mockResolvedValue({
|
||||||
value: JSON.stringify({ ldapConfig }),
|
value: JSON.stringify(ldapConfig),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
const ldapService = new LdapService(mockLogger(), settingsRepository, mock(), mock());
|
const ldapService = new LdapService(mockLogger(), settingsRepository, mock(), mock());
|
||||||
const foundUsers = [
|
|
||||||
// New user
|
// Users that don't exist in memory
|
||||||
|
const newUsers = [
|
||||||
{
|
{
|
||||||
dn: 'uid=jdoe,ou=users,dc=example,dc=com',
|
dn: 'uid=johndoe,ou=users,dc=example,dc=com',
|
||||||
cn: ['John Doe'],
|
cn: 'John Doe',
|
||||||
givenName: 'John',
|
givenName: 'John',
|
||||||
sn: 'Doe',
|
sn: 'Doe',
|
||||||
mail: ['jdoe@example.com'],
|
mail: 'john.doe@example.com',
|
||||||
uid: ['jdoe'],
|
uid: 'johndoe',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dn: 'uid=janedoe,ou=users,dc=example,dc=com',
|
||||||
|
cn: 'Jane Doe',
|
||||||
|
givenName: 'Jane',
|
||||||
|
sn: 'Doe',
|
||||||
|
mail: 'jane.doe@example.com',
|
||||||
|
uid: 'janedoe',
|
||||||
},
|
},
|
||||||
// Existing user
|
|
||||||
// User to delete
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Users that exist in memory and in LDAP response
|
||||||
|
const updateUsers = [
|
||||||
|
{
|
||||||
|
dn: 'uid=emilyclark,ou=users,dc=example,dc=com',
|
||||||
|
cn: 'Emily Clark',
|
||||||
|
givenName: 'Emily',
|
||||||
|
sn: 'Clark',
|
||||||
|
mail: 'emily.clark@example.com',
|
||||||
|
uid: 'emilyclark',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// Users that only exist in memory
|
||||||
|
const deleteUsers = ['santaclaus', 'jackfrost'];
|
||||||
|
|
||||||
|
const foundUsers = [...newUsers, ...updateUsers];
|
||||||
Client.prototype.search = jest.fn().mockResolvedValue({ searchEntries: foundUsers });
|
Client.prototype.search = jest.fn().mockResolvedValue({ searchEntries: foundUsers });
|
||||||
|
|
||||||
const mockedGetLdapIds = getLdapIds as jest.Mock;
|
const mockedGetLdapIds = getLdapIds as jest.Mock;
|
||||||
mockedGetLdapIds.mockResolvedValue([]);
|
|
||||||
|
|
||||||
const createDatabaseUser = mapLdapUserToDbUser(foundUsers[0], ldapConfig, true);
|
// Delete users that exist in memory but not in the LDAP response
|
||||||
|
mockedGetLdapIds.mockResolvedValue(['emilyclark', ...deleteUsers]);
|
||||||
|
|
||||||
|
const newDbUsers = newUsers.map((user) => mapLdapUserToDbUser(user, ldapConfig, true));
|
||||||
|
const updateDbUsers = updateUsers.map((user) => mapLdapUserToDbUser(user, ldapConfig));
|
||||||
|
|
||||||
await ldapService.init();
|
await ldapService.init();
|
||||||
await ldapService.runSync('live');
|
await ldapService.runSync('live');
|
||||||
|
|
||||||
expect(processUsers).toHaveBeenCalledTimes(1);
|
expect(processUsers).toHaveBeenCalledTimes(1);
|
||||||
expect(processUsers).toHaveBeenCalledWith([createDatabaseUser], [], []);
|
expect(processUsers).toHaveBeenCalledWith(newDbUsers, updateDbUsers, deleteUsers);
|
||||||
});
|
});
|
||||||
|
|
||||||
it.todo('should write expected data to the database');
|
it('should sync expected LDAP data when no errors', async () => {
|
||||||
it.todo(
|
const settingsRepository = mock<SettingsRepository>({
|
||||||
'should write expected data to the database with an error message if processing users fails',
|
findOneByOrFail: jest.fn().mockResolvedValue({
|
||||||
);
|
value: JSON.stringify(ldapConfig),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const ldapService = new LdapService(mockLogger(), settingsRepository, mock(), mock());
|
||||||
|
|
||||||
|
// Users that don't exist in memory
|
||||||
|
const newUsers = [
|
||||||
|
{
|
||||||
|
dn: 'uid=johndoe,ou=users,dc=example,dc=com',
|
||||||
|
cn: 'John Doe',
|
||||||
|
givenName: 'John',
|
||||||
|
sn: 'Doe',
|
||||||
|
mail: 'john.doe@example.com',
|
||||||
|
uid: 'johndoe',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dn: 'uid=janedoe,ou=users,dc=example,dc=com',
|
||||||
|
cn: 'Jane Doe',
|
||||||
|
givenName: 'Jane',
|
||||||
|
sn: 'Doe',
|
||||||
|
mail: 'jane.doe@example.com',
|
||||||
|
uid: 'janedoe',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// Users that exist in memory and in LDAP response
|
||||||
|
const updateUsers = [
|
||||||
|
{
|
||||||
|
dn: 'uid=emilyclark,ou=users,dc=example,dc=com',
|
||||||
|
cn: 'Emily Clark',
|
||||||
|
givenName: 'Emily',
|
||||||
|
sn: 'Clark',
|
||||||
|
mail: 'emily.clark@example.com',
|
||||||
|
uid: 'emilyclark',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// Users that only exist in memory
|
||||||
|
const deleteUsers = ['santaclaus', 'jackfrost'];
|
||||||
|
|
||||||
|
const foundUsers = [...newUsers, ...updateUsers];
|
||||||
|
Client.prototype.search = jest.fn().mockResolvedValue({ searchEntries: foundUsers });
|
||||||
|
|
||||||
|
const mockedGetLdapIds = getLdapIds as jest.Mock;
|
||||||
|
|
||||||
|
// Delete users that exist in memory but not in the LDAP response
|
||||||
|
mockedGetLdapIds.mockResolvedValue(['emilyclark', ...deleteUsers]);
|
||||||
|
|
||||||
|
const newDbUsers = newUsers.map((user) => mapLdapUserToDbUser(user, ldapConfig, true));
|
||||||
|
const updateDbUsers = updateUsers.map((user) => mapLdapUserToDbUser(user, ldapConfig));
|
||||||
|
|
||||||
|
jest.setSystemTime(new Date('2024-12-25'));
|
||||||
|
const expectedDate = new Date();
|
||||||
|
|
||||||
|
await ldapService.init();
|
||||||
|
await ldapService.runSync('live');
|
||||||
|
|
||||||
|
expect(saveLdapSynchronization).toHaveBeenCalledTimes(1);
|
||||||
|
expect(saveLdapSynchronization).toHaveBeenCalledWith({
|
||||||
|
startedAt: expectedDate,
|
||||||
|
endedAt: expectedDate,
|
||||||
|
created: newDbUsers.length,
|
||||||
|
updated: updateDbUsers.length,
|
||||||
|
disabled: deleteUsers.length,
|
||||||
|
scanned: foundUsers.length,
|
||||||
|
runMode: 'live',
|
||||||
|
status: 'success',
|
||||||
|
error: '',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should sync expected LDAP data when users fail to process', async () => {
|
||||||
|
const settingsRepository = mock<SettingsRepository>({
|
||||||
|
findOneByOrFail: jest.fn().mockResolvedValue({
|
||||||
|
value: JSON.stringify(ldapConfig),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const ldapService = new LdapService(mockLogger(), settingsRepository, mock(), mock());
|
||||||
|
|
||||||
|
// Users that don't exist in memory
|
||||||
|
const newUsers = [
|
||||||
|
{
|
||||||
|
dn: 'uid=johndoe,ou=users,dc=example,dc=com',
|
||||||
|
cn: 'John Doe',
|
||||||
|
givenName: 'John',
|
||||||
|
sn: 'Doe',
|
||||||
|
mail: 'john.doe@example.com',
|
||||||
|
uid: 'johndoe',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dn: 'uid=janedoe,ou=users,dc=example,dc=com',
|
||||||
|
cn: 'Jane Doe',
|
||||||
|
givenName: 'Jane',
|
||||||
|
sn: 'Doe',
|
||||||
|
mail: 'jane.doe@example.com',
|
||||||
|
uid: 'janedoe',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// Users that exist in memory and in LDAP response
|
||||||
|
const updateUsers = [
|
||||||
|
{
|
||||||
|
dn: 'uid=emilyclark,ou=users,dc=example,dc=com',
|
||||||
|
cn: 'Emily Clark',
|
||||||
|
givenName: 'Emily',
|
||||||
|
sn: 'Clark',
|
||||||
|
mail: 'emily.clark@example.com',
|
||||||
|
uid: 'emilyclark',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// Users that only exist in memory
|
||||||
|
const deleteUsers = ['santaclaus', 'jackfrost'];
|
||||||
|
|
||||||
|
const foundUsers = [...newUsers, ...updateUsers];
|
||||||
|
Client.prototype.search = jest.fn().mockResolvedValue({ searchEntries: foundUsers });
|
||||||
|
|
||||||
|
const mockedProcessUsers = processUsers as jest.Mock;
|
||||||
|
mockedProcessUsers.mockRejectedValue(
|
||||||
|
new QueryFailedError('Query', [], new Error('Error processing users')),
|
||||||
|
);
|
||||||
|
|
||||||
|
const mockedGetLdapIds = getLdapIds as jest.Mock;
|
||||||
|
|
||||||
|
// Delete users that exist in memory but not in the LDAP response
|
||||||
|
mockedGetLdapIds.mockResolvedValue(['emilyclark', ...deleteUsers]);
|
||||||
|
|
||||||
|
const newDbUsers = newUsers.map((user) => mapLdapUserToDbUser(user, ldapConfig, true));
|
||||||
|
const updateDbUsers = updateUsers.map((user) => mapLdapUserToDbUser(user, ldapConfig));
|
||||||
|
|
||||||
|
jest.setSystemTime(new Date('2024-12-25'));
|
||||||
|
const expectedDate = new Date();
|
||||||
|
|
||||||
|
await ldapService.init();
|
||||||
|
await ldapService.runSync('live');
|
||||||
|
|
||||||
|
expect(saveLdapSynchronization).toHaveBeenCalledTimes(1);
|
||||||
|
expect(saveLdapSynchronization).toHaveBeenCalledWith({
|
||||||
|
startedAt: expectedDate,
|
||||||
|
endedAt: expectedDate,
|
||||||
|
created: newDbUsers.length,
|
||||||
|
updated: updateDbUsers.length,
|
||||||
|
disabled: deleteUsers.length,
|
||||||
|
scanned: foundUsers.length,
|
||||||
|
runMode: 'live',
|
||||||
|
status: 'error',
|
||||||
|
error: 'Error processing users',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should emit expected event if synchronization is enabled', async () => {
|
it('should emit expected event if synchronization is enabled', async () => {
|
||||||
const settingsRepository = mock<SettingsRepository>({
|
const settingsRepository = mock<SettingsRepository>({
|
||||||
|
@ -1074,7 +1263,7 @@ describe('LdapService', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should emit expected event if processUsers fails', async () => {
|
it('should emit expected event with error message if processUsers fails', async () => {
|
||||||
const settingsRepository = mock<SettingsRepository>({
|
const settingsRepository = mock<SettingsRepository>({
|
||||||
findOneByOrFail: jest.fn().mockResolvedValue({
|
findOneByOrFail: jest.fn().mockResolvedValue({
|
||||||
value: JSON.stringify(ldapConfig),
|
value: JSON.stringify(ldapConfig),
|
||||||
|
|
Loading…
Reference in a new issue