First commit

This commit is contained in:
2025-10-08 11:12:59 -04:00
commit b0605a28a9
820 changed files with 100317 additions and 0 deletions

9
node_modules/ldapjs/test/.eslintrc.js generated vendored Normal file
View File

@ -0,0 +1,9 @@
module.exports = {
parserOptions: {
ecmaVersion: 'latest'
},
rules: {
'no-shadow': 'off'
}
}

1789
node_modules/ldapjs/test/client.test.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

53
node_modules/ldapjs/test/controls/control.test.js generated vendored Normal file
View File

@ -0,0 +1,53 @@
'use strict'
const { test } = require('tap')
const { BerReader, BerWriter } = require('@ldapjs/asn1')
const { Control, getControl } = require('../../lib')
test('new no args', function (t) {
t.ok(new Control())
t.end()
})
test('new with args', function (t) {
const c = new Control({
type: '2.16.840.1.113730.3.4.2',
criticality: true
})
t.ok(c)
t.equal(c.type, '2.16.840.1.113730.3.4.2')
t.ok(c.criticality)
t.end()
})
test('parse', function (t) {
const ber = new BerWriter()
ber.startSequence()
ber.writeString('2.16.840.1.113730.3.4.2')
ber.writeBoolean(true)
ber.writeString('foo')
ber.endSequence()
const c = getControl(new BerReader(ber.buffer))
t.ok(c)
t.equal(c.type, '2.16.840.1.113730.3.4.2')
t.ok(c.criticality)
t.equal(c.value.toString('utf8'), 'foo')
t.end()
})
test('parse no value', function (t) {
const ber = new BerWriter()
ber.startSequence()
ber.writeString('2.16.840.1.113730.3.4.2')
ber.endSequence()
const c = getControl(new BerReader(ber.buffer))
t.ok(c)
t.equal(c.type, '2.16.840.1.113730.3.4.2')
t.equal(c.criticality, false)
t.notOk(c.value, null)
t.end()
})

104
node_modules/ldapjs/test/corked_emitter.test.js generated vendored Normal file
View File

@ -0,0 +1,104 @@
'use strict'
const { test } = require('tap')
const CorkedEmitter = require('../lib/corked_emitter')
function gatherEventSequence (expectedNumber) {
const gatheredEvents = []
let callback
const finished = new Promise(function (resolve) {
callback = function (...args) {
gatheredEvents.push(...args)
if (gatheredEvents.length >= expectedNumber) {
// Prevent result mutation after our promise is resolved:
resolve(gatheredEvents.slice())
}
}
})
return {
finished,
callback
}
}
test('normal emit flow', function (t) {
const emitter = new CorkedEmitter()
const expectedSequence = [
['searchEntry', { data: 'a' }],
['searchEntry', { data: 'b' }],
['end']
]
const gatherer = gatherEventSequence(3)
emitter.on('searchEntry', function (...args) {
gatherer.callback(['searchEntry', ...args])
})
emitter.on('end', function (...args) {
gatherer.callback(['end', ...args])
})
emitter.emit('searchEntry', { data: 'a' })
emitter.emit('searchEntry', { data: 'b' })
emitter.emit('end')
gatherer.finished.then(function (gatheredEvents) {
expectedSequence.forEach(function (expectedEvent, i) {
t.equal(JSON.stringify(expectedEvent), JSON.stringify(gatheredEvents[i]))
})
t.end()
})
})
test('reversed listener registration', function (t) {
const emitter = new CorkedEmitter()
const expectedSequence = [
['searchEntry', { data: 'a' }],
['searchEntry', { data: 'b' }],
['end']
]
const gatherer = gatherEventSequence(3)
// This time, we swap the event listener registrations.
// The order of emits should remain unchanged.
emitter.on('end', function (...args) {
gatherer.callback(['end', ...args])
})
emitter.on('searchEntry', function (...args) {
gatherer.callback(['searchEntry', ...args])
})
emitter.emit('searchEntry', { data: 'a' })
emitter.emit('searchEntry', { data: 'b' })
emitter.emit('end')
gatherer.finished.then(function (gatheredEvents) {
expectedSequence.forEach(function (expectedEvent, i) {
t.equal(JSON.stringify(expectedEvent), JSON.stringify(gatheredEvents[i]))
})
t.end()
})
})
test('delayed listener registration', function (t) {
const emitter = new CorkedEmitter()
const expectedSequence = [
['searchEntry', { data: 'a' }],
['searchEntry', { data: 'b' }],
['end']
]
const gatherer = gatherEventSequence(3)
emitter.emit('searchEntry', { data: 'a' })
emitter.emit('searchEntry', { data: 'b' })
emitter.emit('end')
// The listeners only appear after a brief delay - this simulates
// the situation described in https://github.com/ldapjs/node-ldapjs/issues/602
// and in https://github.com/ifroz/node-ldapjs/commit/5239f6c68827f2c25b4589089c199d15bb882412
setTimeout(function () {
emitter.on('end', function (...args) {
gatherer.callback(['end', ...args])
})
emitter.on('searchEntry', function (...args) {
gatherer.callback(['searchEntry', ...args])
})
}, 50)
gatherer.finished.then(function (gatheredEvents) {
expectedSequence.forEach(function (expectedEvent, i) {
t.equal(JSON.stringify(expectedEvent), JSON.stringify(gatheredEvents[i]))
})
t.end()
})
})

53
node_modules/ldapjs/test/errors.test.js generated vendored Normal file
View File

