From 5097e540874f573e5dcccc1173e15774d0592758 Mon Sep 17 00:00:00 2001 From: Antonio Musolino Date: Wed, 20 Jul 2022 14:26:11 +0200 Subject: [PATCH] fix(ldap): added manual paging with pause in searchStreaming to limit memory usage Signed-off-by: Antonio Musolino --- .../src/ldap/client.ts | 75 +++++++++++-------- 1 file changed, 45 insertions(+), 30 deletions(-) diff --git a/plugins/catalog-backend-module-ldap/src/ldap/client.ts b/plugins/catalog-backend-module-ldap/src/ldap/client.ts index d5f6a7d0f6..d182875e10 100644 --- a/plugins/catalog-backend-module-ldap/src/ldap/client.ts +++ b/plugins/catalog-backend-module-ldap/src/ldap/client.ts @@ -16,7 +16,7 @@ import { ForwardedError } from '@backstage/errors'; import ldap, { Client, SearchEntry, SearchOptions } from 'ldapjs'; -import { cloneDeep } from 'lodash'; +import { cloneDeep, merge } from 'lodash'; import { Logger } from 'winston'; import { BindConfig, TLSConfig } from './config'; import { errorString } from './util'; @@ -150,39 +150,54 @@ export class LdapClient { f: (entry: SearchEntry) => Promise | void, ): Promise { try { - const awaitList: Array | void> = []; + let awaitList: Array | void> = []; return await new Promise((resolve, reject) => { // Note that we clone the (frozen) options, since ldapjs rudely tries to // overwrite parts of them - this.client.search(dn, cloneDeep(options), (err, res) => { - if (err) { - reject(new Error(errorString(err))); - } - - res.on('searchReference', () => { - this.logger.warn('Received unsupported search referral'); - }); - - res.on('searchEntry', entry => { - awaitList.push(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 { - Promise.all(awaitList) - .then(() => resolve()) - .catch(reject); + this.client.search( + dn, + merge(cloneDeep(options), { paged: { pagePause: true } }), + (err, res) => { + if (err) { + reject(new Error(errorString(err))); } - }); - }); + + res.on('searchReference', () => { + this.logger.warn('Received unsupported search referral'); + }); + + res.on('searchEntry', entry => { + awaitList.push(f(entry)); + }); + + res.on('page', (_, cb) => { + // awaits completion before fetching next page + Promise.all(awaitList) + .then(() => { + // flush list + awaitList = []; + if (cb) cb(); + }) + .catch(reject); + }); + + 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 { + Promise.all(awaitList) + .then(() => resolve()) + .catch(reject); + } + }); + }, + ); }); } catch (e) { throw new ForwardedError(`LDAP search at DN "${dn}" failed`, e);