First commit
This commit is contained in:
1345
node_modules/ldapjs/lib/client/client.js
generated
vendored
Normal file
1345
node_modules/ldapjs/lib/client/client.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
7
node_modules/ldapjs/lib/client/constants.js
generated
vendored
Normal file
7
node_modules/ldapjs/lib/client/constants.js
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
'use strict'
|
||||
|
||||
module.exports = {
|
||||
// https://tools.ietf.org/html/rfc4511#section-4.1.1
|
||||
// Message identifiers are an integer between (0, maxint).
|
||||
MAX_MSGID: Math.pow(2, 31) - 1
|
||||
}
|
||||
23
node_modules/ldapjs/lib/client/index.js
generated
vendored
Normal file
23
node_modules/ldapjs/lib/client/index.js
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
'use strict'
|
||||
|
||||
const logger = require('../logger')
|
||||
const Client = require('./client')
|
||||
|
||||
module.exports = {
|
||||
Client,
|
||||
createClient: function createClient (options) {
|
||||
if (isObject(options) === false) throw TypeError('options (object) required')
|
||||
if (options.url && typeof options.url !== 'string' && !Array.isArray(options.url)) throw TypeError('options.url (string|array) required')
|
||||
if (options.socketPath && typeof options.socketPath !== 'string') throw TypeError('options.socketPath must be a string')
|
||||
if ((options.url && options.socketPath) || !(options.url || options.socketPath)) throw TypeError('options.url ^ options.socketPath (String) required')
|
||||
if (!options.log) options.log = logger
|
||||
if (isObject(options.log) !== true) throw TypeError('options.log must be an object')
|
||||
if (!options.log.child) options.log.child = function () { return options.log }
|
||||
|
||||
return new Client(options)
|
||||
}
|
||||
}
|
||||
|
||||
function isObject (input) {
|
||||
return Object.prototype.toString.apply(input) === '[object Object]'
|
||||
}
|
||||
25
node_modules/ldapjs/lib/client/message-tracker/ge-window.js
generated
vendored
Normal file
25
node_modules/ldapjs/lib/client/message-tracker/ge-window.js
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
'use strict'
|
||||
|
||||
const { MAX_MSGID } = require('../constants')
|
||||
|
||||
/**
|
||||
* Compare a reference id with another id to determine "greater than or equal"
|
||||
* between the two values according to a sliding window.
|
||||
*
|
||||
* @param {integer} ref
|
||||
* @param {integer} comp
|
||||
*
|
||||
* @returns {boolean} `true` if the `comp` value is >= to the `ref` value
|
||||
* within the computed window, otherwise `false`.
|
||||
*/
|
||||
module.exports = function geWindow (ref, comp) {
|
||||
let max = ref + Math.floor(MAX_MSGID / 2)
|
||||
const min = ref
|
||||
if (max >= MAX_MSGID) {
|
||||
// Handle roll-over
|
||||
max = max - MAX_MSGID - 1
|
||||
return ((comp <= max) || (comp >= min))
|
||||
} else {
|
||||
return ((comp <= max) && (comp >= min))
|
||||
}
|
||||
}
|
||||
23
node_modules/ldapjs/lib/client/message-tracker/id-generator.js
generated
vendored
Normal file
23
node_modules/ldapjs/lib/client/message-tracker/id-generator.js
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
'use strict'
|
||||
|
||||
const { MAX_MSGID } = require('../constants')
|
||||
|
||||
/**
|
||||
* Returns a function that generates message identifiers. According to RFC 4511
|
||||
* the identifers should be `(0, MAX_MSGID)`. The returned function handles
|
||||
* this and wraps around when the maximum has been reached.
|
||||
*
|
||||
* @param {integer} [start=0] Starting number in the identifier sequence.
|
||||
*
|
||||
* @returns {function} This function accepts no parameters and returns an
|
||||
* increasing sequence identifier each invocation until it reaches the maximum
|
||||
* identifier. At this point the sequence starts over.
|
||||
*/
|
||||
module.exports = function idGeneratorFactory (start = 0) {
|
||||
let currentID = start
|
||||
return function nextID () {
|
||||
const id = currentID + 1
|
||||
currentID = (id >= MAX_MSGID) ? 1 : id
|
||||
return currentID
|
||||
}
|
||||
}
|
||||
161
node_modules/ldapjs/lib/client/message-tracker/index.js
generated
vendored
Normal file
161
node_modules/ldapjs/lib/client/message-tracker/index.js
generated
vendored
Normal file
@ -0,0 +1,161 @@
|
||||
'use strict'
|
||||
|
||||
const idGeneratorFactory = require('./id-generator')
|
||||
const purgeAbandoned = require('./purge-abandoned')
|
||||
|
||||
/**
|
||||
* Returns a message tracker object that keeps track of which message
|
||||
* identifiers correspond to which message handlers. Also handles keeping track
|
||||
* of abandoned messages.
|
||||
*
|
||||
* @param {object} options
|
||||
* @param {string} options.id An identifier for the tracker.
|
||||
* @param {object} options.parser An object that will be used to parse messages.
|
||||
*
|
||||
* @returns {MessageTracker}
|
||||
*/
|
||||
module.exports = function messageTrackerFactory (options) {
|
||||
if (Object.prototype.toString.call(options) !== '[object Object]') {
|
||||
throw Error('options object is required')
|
||||
}
|
||||
if (!options.id || typeof options.id !== 'string') {
|
||||
throw Error('options.id string is required')
|
||||
}
|
||||
if (!options.parser || Object.prototype.toString.call(options.parser) !== '[object Object]') {
|
||||
throw Error('options.parser object is required')
|
||||
}
|
||||
|
||||
let currentID = 0
|
||||
const nextID = idGeneratorFactory()
|
||||
const messages = new Map()
|
||||
const abandoned = new Map()
|
||||
|
||||
/**
|
||||
* @typedef {object} MessageTracker
|
||||
* @property {string} id The identifier of the tracker as supplied via the options.
|
||||
* @property {object} parser The parser object given by the the options.
|
||||
*/
|
||||
const tracker = {
|
||||
id: options.id,
|
||||
parser: options.parser
|
||||
}
|
||||
|
||||
/**
|
||||
* Count of messages awaiting response.
|
||||
*
|
||||
* @alias pending
|
||||
* @memberof! MessageTracker#
|
||||
*/
|
||||
Object.defineProperty(tracker, 'pending', {
|
||||
get () {
|
||||
return messages.size
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* Move a specific message to the abanded track.
|
||||
*
|
||||
* @param {integer} msgID The identifier for the message to move.
|
||||
*
|
||||
* @memberof MessageTracker
|
||||
* @method abandon
|
||||
*/
|
||||
tracker.abandon = function abandonMessage (msgID) {
|
||||
if (messages.has(msgID) === false) return false
|
||||
const toAbandon = messages.get(msgID)
|
||||
abandoned.set(msgID, {
|
||||
age: currentID,
|
||||
message: toAbandon.message,
|
||||
cb: toAbandon.callback
|
||||
})
|
||||
return messages.delete(msgID)
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {object} Tracked
|
||||
* @property {object} message The tracked message. Usually the outgoing
|
||||
* request object.
|
||||
* @property {Function} callback The handler to use when receiving a
|
||||
* response to the tracked message.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Retrieves the message handler for a message. Removes abandoned messages
|
||||
* that have been given time to be resolved.
|
||||
*
|
||||
* @param {integer} msgID The identifier for the message to get the handler for.
|
||||
*
|
||||
* @memberof MessageTracker
|
||||
* @method fetch
|
||||
*/
|
||||
tracker.fetch = function fetchMessage (msgID) {
|
||||
const tracked = messages.get(msgID)
|
||||
if (tracked) {
|
||||
purgeAbandoned(msgID, abandoned)
|
||||
return tracked
|
||||
}
|
||||
|
||||
// We sent an abandon request but the server either wasn't able to process
|
||||
// it or has not received it yet. Therefore, we received a response for the
|
||||
// abandoned message. So we must return the abandoned message's callback
|
||||
// to be processed normally.
|
||||
const abandonedMsg = abandoned.get(msgID)
|
||||
if (abandonedMsg) {
|
||||
return { message: abandonedMsg, callback: abandonedMsg.cb }
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all message tracks, cleans up the abandoned track, and invokes
|
||||
* a callback for each message purged.
|
||||
*
|
||||
* @param {function} cb A function with the signature `(msgID, handler)`.
|
||||
*
|
||||
* @memberof MessageTracker
|
||||
* @method purge
|
||||
*/
|
||||
tracker.purge = function purgeMessages (cb) {
|
||||
messages.forEach((val, key) => {
|
||||
purgeAbandoned(key, abandoned)
|
||||
tracker.remove(key)
|
||||
cb(key, val.callback)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a message from all tracking.
|
||||
*
|
||||
* @param {integer} msgID The identifier for the message to remove from tracking.
|
||||
*
|
||||
* @memberof MessageTracker
|
||||
* @method remove
|
||||
*/
|
||||
tracker.remove = function removeMessage (msgID) {
|
||||
if (messages.delete(msgID) === false) {
|
||||
abandoned.delete(msgID)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a message handler to be tracked.
|
||||
*
|
||||
* @param {object} message The message object to be tracked. This object will
|
||||
* have a new property added to it: `messageId`.
|
||||
* @param {function} callback The handler for the message.
|
||||
*
|
||||
* @memberof MessageTracker
|
||||
* @method track
|
||||
*/
|
||||
tracker.track = function trackMessage (message, callback) {
|
||||
currentID = nextID()
|
||||
// This side effect is not ideal but the client doesn't attach the tracker
|
||||
// to itself until after the `.connect` method has fired. If this can be
|
||||
// refactored later, then we can possibly get rid of this side effect.
|
||||
message.messageId = currentID
|
||||
messages.set(currentID, { callback, message })
|
||||
}
|
||||
|
||||
return tracker
|
||||
}
|
||||
34
node_modules/ldapjs/lib/client/message-tracker/purge-abandoned.js
generated
vendored
Normal file
34
node_modules/ldapjs/lib/client/message-tracker/purge-abandoned.js
generated
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
'use strict'
|
||||
|
||||
const { AbandonedError } = require('../../errors')
|
||||
const geWindow = require('./ge-window')
|
||||
|
||||
/**
|
||||
* Given a `msgID` and a set of `abandoned` messages, remove any abandoned
|
||||
* messages that existed _prior_ to the specified `msgID`. For example, let's
|
||||
* assume the server has sent 3 messages:
|
||||
*
|
||||
* 1. A search message.
|
||||
* 2. An abandon message for the search message.
|
||||
* 3. A new search message.
|
||||
*
|
||||
* When the response for message #1 comes in, if it does, it will be processed
|
||||
* normally due to the specification. Message #2 will not receive a response, or
|
||||
* if the server does send one since the spec sort of allows it, we won't do
|
||||
* anything with it because we just discard that listener. Now the response
|
||||
* for message #3 comes in. At this point, we will issue a purge of responses
|
||||
* by passing in `msgID = 3`. This result is that we will remove the tracking
|
||||
* for message #1.
|
||||
*
|
||||
* @param {integer} msgID An upper bound for the messages to be purged.
|
||||
* @param {Map} abandoned A set of abandoned messages. Each message is an object
|
||||
* `{ age: <id>, cb: <func> }` where `age` was the current message id when the
|
||||
* abandon message was sent.
|
||||
*/
|
||||
module.exports = function purgeAbandoned (msgID, abandoned) {
|
||||
abandoned.forEach((val, key) => {
|
||||
if (geWindow(val.age, msgID) === false) return
|
||||
val.cb(new AbandonedError('client request abandoned'))
|
||||
abandoned.delete(key)
|
||||
})
|
||||
}
|
||||
36
node_modules/ldapjs/lib/client/request-queue/enqueue.js
generated
vendored
Normal file
36
node_modules/ldapjs/lib/client/request-queue/enqueue.js
generated
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Adds requests to the queue. If a timeout has been added to the queue then
|
||||
* this will freeze the queue with the newly added item, flush it, and then
|
||||
* unfreeze it when the queue has been cleared.
|
||||
*
|
||||
* @param {object} message An LDAP message object.
|
||||
* @param {object} expect An expectation object.
|
||||
* @param {object} emitter An event emitter or `null`.
|
||||
* @param {function} cb A callback to invoke when the request is finished.
|
||||
*
|
||||
* @returns {boolean} `true` if the requested was queued. `false` if the queue
|
||||
* is not accepting any requests.
|
||||
*/
|
||||
module.exports = function enqueue (message, expect, emitter, cb) {
|
||||
if (this._queue.size >= this.size || this._frozen) {
|
||||
return false
|
||||
}
|
||||
|
||||
this._queue.add({ message, expect, emitter, cb })
|
||||
|
||||
if (this.timeout === 0) return true
|
||||
if (this._timer === null) return true
|
||||
|
||||
// A queue can have a specified time allotted for it to be cleared. If that
|
||||
// time has been reached, reject new entries until the queue has been cleared.
|
||||
this._timer = setTimeout(queueTimeout.bind(this), this.timeout)
|
||||
|
||||
return true
|
||||
|
||||
function queueTimeout () {
|
||||
this.freeze()
|
||||
this.purge()
|
||||
}
|
||||
}
|
||||
24
node_modules/ldapjs/lib/client/request-queue/flush.js
generated
vendored
Normal file
24
node_modules/ldapjs/lib/client/request-queue/flush.js
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Invokes all requests in the queue by passing them to the supplied callback
|
||||
* function and then clears all items from the queue.
|
||||
*
|
||||
* @param {function} cb A function used to handle the requests.
|
||||
*/
|
||||
module.exports = function flush (cb) {
|
||||
if (this._timer) {
|
||||
clearTimeout(this._timer)
|
||||
this._timer = null
|
||||
}
|
||||
|
||||
// We must get a local copy of the queue and clear it before iterating it.
|
||||
// The client will invoke this flush function _many_ times. If we try to
|
||||
// iterate it without a local copy and clearing first then we will overflow
|
||||
// the stack.
|
||||
const requests = Array.from(this._queue.values())
|
||||
this._queue.clear()
|
||||
for (const req of requests) {
|
||||
cb(req.message, req.expect, req.emitter, req.cb)
|
||||
}
|
||||
}
|
||||
39
node_modules/ldapjs/lib/client/request-queue/index.js
generated
vendored
Normal file
39
node_modules/ldapjs/lib/client/request-queue/index.js
generated
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
'use strict'
|
||||
|
||||
const enqueue = require('./enqueue')
|
||||
const flush = require('./flush')
|
||||
const purge = require('./purge')
|
||||
|
||||
/**
|
||||
* Builds a request queue object and returns it.
|
||||
*
|
||||
* @param {object} [options]
|
||||
* @param {integer} [options.size] Maximum size of the request queue. Must be
|
||||
* a number greater than `0` if supplied. Default: `Infinity`.
|
||||
* @param {integer} [options.timeout] Time in milliseconds a queue has to
|
||||
* complete the requests it contains.
|
||||
*
|
||||
* @returns {object} A queue instance.
|
||||
*/
|
||||
module.exports = function requestQueueFactory (options) {
|
||||
const opts = Object.assign({}, options)
|
||||
const q = {
|
||||
size: (opts.size > 0) ? opts.size : Infinity,
|
||||
timeout: (opts.timeout > 0) ? opts.timeout : 0,
|
||||
_queue: new Set(),
|
||||
_timer: null,
|
||||
_frozen: false
|
||||
}
|
||||
|
||||
q.enqueue = enqueue.bind(q)
|
||||
q.flush = flush.bind(q)
|
||||
q.purge = purge.bind(q)
|
||||
q.freeze = function freeze () {
|
||||
this._frozen = true
|
||||
}
|
||||
q.thaw = function thaw () {
|
||||
this._frozen = false
|
||||
}
|
||||
|
||||
return q
|
||||
}
|
||||
12
node_modules/ldapjs/lib/client/request-queue/purge.js
generated
vendored
Normal file
12
node_modules/ldapjs/lib/client/request-queue/purge.js
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
'use strict'
|
||||
|
||||
const { TimeoutError } = require('../../errors')
|
||||
|
||||
/**
|
||||
* Flushes the queue by rejecting all pending requests with a timeout error.
|
||||
*/
|
||||
module.exports = function purge () {
|
||||
this.flush(function flushCB (a, b, c, cb) {
|
||||
cb(new TimeoutError('request queue timeout'))
|
||||
})
|
||||
}
|
||||
167
node_modules/ldapjs/lib/client/search_pager.js
generated
vendored
Normal file
167
node_modules/ldapjs/lib/client/search_pager.js
generated
vendored
Normal file
@ -0,0 +1,167 @@
|
||||
'use strict'
|
||||
|
||||
const EventEmitter = require('events').EventEmitter
|
||||
const util = require('util')
|
||||
const assert = require('assert-plus')
|
||||
const { PagedResultsControl } = require('@ldapjs/controls')
|
||||
const CorkedEmitter = require('../corked_emitter.js')
|
||||
|
||||
/// --- API
|
||||
|
||||
/**
|
||||
* Handler object for paged search operations.
|
||||
*
|
||||
* Provided to consumers in place of the normal search EventEmitter it adds the
|
||||
* following new events:
|
||||
* 1. page - Emitted whenever the end of a result page is encountered.
|
||||
* If this is the last page, 'end' will also be emitted.
|
||||
* The event passes two arguments:
|
||||
* 1. The result object (similar to 'end')
|
||||
* 2. A callback function optionally used to continue the search
|
||||
* operation if the pagePause option was specified during
|
||||
* initialization.
|
||||
* 2. pageError - Emitted if the server does not support paged search results
|
||||
* If there are no listeners for this event, the 'error' event
|
||||
* will be emitted (and 'end' will not be). By listening to
|
||||
* 'pageError', a successful search that lacks paging will be
|
||||
* able to emit 'end'.
|
||||
*/
|
||||
function SearchPager (opts) {
|
||||
assert.object(opts)
|
||||
assert.func(opts.callback)
|
||||
assert.number(opts.pageSize)
|
||||
assert.func(opts.sendRequest)
|
||||
|
||||
CorkedEmitter.call(this, {})
|
||||
|
||||
this.callback = opts.callback
|
||||
this.controls = opts.controls
|
||||
this.pageSize = opts.pageSize
|
||||
this.pagePause = opts.pagePause
|
||||
this.sendRequest = opts.sendRequest
|
||||
|
||||
this.controls.forEach(function (control) {
|
||||
if (control.type === PagedResultsControl.OID) {
|
||||
// The point of using SearchPager is not having to do this.
|
||||
// Toss an error if the pagedResultsControl is present
|
||||
throw new Error('redundant pagedResultControl')
|
||||
}
|
||||
})
|
||||
|
||||
this.finished = false
|
||||
this.started = false
|
||||
|
||||
const emitter = new EventEmitter()
|
||||
emitter.on('searchRequest', this.emit.bind(this, 'searchRequest'))
|
||||
emitter.on('searchEntry', this.emit.bind(this, 'searchEntry'))
|
||||
emitter.on('end', this._onEnd.bind(this))
|
||||
emitter.on('error', this._onError.bind(this))
|
||||
this.childEmitter = emitter
|
||||
}
|
||||
util.inherits(SearchPager, CorkedEmitter)
|
||||
module.exports = SearchPager
|
||||
|
||||
/**
|
||||
* Start the paged search.
|
||||
*/
|
||||
SearchPager.prototype.begin = function begin () {
|
||||
// Starting first page
|
||||
this._nextPage(null)
|
||||
}
|
||||
|
||||
SearchPager.prototype._onEnd = function _onEnd (res) {
|
||||
const self = this
|
||||
let cookie = null
|
||||
res.controls.forEach(function (control) {
|
||||
if (control.type === PagedResultsControl.OID) {
|
||||
cookie = control.value.cookie
|
||||
}
|
||||
})
|
||||
// Pass a noop callback by default for page events
|
||||
const nullCb = function () { }
|
||||
|
||||
if (cookie === null) {
|
||||
// paged search not supported
|
||||
this.finished = true
|
||||
this.emit('page', res, nullCb)
|
||||
const err = new Error('missing paged control')
|
||||
err.name = 'PagedError'
|
||||
if (this.listeners('pageError').length > 0) {
|
||||
this.emit('pageError', err)
|
||||
// If the consumer as subscribed to pageError, SearchPager is absolved
|
||||
// from delivering the fault via the 'error' event. Emitting an 'end'
|
||||
// event after 'error' breaks the contract that the standard client
|
||||
// provides, so it's only a possibility if 'pageError' is used instead.
|
||||
this.emit('end', res)
|
||||
} else {
|
||||
this.emit('error', err)
|
||||
// No end event possible per explanation above.
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (cookie.length === 0) {
|
||||
// end of paged results
|
||||
this.finished = true
|
||||
this.emit('page', nullCb)
|
||||
this.emit('end', res)
|
||||
} else {
|
||||
if (this.pagePause) {
|
||||
// Wait to fetch next page until callback is invoked
|
||||
// Halt page fetching if called with error
|
||||
this.emit('page', res, function (err) {
|
||||
if (!err) {
|
||||
self._nextPage(cookie)
|
||||
} else {
|
||||
// the paged search has been canceled so emit an end
|
||||
self.emit('end', res)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.emit('page', res, nullCb)
|
||||
this._nextPage(cookie)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SearchPager.prototype._onError = function _onError (err) {
|
||||
this.finished = true
|
||||
this.emit('error', err)
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate a search for the next page using the returned cookie value.
|
||||
*/
|
||||
SearchPager.prototype._nextPage = function _nextPage (cookie) {
|
||||
const controls = this.controls.slice(0)
|
||||
controls.push(new PagedResultsControl({
|
||||
value: {
|
||||
size: this.pageSize,
|
||||
cookie
|
||||
}
|
||||
}))
|
||||
|
||||
this.sendRequest(controls, this.childEmitter, this._sendCallback.bind(this))
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback provided to the client API for successful transmission.
|
||||
*/
|
||||
SearchPager.prototype._sendCallback = function _sendCallback (err) {
|
||||
if (err) {
|
||||
this.finished = true
|
||||
if (!this.started) {
|
||||
// EmitSend error during the first page, bail via callback
|
||||
this.callback(err, null)
|
||||
} else {
|
||||
this.emit('error', err)
|
||||
}
|
||||
} else {
|
||||
// search successfully send
|
||||
if (!this.started) {
|
||||
this.started = true
|
||||
// send self as emitter as the client would
|
||||
this.callback(null, this)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user