@ -0,0 +1,53 @@
'use strict'
const { test } = require('tap')
const {
LDAPError,
ConnectionError,
AbandonedError,
TimeoutError,
ConstraintViolationError,
LDAP_OTHER
} = require('../lib')
test('basic error', function (t) {
const msg = 'mymsg'
const err = new LDAPError(msg, null, null)
t.ok(err)
t.equal(err.name, 'LDAPError')
t.equal(err.code, LDAP_OTHER)
t.equal(err.dn, '')
t.equal(err.message, msg)
t.end()
})
test('exports ConstraintViolationError', function (t) {
const msg = 'mymsg'
const err = new ConstraintViolationError(msg, null, null)
t.ok(err)
t.equal(err.name, 'ConstraintViolationError')
t.equal(err.code, 19)
t.equal(err.dn, '')
t.equal(err.message, msg)
t.end()
})
test('"custom" errors', function (t) {
const errors = [
{ name: 'ConnectionError', Func: ConnectionError },
{ name: 'AbandonedError', Func: AbandonedError },
{ name: 'TimeoutError', Func: TimeoutError }
]
errors.forEach(function (entry) {
const msg = entry.name + 'msg'
const err = new entry.Func(msg)
t.ok(err)
t.equal(err.name, entry.name)
t.equal(err.code, LDAP_OTHER)
t.equal(err.dn, '')
t.equal(err.message, msg)
})
t.end()
})

BIN
node_modules/ldapjs/test/imgs/test.jpg generated vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 740 B

116
node_modules/ldapjs/test/issue-845.test.js generated vendored Normal file
View File

@ -0,0 +1,116 @@
'use strict'
const tap = require('tap')
const { SearchResultEntry, SearchRequest } = require('@ldapjs/messages')
const ldapjs = require('../')
const server = ldapjs.createServer()
const SUFFIX = ''
const directory = {
'dc=example,dc=com': {
objectclass: 'example',
dc: 'example',
cn: 'example'
}
}
server.bind(SUFFIX, (req, res, done) => {
res.end()
return done()
})
server.search(SUFFIX, (req, res, done) => {
const dn = req.dn.toString().toLowerCase()
if (Object.hasOwn(directory, dn) === false) {
return done(Error('not in directory'))
}
switch (req.scope) {
case SearchRequest.SCOPE_BASE:
case SearchRequest.SCOPE_SUBTREE: {
res.send(new SearchResultEntry({ objectName: `dc=${req.scopeName}` }))
break
}
}
res.end()
done()
})
tap.beforeEach(t => {
return new Promise((resolve, reject) => {
server.listen(0, '127.0.0.1', (err) => {
if (err) return reject(err)
t.context.url = server.url
t.context.client = ldapjs.createClient({ url: [server.url] })
t.context.searchOpts = {
filter: '(&(objectClass=*))',
scope: 'sub',
attributes: ['dn', 'cn']
}
resolve()
})
})
})
tap.afterEach(t => {
return new Promise((resolve, reject) => {
t.context.client.destroy()
server.close((err) => {
if (err) return reject(err)
resolve()
})
})
})
tap.test('rejects if search not in directory', t => {
const { client, searchOpts } = t.context
client.search('dc=nope', searchOpts, (err, res) => {
t.error(err)
res.on('error', err => {
// TODO: plain error messages should not be lost
// This should be fixed in a revamp of the server code.
// ~ jsumners 2023-03-08
t.equal(err.lde_message, 'Operations Error')
t.end()
})
})
})
tap.test('base scope matches', t => {
const { client, searchOpts } = t.context
searchOpts.scope = 'base'
client.search('dc=example,dc=com', searchOpts, (err, res) => {
t.error(err)
res.on('error', (err) => {
t.error(err)
t.end()
})
res.on('searchEntry', entry => {
t.equal(entry.objectName.toString(), 'dc=base')
t.end()
})
})
})
tap.test('sub scope matches', t => {
const { client, searchOpts } = t.context
client.search('dc=example,dc=com', searchOpts, (err, res) => {
t.error(err)
res.on('error', (err) => {
t.error(err)
t.end()
})
res.on('searchEntry', entry => {
t.equal(entry.objectName.toString(), 'dc=subtree')
t.end()
})
})
})

103
node_modules/ldapjs/test/issue-890.test.js generated vendored Normal file
View File

@ -0,0 +1,103 @@
'use strict'
// This test is complicated. It must simulate a server sending an unsolicited,
// or a mismatched, message in order to force the client's internal message
// tracker to try and find a corresponding sent message that does not exist.
// In order to do that, we need to set a high test timeout and wait for the
// error message to be logged.
const tap = require('tap')
const ldapjs = require('../')
const { SearchResultEntry } = require('@ldapjs/messages')
const server = ldapjs.createServer()
const SUFFIX = ''
tap.timeout = 10000
server.bind(SUFFIX, (res, done) => {
res.end()
return done()
})
server.search(SUFFIX, (req, res, done) => {
const result = new SearchResultEntry({
objectName: `dc=${req.scopeName}`
})
// Respond to the search request with a matched response.
res.send(result)
res.end()
// After a short delay, send ANOTHER response to the client that will not
// be matched by the client's internal tracker.
setTimeout(
() => {
res.send(result)
res.end()
done()
},
100
)
})
tap.beforeEach(t => {
return new Promise((resolve, reject) => {
server.listen(0, '127.0.0.1', (err) => {
if (err) return reject(err)
t.context.logMessages = []
t.context.logger = {
child () { return this },
debug () {},
error (...args) {
t.context.logMessages.push(args)
},
trace () {}
}
t.context.url = server.url
t.context.client = ldapjs.createClient({
url: [server.url],
timeout: 5,
log: t.context.logger
})
resolve()
})
})
})
tap.afterEach(t => {
return new Promise((resolve, reject) => {
t.context.client.destroy()
server.close((err) => {
if (err) return reject(err)
resolve()
})
})
})
tap.test('handle null messages', t => {
const { client, logMessages } = t.context
// There's no way to get an error from the client when it has received an
// unmatched response from the server. So we need to poll our logger instance
// and detect when the corresponding error message has been logged.
const timer = setInterval(
() => {
if (logMessages.length > 0) {
t.equal(
logMessages.some(msg => msg[1] === 'unmatched server message received'),
true
)
clearInterval(timer)
t.end()
}
},
100
)
client.search('dc=test', (error) => {
t.error(error)
})
})

