mock implementation for new func and passing tests
Signed-off-by: Phil Gore <pgore@ea.com>
This commit is contained in:
@@ -24,6 +24,10 @@ import {
|
||||
LdapVendor,
|
||||
} from './vendors';
|
||||
|
||||
export interface SearchCallback {
|
||||
(entry: SearchEntry): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Basic wrapper for the ldapjs library.
|
||||
*
|
||||
@@ -110,66 +114,50 @@ export class LdapClient {
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs an LDAP search operation, creates a generator to limit memory usage
|
||||
* Performs an LDAP search operation, calls a function on each entry to limit memory usage
|
||||
*
|
||||
* @param dn The fully qualified base DN to search within
|
||||
* @param options The search options
|
||||
* @param f The callback to call on each search entry
|
||||
*/
|
||||
*searchStreaming(
|
||||
async searchStreaming(
|
||||
dn: string,
|
||||
options: SearchOptions,
|
||||
): IterableIterator<SearchEntry> {
|
||||
let queueSize = 25;
|
||||
if (options) {
|
||||
if (
|
||||
options.paged &&
|
||||
typeof options.paged === 'object' &&
|
||||
typeof options.paged.pageSize === 'number' &&
|
||||
options.paged.pageSize > 0
|
||||
) {
|
||||
queueSize = options.paged.pageSize;
|
||||
}
|
||||
}
|
||||
const queue: SearchEntry[] = new Array(queueSize);
|
||||
let done = false;
|
||||
f: SearchCallback,
|
||||
): Promise<void> {
|
||||
try {
|
||||
this.client.search(dn, options, (err, res) => {
|
||||
if (err) {
|
||||
throw new Error(errorString(err));
|
||||
}
|
||||
|
||||
res.on('searchReference', () => {
|
||||
throw new Error('Unable to handle referral');
|
||||
});
|
||||
|
||||
res.on('searchEntry', entry => {
|
||||
queue.push(entry);
|
||||
});
|
||||
|
||||
res.on('error', e => {
|
||||
throw new Error(errorString(e));
|
||||
});
|
||||
|
||||
res.on('end', r => {
|
||||
if (!r) {
|
||||
throw new Error('Null response');
|
||||
} else if (r.status !== 0) {
|
||||
throw new Error(`Got status ${r.status}: ${r.errorMessage}`);
|
||||
} else {
|
||||
done = true;
|
||||
return await new Promise<void>((resolve, reject) => {
|
||||
this.client.search(dn, options, (err, res) => {
|
||||
if (err) {
|
||||
reject(new Error(errorString(err)));
|
||||
}
|
||||
|
||||
res.on('searchReference', () => {
|
||||
reject(new Error('Unable to handle referral'));
|
||||
});
|
||||
|
||||
res.on('searchEntry', async entry => {
|
||||
await f(entry);
|
||||
});
|
||||
|
||||
res.on('error', e => {
|
||||
reject(new Error(errorString(e)));
|
||||
});
|
||||
|
||||
res.on('end', r => {
|
||||
if (!r) {
|
||||
throw new Error('Null response');
|
||||
} else if (r.status !== 0) {
|
||||
throw new Error(`Got status ${r.status}: ${r.errorMessage}`);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
} catch (e) {
|
||||
throw new Error(`LDAP search at DN "${dn}" failed, ${e.message}`);
|
||||
}
|
||||
while (!done && queue.length > 0) {
|
||||
const res = queue.pop();
|
||||
if (!res) {
|
||||
continue;
|
||||
}
|
||||
yield res;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -67,7 +67,7 @@ function searchEntry(
|
||||
|
||||
describe('readLdapUsers', () => {
|
||||
const client: jest.Mocked<LdapClient> = {
|
||||
search: jest.fn(),
|
||||
searchStreaming: jest.fn(),
|
||||
getVendor: jest.fn(),
|
||||
} as any;
|
||||
|
||||
@@ -75,18 +75,25 @@ describe('readLdapUsers', () => {
|
||||
|
||||
it('transfers all attributes from a default ldap vendor', async () => {
|
||||
client.getVendor.mockResolvedValue(DefaultLdapVendor);
|
||||
client.search.mockResolvedValue([
|
||||
searchEntry({
|
||||
uid: ['uid-value'],
|
||||
description: ['description-value'],
|
||||
cn: ['cn-value'],
|
||||
mail: ['mail-value'],
|
||||
avatarUrl: ['avatarUrl-value'],
|
||||
memberOf: ['x', 'y', 'z'],
|
||||
entryDN: ['dn-value'],
|
||||
entryUUID: ['uuid-value'],
|
||||
}),
|
||||
]);
|
||||
client.searchStreaming.mockImplementation(
|
||||
async (_dn, _opts, fn): Promise<void> => {
|
||||
return await new Promise<void>((resolve, _reject) => {
|
||||
fn(
|
||||
searchEntry({
|
||||
uid: ['uid-value'],
|
||||
description: ['description-value'],
|
||||
cn: ['cn-value'],
|
||||
mail: ['mail-value'],
|
||||
avatarUrl: ['avatarUrl-value'],
|
||||
memberOf: ['x', 'y', 'z'],
|
||||
entryDN: ['dn-value'],
|
||||
entryUUID: ['uuid-value'],
|
||||
}),
|
||||
);
|
||||
resolve();
|
||||
});
|
||||
},
|
||||
);
|
||||
const config: UserConfig = {
|
||||
dn: 'ddd',
|
||||
options: {},
|
||||
@@ -129,37 +136,45 @@ describe('readLdapUsers', () => {
|
||||
|
||||
it('transfers all attributes from Microsoft Active Directory', async () => {
|
||||
client.getVendor.mockResolvedValue(ActiveDirectoryVendor);
|
||||
client.search.mockResolvedValue([
|
||||
searchEntry({
|
||||
uid: ['uid-value'],
|
||||
description: ['description-value'],
|
||||
cn: ['cn-value'],
|
||||
mail: ['mail-value'],
|
||||
avatarUrl: ['avatarUrl-value'],
|
||||
memberOf: ['x', 'y', 'z'],
|
||||
distinguishedName: ['dn-value'],
|
||||
objectGUID: [
|
||||
Buffer.from([
|
||||
68,
|
||||
2,
|
||||
125,
|
||||
190,
|
||||
209,
|
||||
0,
|
||||
94,
|
||||
73,
|
||||
133,
|
||||
33,
|
||||
230,
|
||||
174,
|
||||
234,
|
||||
195,
|
||||
160,
|
||||
152,
|
||||
]),
|
||||
],
|
||||
}),
|
||||
]);
|
||||
client.searchStreaming.mockImplementation(
|
||||
async (_dn, _opts, fn): Promise<void> => {
|
||||
return await new Promise<void>((resolve, _reject) => {
|
||||
fn(
|
||||
searchEntry({
|
||||
uid: ['uid-value'],
|
||||
description: ['description-value'],
|
||||
cn: ['cn-value'],
|
||||
mail: ['mail-value'],
|
||||
avatarUrl: ['avatarUrl-value'],
|
||||
memberOf: ['x', 'y', 'z'],
|
||||
distinguishedName: ['dn-value'],
|
||||
objectGUID: [
|
||||
Buffer.from([
|
||||
68,
|
||||
2,
|
||||
125,
|
||||
190,
|
||||
209,
|
||||
0,
|
||||
94,
|
||||
73,
|
||||
133,
|
||||
33,
|
||||
230,
|
||||
174,
|
||||
234,
|
||||
195,
|
||||
160,
|
||||
152,
|
||||
]),
|
||||
],
|
||||
}),
|
||||
);
|
||||
resolve();
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
const config: UserConfig = {
|
||||
dn: 'ddd',
|
||||
options: {},
|
||||
@@ -203,7 +218,7 @@ describe('readLdapUsers', () => {
|
||||
|
||||
describe('readLdapGroups', () => {
|
||||
const client: jest.Mocked<LdapClient> = {
|
||||
search: jest.fn(),
|
||||
searchStreaming: jest.fn(),
|
||||
getVendor: jest.fn(),
|
||||
} as any;
|
||||
|
||||
@@ -211,19 +226,26 @@ describe('readLdapGroups', () => {
|
||||
|
||||
it('transfers all attributes from a default ldap vendor', async () => {
|
||||
client.getVendor.mockResolvedValue(DefaultLdapVendor);
|
||||
client.search.mockResolvedValue([
|
||||
searchEntry({
|
||||
cn: ['cn-value'],
|
||||
description: ['description-value'],
|
||||
tt: ['type-value'],
|
||||
mail: ['mail-value'],
|
||||
avatarUrl: ['avatarUrl-value'],
|
||||
memberOf: ['x', 'y', 'z'],
|
||||
member: ['e', 'f', 'g'],
|
||||
entryDN: ['dn-value'],
|
||||
entryUUID: ['uuid-value'],
|
||||
}),
|
||||
]);
|
||||
client.searchStreaming.mockImplementation(
|
||||
async (_dn, _opts, fn): Promise<void> => {
|
||||
return await new Promise<void>((resolve, _reject) => {
|
||||
fn(
|
||||
searchEntry({
|
||||
cn: ['cn-value'],
|
||||
description: ['description-value'],
|
||||
tt: ['type-value'],
|
||||
mail: ['mail-value'],
|
||||
avatarUrl: ['avatarUrl-value'],
|
||||
memberOf: ['x', 'y', 'z'],
|
||||
member: ['e', 'f', 'g'],
|
||||
entryDN: ['dn-value'],
|
||||
entryUUID: ['uuid-value'],
|
||||
}),
|
||||
);
|
||||
resolve();
|
||||
});
|
||||
},
|
||||
);
|
||||
const config: GroupConfig = {
|
||||
dn: 'ddd',
|
||||
options: {},
|
||||
@@ -274,38 +296,45 @@ describe('readLdapGroups', () => {
|
||||
});
|
||||
it('transfers all attributes from Microsoft Active Directory', async () => {
|
||||
client.getVendor.mockResolvedValue(ActiveDirectoryVendor);
|
||||
client.search.mockResolvedValue([
|
||||
searchEntry({
|
||||
cn: ['cn-value'],
|
||||
description: ['description-value'],
|
||||
tt: ['type-value'],
|
||||
mail: ['mail-value'],
|
||||
avatarUrl: ['avatarUrl-value'],
|
||||
memberOf: ['x', 'y', 'z'],
|
||||
member: ['e', 'f', 'g'],
|
||||
distinguishedName: ['dn-value'],
|
||||
objectGUID: [
|
||||
Buffer.from([
|
||||
68,
|
||||
2,
|
||||
125,
|
||||
190,
|
||||
209,
|
||||
0,
|
||||
94,
|
||||
73,
|
||||
133,
|
||||
33,
|
||||
230,
|
||||
174,
|
||||
234,
|
||||
195,
|
||||
160,
|
||||
152,
|
||||
]),
|
||||
],
|
||||
}),
|
||||
]);
|
||||
client.searchStreaming.mockImplementation(
|
||||
async (_dn, _opts, fn): Promise<void> => {
|
||||
return await new Promise<void>((resolve, _reject) => {
|
||||
fn(
|
||||
searchEntry({
|
||||
cn: ['cn-value'],
|
||||
description: ['description-value'],
|
||||
tt: ['type-value'],
|
||||
mail: ['mail-value'],
|
||||
avatarUrl: ['avatarUrl-value'],
|
||||
memberOf: ['x', 'y', 'z'],
|
||||
member: ['e', 'f', 'g'],
|
||||
distinguishedName: ['dn-value'],
|
||||
objectGUID: [
|
||||
Buffer.from([
|
||||
68,
|
||||
2,
|
||||
125,
|
||||
190,
|
||||
209,
|
||||
0,
|
||||
94,
|
||||
73,
|
||||
133,
|
||||
33,
|
||||
230,
|
||||
174,
|
||||
234,
|
||||
195,
|
||||
160,
|
||||
152,
|
||||
]),
|
||||
],
|
||||
}),
|
||||
);
|
||||
resolve();
|
||||
});
|
||||
},
|
||||
);
|
||||
const config: GroupConfig = {
|
||||
dn: 'ddd',
|
||||
options: {},
|
||||
|
||||
@@ -107,24 +107,19 @@ export async function readLdapUsers(
|
||||
|
||||
const transformer = opts?.transformer ?? defaultUserTransformer;
|
||||
|
||||
const entries = client.searchStreaming(dn, options);
|
||||
for (const entry of entries) {
|
||||
if (!entry) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const entity = await transformer(vendor, config, entry);
|
||||
await client.searchStreaming(dn, options, async user => {
|
||||
const entity = await transformer(vendor, config, user);
|
||||
|
||||
if (!entity) {
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
mapReferencesAttr(entry, vendor, map.memberOf, (myDn, vs) => {
|
||||
mapReferencesAttr(user, vendor, map.memberOf, (myDn, vs) => {
|
||||
ensureItems(userMemberOf, myDn, vs);
|
||||
});
|
||||
|
||||
entities.push(entity);
|
||||
}
|
||||
});
|
||||
|
||||
return { users: entities, userMemberOf };
|
||||
}
|
||||
@@ -213,17 +208,15 @@ export async function readLdapGroups(
|
||||
|
||||
const transformer = opts?.transformer ?? defaultGroupTransformer;
|
||||
|
||||
const entries = client.searchStreaming(dn, options);
|
||||
|
||||
for (const entry of entries) {
|
||||
await client.searchStreaming(dn, options, async entry => {
|
||||
if (!entry) {
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
const entity = await transformer(vendor, config, entry);
|
||||
|
||||
if (!entity) {
|
||||
continue;
|
||||
return;
|
||||
}
|
||||
|
||||
mapReferencesAttr(entry, vendor, map.memberOf, (myDn, vs) => {
|
||||
@@ -234,7 +227,7 @@ export async function readLdapGroups(
|
||||
});
|
||||
|
||||
groups.push(entity);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
groups,
|
||||
|
||||
Reference in New Issue
Block a user