UNPKG

ad-promise

Version:

This is a fork of the gheeres node-activedirectory. It fixes some issues with timeouts with very large AD-Groups as well as returning also promises so you won't have to use callbacks

132 lines (117 loc) 5.66 kB
var _ = require('underscore'); const log = require('./internal/service.log'); const joinAttributes = require('./internal/service.joinAttributes'); const chunkItem = require('./getUsersForGroup/service.usersforgroup.chunkItem'); const defaultPageSize = 1000; // The maximum number of results that AD will return in a single call. Default=1000 const defaultAttributes = require('../configs/config.defaultAttributes'); const limitPromises = require('limitpromises'); const maxPromiseConfig = require('../configs/config.maxPromiseGroup'); /** * For the specified group, retrieve all of the users that belong to the group. * * @public * @param {Object} [opts] Optional LDAP query string parameters to execute. { scope: '', filter: '', attributes: [ '', '', ... ], sizeLimit: 0, timelimit: 0 } * @param {String} groupName The name of the group to retrieve membership from. * @param {Function} callback The callback to execute when completed. callback(err: {Object}, users: {Array[User]}) */ async function getUsersForGroup(opts, groupName, callback) { var self = this; return new Promise((resolve, reject) => { if (typeof (groupName) === 'function' || !groupName) { callback = groupName; groupName = opts; opts = undefined; } log.trace('getUsersForGroup(%j,%s)', opts, groupName); let result = []; let groups = limitPromises(GroupName => { return new Promise((resolve, reject) => { self.findGroup( _.defaults( {}, _.omit(opts || {}, 'attributes'), {attributes: joinAttributes((opts || {}).attributes || defaultAttributes.group, ['member'])} ), GroupName).then(group => { resolve(group); }, err => { reject(err); }); }); }, [groupName], maxPromiseConfig.members, "members", { Reject: { rejectBehaviour: "retry", retryAttempts : 10 } }); Promise.all(groups.map(p => {return p.result})).then(async groups => { let group = groups[0]; // Group not found if (!group) { if (callback) callback(null, group); resolve(group); return; } // If only one result found, encapsulate result into array. if (typeof (group.member) === 'string') { group.member = [group.member]; } /** * Breaks the large array into chucks of the specified size. * @param {Array} arr The array to break into chunks * @param {Number} chunkSize The size of each chunk. * @returns {Array} The resulting array containing each chunk */ function chunk(arr, chunkSize) { var result = []; for (var index = 0, length = arr.length; index < length; index += chunkSize) { result.push(arr.slice(index, index + chunkSize)); } return (result); } // We need to break this into the default size queries so // we can have them running concurrently. var chunks = chunk(group.member || [], defaultPageSize); if (chunks.length > 1) { log.debug('Splitting %d member(s) of "%s" into %d parallel chunks', (group.member || []).length, groupName, chunks.length); } // Chunks represent the cn for each user; // We use the limitPromises Function which will limit the number of promises running at the same time. // This is necessary to avoid that the socket of the AD is in use and thus cannot be accessed if(!chunks || chunks.length === 0) return resolve([]); let allChunks = limitPromises(Chunk => { return new Promise((resolve, reject) => { chunkItem(Chunk, opts, self).then(members => { resolve(result.concat(members)); }, err => { if(callback){ callback(err); } return reject(err); }); }); }, chunks, maxPromiseConfig.chunks, "chunks", { Reject: { rejectBehaviour: "retry", retryAttempts: 5 } }); // Wait for all the chunks to be ready then send the result back; await Promise.all(allChunks.map(Chunk => { return Chunk.result })).then(data => { if(callback){ if(data.length === 0){ callback(null, []); } else { callback(null, data[0]); } } return data.length === 0 ? resolve([]) : resolve(data[0]); }, err => { return reject(err); }); }); }); }; module.exports = getUsersForGroup;