143
node_modules/ldapjs/test/laundry.test.js generated vendored Normal file
View File

@ -0,0 +1,143 @@
'use strict'
const tap = require('tap')
const { getSock, uuid } = require('./utils')
const { SearchResultEntry } = require('@ldapjs/messages')
const Attribute = require('@ldapjs/attribute')
const ldap = require('../lib')
function search (t, options, callback) {
t.context.client.search(t.context.suffix, options, function (err, res) {
t.error(err)
t.ok(res)
let found = false
res.on('searchEntry', function (entry) {
t.ok(entry)
found = true
})
res.on('end', function () {
t.ok(found)
if (callback) return callback()
return t.end()
})
})
}
tap.beforeEach((t) => {
return new Promise((resolve, reject) => {
const suffix = `dc=${uuid()}`
const server = ldap.createServer()
t.context.server = server
t.context.socketPath = getSock()
t.context.suffix = suffix
server.on('error', err => {
server.close(() => reject(err))
})
server.bind('cn=root', function (req, res, next) {
res.end()
return next()
})
server.search(suffix, function (req, res) {
const entry = new SearchResultEntry({
entry: 'cn=foo,' + suffix,
attributes: Attribute.fromObject({
objectclass: ['person', 'top'],
cn: 'Pogo Stick',
sn: 'Stick',
givenname: 'ogo',
mail: uuid() + '@pogostick.org'
})
})
if (req.filter.matches(entry.attributes)) {
res.send(entry)
}
res.end()
})
server.listen(t.context.socketPath, function () {
t.context.client = ldap.createClient({
socketPath: t.context.socketPath
})
t.context.client.on('error', (err) => {
t.context.server.close(() => reject(err))
})
t.context.client.on('connectError', (err) => {
t.context.server.close(() => reject(err))
})
t.context.client.on('connect', (socket) => {
t.context.socket = socket
resolve()
})
})
})
})
tap.afterEach((t) => {
return new Promise((resolve, reject) => {
if (!t.context.client) return resolve()
t.context.client.unbind(() => {
t.context.server.close((err) => {
if (err) return reject(err)
resolve()
})
})
})
})
tap.test('Evolution search filter (GH-3)', function (t) {
// This is what Evolution sends, when searching for a contact 'ogo'. Wow.
const filter =
'(|(cn=ogo*)(givenname=ogo*)(sn=ogo*)(mail=ogo*)(member=ogo*)' +
'(primaryphone=ogo*)(telephonenumber=ogo*)(homephone=ogo*)(mobile=ogo*)' +
'(carphone=ogo*)(facsimiletelephonenumber=ogo*)' +
'(homefacsimiletelephonenumber=ogo*)(otherphone=ogo*)' +
'(otherfacsimiletelephonenumber=ogo*)(internationalisdnnumber=ogo*)' +
'(pager=ogo*)(radio=ogo*)(telex=ogo*)(assistantphone=ogo*)' +
'(companyphone=ogo*)(callbackphone=ogo*)(tty=ogo*)(o=ogo*)(ou=ogo*)' +
'(roomnumber=ogo*)(title=ogo*)(businessrole=ogo*)(managername=ogo*)' +
'(assistantname=ogo*)(postaladdress=ogo*)(l=ogo*)(st=ogo*)' +
'(postofficebox=ogo*)(postalcode=ogo*)(c=ogo*)(homepostaladdress=ogo*)' +
'(mozillahomelocalityname=ogo*)(mozillahomestate=ogo*)' +
'(mozillahomepostalcode=ogo*)(mozillahomecountryname=ogo*)' +
'(otherpostaladdress=ogo*)(jpegphoto=ogo*)(usercertificate=ogo*)' +
'(labeleduri=ogo*)(displayname=ogo*)(spousename=ogo*)(note=ogo*)' +
'(anniversary=ogo*)(birthdate=ogo*)(mailer=ogo*)(fileas=ogo*)' +
'(category=ogo*)(calcaluri=ogo*)(calfburl=ogo*)(icscalendar=ogo*))'
return search(t, filter)
})
tap.test('GH-49 Client errors on bad attributes', function (t) {
const searchOpts = {
filter: 'cn=*ogo*',
scope: 'one',
attributes: 'dn'
}
return search(t, searchOpts)
})
tap.test('GH-55 Client emits connect multiple times', function (t) {
const c = ldap.createClient({
socketPath: t.context.socketPath
})
let count = 0
c.on('connect', function (socket) {
t.ok(socket)
count++
c.bind('cn=root', 'secret', function (err) {
t.error(err)
c.unbind(function () {
t.equal(count, 1)
t.end()
})
})
})
})

View File

@ -0,0 +1,47 @@
'use strict'
const { test } = require('tap')
const { MAX_MSGID } = require('../../../../lib/client/constants')
const geWindow = require('../../../../lib/client/message-tracker/ge-window')
test('comp > (ref in upper window) => true', async t => {
const ref = Math.floor(MAX_MSGID / 2) + 10
const comp = ref + 10
const result = geWindow(ref, comp)
t.equal(result, true)
})
test('comp < (ref in upper window) => false', async t => {
const ref = Math.floor(MAX_MSGID / 2) + 10
const comp = ref - 5
const result = geWindow(ref, comp)
t.equal(result, false)
})
test('comp > (ref in lower window) => true', async t => {
const ref = Math.floor(MAX_MSGID / 2) - 10
const comp = ref + 20
const result = geWindow(ref, comp)
t.equal(result, true)
})
test('comp < (ref in lower window) => false', async t => {
const ref = Math.floor(MAX_MSGID / 2) - 10
const comp = ref - 5
const result = geWindow(ref, comp)
t.equal(result, false)
})
test('(max === MAX_MSGID) && (comp > ref) => true', async t => {
const ref = MAX_MSGID - Math.floor(MAX_MSGID / 2)
const comp = ref + 1
const result = geWindow(ref, comp)
t.equal(result, true)
})
test('(max === MAX_MSGID) && (comp < ref) => false', async t => {
const ref = MAX_MSGID - Math.floor(MAX_MSGID / 2)
const comp = ref - 1
const result = geWindow(ref, comp)
t.equal(result, false)
})

View File

@ -0,0 +1,21 @@
'use strict'
const { test } = require('tap')
const { MAX_MSGID } = require('../../../../lib/client/constants')
const idGeneratorFactory = require('../../../../lib/client/message-tracker/id-generator')
test('starts at 0', async t => {
const nextID = idGeneratorFactory()
const currentID = nextID()
t.equal(currentID, 1)
})
test('handles wrapping around', async t => {
const nextID = idGeneratorFactory(MAX_MSGID - 2)
let currentID = nextID()
t.equal(currentID, MAX_MSGID - 1)
currentID = nextID()
t.equal(currentID, 1)
})

View File

@ -0,0 +1,201 @@
'use strict'
const tap = require('tap')
const messageTrackerFactory = require('../../../../lib/client/message-tracker/')
tap.test('options', t => {
t.test('requires an options object', async t => {
try {
messageTrackerFactory()
} catch (error) {
t.match(error, /options object is required/)
}
try {
messageTrackerFactory([])
} catch (error) {
t.match(error, /options object is required/)
}
try {
messageTrackerFactory('')
} catch (error) {
t.match(error, /options object is required/)
}
try {
messageTrackerFactory(42)
} catch (error) {
t.match(error, /options object is required/)
}
})
t.test('requires id to be a string', async t => {
try {
messageTrackerFactory({ id: {} })
} catch (error) {
t.match(error, /options\.id string is required/)
}
try {
messageTrackerFactory({ id: [] })
} catch (error) {
t.match(error, /options\.id string is required/)
}
try {
messageTrackerFactory({ id: 42 })
} catch (error) {
t.match(error, /options\.id string is required/)
}
})
t.test('requires parser to be an object', async t => {
try {
messageTrackerFactory({ id: 'foo', parser: 'bar' })
} catch (error) {
t.match(error, /options\.parser object is required/)
}
try {
messageTrackerFactory({ id: 'foo', parser: 42 })
} catch (error) {
t.match(error, /options\.parser object is required/)
}
try {
messageTrackerFactory({ id: 'foo', parser: [] })
} catch (error) {
t.match(error, /options\.parser object is required/)
}
})
t.end()
})
tap.test('.pending', t => {
t.test('returns 0 for no messages', async t => {
const tracker = messageTrackerFactory({ id: 'foo', parser: {} })
t.equal(tracker.pending, 0)
})
t.test('returns 1 for 1 message', async t => {
const tracker = messageTrackerFactory({ id: 'foo', parser: {} })
tracker.track({}, () => {})
t.equal(tracker.pending, 1)
})
t.end()
})
tap.test('#abandon', t => {
t.test('returns false if message does not exist', async t => {
const tracker = messageTrackerFactory({ id: 'foo', parser: {} })
const result = tracker.abandon(1)
t.equal(result, false)
})
t.test('returns true if message is abandoned', async t => {
const tracker = messageTrackerFactory({ id: 'foo', parser: {} })
tracker.track({}, {})
const result = tracker.abandon(1)
t.equal(result, true)
})
t.end()
})
tap.test('#fetch', t => {
t.test('returns handler for fetched message', async t => {
const tracker = messageTrackerFactory({ id: 'foo', parser: {} })
tracker.track({}, handler)
const { callback: fetched } = tracker.fetch(1)
t.equal(fetched, handler)
function handler () {}
})
t.test('returns handler for fetched abandoned message', async t => {
const tracker = messageTrackerFactory({ id: 'foo', parser: {} })
tracker.track({}, handler)
tracker.track({ abandon: 'message' }, () => {})
tracker.abandon(1)
const { callback: fetched } = tracker.fetch(1)
t.equal(fetched, handler)
function handler () {}
})
t.test('returns null when message does not exist', async t => {
const tracker = messageTrackerFactory({ id: 'foo', parser: {} })
const fetched = tracker.fetch(1)
t.equal(fetched, null)
})
t.end()
})
tap.test('#purge', t => {
t.test('invokes cb for each tracked message', async t => {
t.plan(4)
let count = 0
const tracker = messageTrackerFactory({ id: 'foo', parser: {} })
tracker.track({}, handler1)
tracker.track({}, handler2)
tracker.purge(cb)
function cb (msgID, handler) {
if (count === 0) {
t.equal(msgID, 1)
t.equal(handler, handler1)
count += 1
return
}
t.equal(msgID, 2)
t.equal(handler, handler2)
}
function handler1 () {}
function handler2 () {}
})
t.end()
})
tap.test('#remove', t => {
t.test('removes from the current track', async t => {
const tracker = messageTrackerFactory({ id: 'foo', parser: {} })
tracker.track({}, () => {})
tracker.remove(1)
t.equal(tracker.pending, 0)
})
// Not a great test. It exercises the desired code path, but we probably
// should expose some insight into the abandoned track.
t.test('removes from the abandoned track', async t => {
const tracker = messageTrackerFactory({ id: 'foo', parser: {} })
tracker.track({}, () => {})
tracker.track({ abandon: 'message' }, () => {})
tracker.abandon(1)
tracker.remove(1)
t.equal(tracker.pending, 1)
})
t.end()
})
tap.test('#track', t => {
t.test('add messageId and tracks message', async t => {
const tracker = messageTrackerFactory({ id: 'foo', parser: {} })
const msg = {}
tracker.track(msg, handler)
t.same(msg, { messageId: 1 })
const { callback: cb } = tracker.fetch(1)
t.equal(cb, handler)
function handler () {}
})
t.end()
})

View File

@ -0,0 +1,64 @@
'use strict'
const { test } = require('tap')
const { MAX_MSGID } = require('../../../../lib/client/constants')
const purgeAbandoned = require('../../../../lib/client/message-tracker/purge-abandoned')
test('clears queue if only one message present', async t => {
t.plan(3)
const abandoned = new Map()
abandoned.set(1, { age: 2, cb })
purgeAbandoned(2, abandoned)
t.equal(abandoned.size, 0)
function cb (err) {
t.equal(err.name, 'AbandonedError')
t.equal(err.message, 'client request abandoned')
}
})
test('clears queue if multiple messages present', async t => {
t.plan(5)
const abandoned = new Map()
abandoned.set(1, { age: 2, cb })
abandoned.set(2, { age: 3, cb })
purgeAbandoned(4, abandoned)
t.equal(abandoned.size, 0)
function cb (err) {
t.equal(err.name, 'AbandonedError')
t.equal(err.message, 'client request abandoned')
}
})
test('message id has wrappred around', async t => {
t.plan(3)
const abandoned = new Map()
abandoned.set(MAX_MSGID - 1, { age: MAX_MSGID, cb })
// The "abandon" message was sent with an id of "MAX_MSGID". So the message
// that is triggering the purge was the "first" message in the new sequence
// of message identifiers.
purgeAbandoned(1, abandoned)
t.equal(abandoned.size, 0)
function cb (err) {
t.equal(err.name, 'AbandonedError')
t.equal(err.message, 'client request abandoned')
}
})
test('does not clear if window not met', async t => {
t.plan(1)
const abandoned = new Map()
abandoned.set(1, { age: 2, cb })
purgeAbandoned(1, abandoned)
t.equal(abandoned.size, 1)
function cb () {
t.fail('should not be invoked')
}
})

View File

@ -0,0 +1,82 @@
'use strict'
const { test } = require('tap')
const enqueue = require('../../../../lib/client/request-queue/enqueue')
test('rejects new requests if size is exceeded', async t => {
const q = { _queue: { size: 5 }, size: 5 }
const result = enqueue.call(q, 'foo', 'bar', {}, {})
t.notOk(result)
})
test('rejects new requests if queue is frozen', async t => {
const q = { _queue: { size: 0 }, size: 5, _frozen: true }
const result = enqueue.call(q, 'foo', 'bar', {}, {})
t.notOk(result)
})
test('adds a request and returns if no timeout', async t => {
const q = {
_queue: {
size: 0,
add (obj) {
t.same(obj, {
message: 'foo',
expect: 'bar',
emitter: 'baz',
cb: 'bif'
})
}
},
_frozen: false,
timeout: 0
}
const result = enqueue.call(q, 'foo', 'bar', 'baz', 'bif')
t.ok(result)
})
test('adds a request and returns timer not set', async t => {
const q = {
_queue: {
size: 0,
add (obj) {
t.same(obj, {
message: 'foo',
expect: 'bar',
emitter: 'baz',
cb: 'bif'
})
}
},
_frozen: false,
timeout: 100,
_timer: null
}
const result = enqueue.call(q, 'foo', 'bar', 'baz', 'bif')
t.ok(result)
})
test('adds a request, returns true, and clears queue', t => {
// Must not be an async test due to an internal `setTimeout`
t.plan(4)
const q = {
_queue: {
size: 0,
add (obj) {
t.same(obj, {
message: 'foo',
expect: 'bar',
emitter: 'baz',
cb: 'bif'
})
}
},
_frozen: false,
timeout: 5,
_timer: 123,
freeze () { t.pass() },
purge () { t.pass() }
}
const result = enqueue.call(q, 'foo', 'bar', 'baz', 'bif')
t.ok(result)
})

View File

@ -0,0 +1,51 @@
'use strict'
const { test } = require('tap')
const flush = require('../../../../lib/client/request-queue/flush')
test('clears timer', async t => {
t.plan(2)
const q = {
_timer: 123,
_queue: {
values () {
return []
},
clear () {
t.pass()
}
}
}
flush.call(q)
t.equal(q._timer, null)
})
test('invokes callback with parameters', async t => {
t.plan(6)
const req = {
message: 'foo',
expect: 'bar',
emitter: 'baz',
cb: theCB
}
const q = {
_timer: 123,
_queue: {
values () {
return [req]
},
clear () {
t.pass()
}
}
}
flush.call(q, (message, expect, emitter, cb) => {
t.equal(message, 'foo')
t.equal(expect, 'bar')
t.equal(emitter, 'baz')
t.equal(cb, theCB)
})
t.equal(q._timer, null)
function theCB () {}
})

View File

@ -0,0 +1,18 @@
'use strict'
const { test } = require('tap')
const purge = require('../../../../lib/client/request-queue/purge')
test('flushes the queue with timeout errors', async t => {
t.plan(3)
const q = {
flush (func) {
func('a', 'b', 'c', (err) => {
t.ok(err)
t.equal(err.name, 'TimeoutError')
t.equal(err.message, 'request queue timeout')
})
}
}
purge.call(q)
})

16
node_modules/ldapjs/test/messages/parser.test.js generated vendored Normal file
View File

@ -0,0 +1,16 @@
'use strict'
const { test } = require('tap')
const { Parser } = require('../../lib')
test('wrong protocol error', function (t) {
const p = new Parser()
p.once('error', function (err) {
t.ok(err)
t.end()
})
// Send some bogus data to incur an error
p.write(Buffer.from([16, 1, 4]))
})

471
node_modules/ldapjs/test/server.test.js generated vendored Normal file
View File

@ -0,0 +1,471 @@
'use strict'
const net = require('net')
const tap = require('tap')
const vasync = require('vasync')
const vm = require('node:vm')
const { getSock } = require('./utils')
const ldap = require('../lib')
const SERVER_PORT = process.env.SERVER_PORT || 1389
const SUFFIX = 'dc=test'
tap.beforeEach(function (t) {
// We do not need a `.afterEach` to clean up the sock files because that
// is done when the server is destroyed.
t.context.sock = getSock()
})
tap.test('basic create', function (t) {
const server = ldap.createServer()
t.ok(server)
t.end()
})
tap.test('connection count', function (t) {
const server = ldap.createServer()
t.ok(server)
server.listen(0, '127.0.0.1', function () {
t.ok(true, 'server listening on ' + server.url)
server.getConnections(function (err, count) {
t.error(err)
t.equal(count, 0)
const client = ldap.createClient({ url: server.url })
client.on('connect', function () {
t.ok(true, 'client connected')
server.getConnections(function (err, count) {
t.error(err)
t.equal(count, 1)
client.unbind()
server.close(() => t.end())
})
})
})
})
})
tap.test('properties', function (t) {
const server = ldap.createServer()
t.equal(server.name, 'LDAPServer')
// TODO: better test
server.maxConnections = 10
t.equal(server.maxConnections, 10)
t.equal(server.url, null, 'url empty before bind')
// listen on a random port so we have a url
server.listen(0, '127.0.0.1', function () {
t.ok(server.url)
server.close(() => t.end())
})
})
tap.test('IPv6 URL is formatted correctly', function (t) {
const server = ldap.createServer()
t.equal(server.url, null, 'url empty before bind')
server.listen(0, '::1', function () {
t.ok(server.url)
t.equal(server.url, 'ldap://[::1]:' + server.port)
server.close(() => t.end())
})
})
tap.test('listen on unix/named socket', function (t) {
const server = ldap.createServer()
server.listen(t.context.sock, function () {
t.ok(server.url)
t.equal(server.url.split(':')[0], 'ldapi')
server.close(() => t.end())
})
})
tap.test('listen on static port', function (t) {
const server = ldap.createServer()
server.listen(SERVER_PORT, '127.0.0.1', function () {
const addr = server.address()
t.equal(addr.port, parseInt(SERVER_PORT, 10))
t.equal(server.url, `ldap://127.0.0.1:${SERVER_PORT}`)
server.close(() => t.end())
})
})
tap.test('listen on ephemeral port', function (t) {
const server = ldap.createServer()
server.listen(0, '127.0.0.1', function () {
const addr = server.address()
t.ok(addr.port > 0)
t.ok(addr.port < 65535)
server.close(() => t.end())
})
})
tap.test('route order', function (t) {
function generateHandler (response) {
const func = function handler (req, res, next) {
res.send({
dn: response,
attributes: { }
})
res.end()
return next()
}
return func
}
const server = ldap.createServer()
const sock = t.context.sock
const dnShort = SUFFIX
const dnMed = 'dc=sub,' + SUFFIX
const dnLong = 'dc=long,dc=sub,' + SUFFIX
// Mount routes out of order
server.search(dnMed, generateHandler(dnMed))
server.search(dnShort, generateHandler(dnShort))
server.search(dnLong, generateHandler(dnLong))
server.listen(sock, function () {
t.ok(true, 'server listen')
const client = ldap.createClient({ socketPath: sock })
client.on('connect', () => {
vasync.forEachParallel({
func: runSearch,
inputs: [dnShort, dnMed, dnLong]
}, function (err) {
t.error(err)
client.unbind()
server.close(() => t.end())
})
})
function runSearch (value, cb) {
client.search(value, '(objectclass=*)', function (err, res) {
t.error(err)
t.ok(res)
res.on('searchEntry', function (entry) {
t.equal(entry.dn.toString(), value)
})
res.on('end', function () {
cb()
})
})
}
})
})
tap.test('route absent', function (t) {
const server = ldap.createServer()
const DN_ROUTE = 'dc=base'
const DN_MISSING = 'dc=absent'
server.bind(DN_ROUTE, function (req, res, next) {
res.end()
return next()
})
server.listen(t.context.sock, function () {
t.ok(true, 'server startup')
vasync.parallel({
funcs: [
function presentBind (cb) {
const clt = ldap.createClient({ socketPath: t.context.sock })
clt.bind(DN_ROUTE, '', function (err) {
t.notOk(err)
clt.unbind()
cb()
})
},
function absentBind (cb) {
const clt = ldap.createClient({ socketPath: t.context.sock })
clt.bind(DN_MISSING, '', function (err) {
t.ok(err)
t.equal(err.code, ldap.LDAP_NO_SUCH_OBJECT)
clt.unbind()
cb()
})
}
]
}, function (err) {
t.notOk(err)
server.close(() => t.end())
})
})
})
tap.test('route unbind', function (t) {
const server = ldap.createServer()
server.unbind(function (req, res, next) {
t.ok(true, 'server unbind successful')
res.end()
return next()
})
server.listen(t.context.sock, function () {
t.ok(true, 'server startup')
const client = ldap.createClient({ socketPath: t.context.sock })
client.bind('', '', function (err) {
t.error(err, 'client bind error')
client.unbind(function (err) {
t.error(err, 'client unbind error')
server.close(() => t.end())
})
})
})
})
tap.test('bind/unbind identity anonymous', function (t) {
const server = ldap.createServer({
connectionRouter: function (c) {
server.newConnection(c)
server.emit('testconnection', c)
}
})
server.unbind(function (req, res, next) {
t.ok(true, 'server unbind successful')
res.end()
return next()
})
server.bind('', function (req, res, next) {
t.ok(true, 'server bind successful')
res.end()
return next()
})
const anonDN = ldap.parseDN('cn=anonymous')
server.listen(t.context.sock, function () {
t.ok(true, 'server startup')
const client = ldap.createClient({ socketPath: t.context.sock })
server.once('testconnection', (c) => {
t.ok(anonDN.equals(c.ldap.bindDN), 'pre bind dn is correct')
client.bind('', '', function (err) {
t.error(err, 'client anon bind error')
t.ok(anonDN.equals(c.ldap.bindDN), 'anon bind dn is correct')
client.unbind(function (err) {
t.error(err, 'client anon unbind error')
t.ok(anonDN.equals(c.ldap.bindDN), 'anon unbind dn is correct')
server.close(() => t.end())
})
})
})
})
})
tap.test('does not crash on empty DN values', function (t) {
const server = ldap.createServer({
connectionRouter: function (c) {
server.newConnection(c)
server.emit('testconnection', c)
}
})
server.listen(t.context.sock, function () {
const client = ldap.createClient({ socketPath: t.context.sock })
server.once('testconnection', () => {
client.bind('', 'pw', function (err) {
t.ok(err, 'blank bind dn throws error')
client.unbind(function () {
server.close(() => t.end())
})
})
})
})
})
tap.test('bind/unbind identity user', function (t) {
const server = ldap.createServer({
connectionRouter: function (c) {
server.newConnection(c)
server.emit('testconnection', c)
}
})
server.unbind(function (req, res, next) {
t.ok(true, 'server unbind successful')
res.end()
return next()
})
server.bind('', function (req, res, next) {
t.ok(true, 'server bind successful')
res.end()
return next()
})
const anonDN = ldap.parseDN('cn=anonymous')
const testDN = ldap.parseDN('cn=anotheruser')
server.listen(t.context.sock, function () {
t.ok(true, 'server startup')
const client = ldap.createClient({ socketPath: t.context.sock })
server.once('testconnection', (c) => {
t.ok(anonDN.equals(c.ldap.bindDN), 'pre bind dn is correct')
client.bind(testDN.toString(), 'somesecret', function (err) {
t.error(err, 'user bind error')
t.ok(testDN.equals(c.ldap.bindDN), 'user bind dn is correct')
// check rebinds too
client.bind('', '', function (err) {
t.error(err, 'client anon bind error')
t.ok(anonDN.equals(c.ldap.bindDN), 'anon bind dn is correct')
// user rebind
client.bind(testDN.toString(), 'somesecret', function (err) {
t.error(err, 'user bind error')
t.ok(testDN.equals(c.ldap.bindDN), 'user rebind dn is correct')
client.unbind(function (err) {
t.error(err, 'user unbind error')
t.ok(anonDN.equals(c.ldap.bindDN), 'user unbind dn is correct')
server.close(() => t.end())
})
})
})
})
})
})
})
tap.test('strict routing', function (t) {
const testDN = 'cn=valid'
let clt
let server
const sock = t.context.sock
vasync.pipeline({
funcs: [
function setup (_, cb) {
server = ldap.createServer({})
// invalid DNs would go to default handler
server.search('', function (req, res, next) {
t.ok(req.dn)
t.equal(typeof (req.dn), 'object')
t.equal(req.dn.toString(), testDN)
res.end()
next()
})
server.listen(sock, function () {
t.ok(true, 'server startup')
clt = ldap.createClient({
socketPath: sock
})
cb()
})
},
function testGood (_, cb) {
clt.search(testDN, { scope: 'base' }, function (err, res) {
t.error(err)
res.once('error', function (err2) {
t.error(err2)
cb(err2)
})
res.once('end', function (result) {
t.ok(result, 'accepted invalid dn')
cb()
})
})
}
]
}, function (err) {
t.error(err)
if (clt) {
clt.destroy()
}
server.close(() => t.end())
})
})
tap.test('close accept a callback', function (t) {
const server = ldap.createServer()
// callback is called when the server is closed
server.listen(0, function (err) {
t.error(err)
server.close(function (err) {
t.error(err)
t.end()
})
})
})
tap.test('close without error calls callback', function (t) {
const server = ldap.createServer()
// when the server is closed without error, the callback parameter is undefined
server.listen(1389, '127.0.0.1', function (err) {
t.error(err)
server.close(function (err) {
t.error(err)
t.end()
})
})
})
tap.test('close passes error to callback', function (t) {
const server = ldap.createServer()
// when the server is closed with an error, the error is the first parameter of the callback
server.close(function (err) {
t.ok(err)
t.end()
})
})
tap.test('multithreading support via external server', function (t) {
const serverOptions = { }
const server = ldap.createServer(serverOptions)
const fauxServer = net.createServer(serverOptions, (connection) => {
server.newConnection(connection)
})
fauxServer.log = serverOptions.log
fauxServer.ldap = {
config: serverOptions
}
t.ok(server)
fauxServer.listen(5555, '127.0.0.1', function () {
t.ok(true, 'server listening on ' + server.url)
t.ok(fauxServer)
const client = ldap.createClient({ url: 'ldap://127.0.0.1:5555' })
client.on('connect', function () {
t.ok(client)
client.unbind()
fauxServer.close(() => t.end())
})
})
})
tap.test('multithreading support via hook', function (t) {
const serverOptions = {
connectionRouter: (connection) => {
server.newConnection(connection)
}
}
const server = ldap.createServer(serverOptions)
const fauxServer = ldap.createServer(serverOptions)
t.ok(server)
fauxServer.listen(0, '127.0.0.1', function () {
t.ok(true, 'server listening on ' + server.url)
t.ok(fauxServer)
const client = ldap.createClient({ url: fauxServer.url })
client.on('connect', function () {
t.ok(client)
client.unbind()
fauxServer.close(() => t.end())
})
})
})
tap.test('cross-realm type checks', function (t) {
const server = ldap.createServer()
const ctx = vm.createContext({})
vm.runInContext(
'globalThis.search=function(){};\n' +
'globalThis.searches=[function(){}];'
, ctx)
server.search('', ctx.search)
server.search('', ctx.searches)
t.ok(server)
t.end()
})

58
node_modules/ldapjs/test/url.test.js generated vendored Normal file
View File

@ -0,0 +1,58 @@
'use strict'
const { test } = require('tap')
const { parseURL } = require('../lib')
test('parse empty', function (t) {
const u = parseURL('ldap:///')
t.equal(u.hostname, 'localhost')
t.equal(u.port, 389)
t.ok(!u.DN)
t.ok(!u.attributes)
t.equal(u.secure, false)
t.end()
})
test('parse hostname', function (t) {
const u = parseURL('ldap://example.com/')
t.equal(u.hostname, 'example.com')
t.equal(u.port, 389)
t.ok(!u.DN)
t.ok(!u.attributes)
t.equal(u.secure, false)
t.end()
})
test('parse host and port', function (t) {
const u = parseURL('ldap://example.com:1389/')
t.equal(u.hostname, 'example.com')
t.equal(u.port, 1389)
t.ok(!u.DN)
t.ok(!u.attributes)
t.equal(u.secure, false)
t.end()
})
test('parse full', function (t) {
const u = parseURL('ldaps://ldap.example.com:1389/dc=example%20,dc=com' +
'?cn,sn?sub?(cn=Babs%20Jensen)')
t.equal(u.secure, true)
t.equal(u.hostname, 'ldap.example.com')
t.equal(u.port, 1389)
t.equal(u.DN, 'dc=example ,dc=com')
t.ok(u.attributes)
t.equal(u.attributes.length, 2)
t.equal(u.attributes[0], 'cn')
t.equal(u.attributes[1], 'sn')
t.equal(u.scope, 'sub')
t.equal(u.filter.toString(), '(cn=Babs Jensen)')
t.end()
})
test('supports href', function (t) {
const u = parseURL('ldaps://ldap.example.com:1389/dc=example%20,dc=com?cn,sn?sub?(cn=Babs%20Jensen)')
t.equal(u.href, 'ldaps://ldap.example.com:1389/dc=example%20,dc=com?cn,sn?sub?(cn=Babs%20Jensen)')
t.end()
})

22
node_modules/ldapjs/test/utils.js generated vendored Normal file
View File

@ -0,0 +1,22 @@
'use strict'
const os = require('os')
const path = require('path')
const crypto = require('crypto')
function uuid () {
return crypto.randomBytes(16).toString('hex')
}
function getSock () {
if (process.platform === 'win32') {
return '\\\\.\\pipe\\' + uuid()
} else {
return path.join(os.tmpdir(), uuid())
}
}
module.exports = {
getSock,
uuid
}