return promises in before/after each

This commit is contained in:
Tony Brix 2021-04-05 00:25:32 -05:00
parent f525cebb64
commit daf1eb8f43
4 changed files with 327 additions and 321 deletions

4
.taprc
View File

@ -1,6 +1,2 @@
esm: false
jsx: false
ts: false
files: files:
- 'test/**/*.test.js' - 'test/**/*.test.js'

View File

@ -14,320 +14,324 @@ const LDAP_CONNECT_TIMEOUT = process.env.LDAP_CONNECT_TIMEOUT || 0
const BIND_DN = 'cn=root' const BIND_DN = 'cn=root'
const BIND_PW = 'secret' const BIND_PW = 'secret'
tap.beforeEach((done, t) => { tap.beforeEach((t) => {
t.context.socketPath = getSock() return new Promise(resolve => {
t.context.server = ldap.createServer() t.context.socketPath = getSock()
t.context.server = ldap.createServer()
const server = t.context.server const server = t.context.server
server.bind(BIND_DN, function (req, res, next) { server.bind(BIND_DN, function (req, res, next) {
if (req.credentials !== BIND_PW) { return next(new ldap.InvalidCredentialsError('Invalid password')) } if (req.credentials !== BIND_PW) { return next(new ldap.InvalidCredentialsError('Invalid password')) }
res.end()
return next()
})
server.add(SUFFIX, function (req, res, next) {
res.end()
return next()
})
server.compare(SUFFIX, function (req, res, next) {
res.end(req.value === 'test')
return next()
})
server.del(SUFFIX, function (req, res, next) {
res.end()
return next()
})
// LDAP whoami
server.exop('1.3.6.1.4.1.4203.1.11.3', function (req, res, next) {
res.value = 'u:xxyyz@EXAMPLE.NET'
res.end()
return next()
})
server.modify(SUFFIX, function (req, res, next) {
res.end()
return next()
})
server.modifyDN(SUFFIX, function (req, res, next) {
res.end()
return next()
})
server.modifyDN('cn=issue-480', function (req, res, next) {
assert(req.newRdn.toString().length > 132)
res.end()
return next()
})
server.search('dc=slow', function (req, res, next) {
res.send({
dn: 'dc=slow',
attributes: {
you: 'wish',
this: 'was',
faster: '.'
}
})
setTimeout(function () {
res.end() res.end()
next() return next()
}, 250) })
})
server.search('dc=timeout', function () { server.add(SUFFIX, function (req, res, next) {
// Cause the client to timeout by not sending a response. res.end()
}) return next()
})
server.search(SUFFIX, function (req, res, next) { server.compare(SUFFIX, function (req, res, next) {
if (req.dn.equals('cn=ref,' + SUFFIX)) { res.end(req.value === 'test')
res.send(res.createSearchReference('ldap://localhost')) return next()
} else if (req.dn.equals('cn=bin,' + SUFFIX)) { })
res.send(res.createSearchEntry({
objectName: req.dn,
attributes: {
'foo;binary': 'wr0gKyDCvCA9IMK+',
gb18030: Buffer.from([0xB5, 0xE7, 0xCA, 0xD3, 0xBB, 0xFA]),
objectclass: 'binary'
}
}))
} else {
const e = res.createSearchEntry({
objectName: req.dn,
attributes: {
cn: ['unit', 'test'],
SN: 'testy'
}
})
res.send(e)
res.send(e)
}
res.end() server.del(SUFFIX, function (req, res, next) {
return next() res.end()
}) return next()
})
server.search('cn=sizelimit', function (req, res, next) { // LDAP whoami
const sizeLimit = 200 server.exop('1.3.6.1.4.1.4203.1.11.3', function (req, res, next) {
for (let i = 0; i < 1000; i++) { res.value = 'u:xxyyz@EXAMPLE.NET'
if (req.sizeLimit > 0 && i >= req.sizeLimit) { res.end()
break return next()
} else if (i > sizeLimit) { })
res.end(ldap.LDAP_SIZE_LIMIT_EXCEEDED)
return next() server.modify(SUFFIX, function (req, res, next) {
} res.end()
return next()
})
server.modifyDN(SUFFIX, function (req, res, next) {
res.end()
return next()
})
server.modifyDN('cn=issue-480', function (req, res, next) {
assert(req.newRdn.toString().length > 132)
res.end()
return next()
})
server.search('dc=slow', function (req, res, next) {
res.send({ res.send({
dn: util.format('o=%d, cn=sizelimit', i), dn: 'dc=slow',
attributes: { attributes: {
o: [i], you: 'wish',
objectclass: ['pagedResult'] this: 'was',
faster: '.'
} }
}) })
} setTimeout(function () {
res.end() res.end()
return next() next()
}) }, 250)
})
server.search('cn=paged', function (req, res, next) { server.search('dc=timeout', function () {
const min = 0 // Cause the client to timeout by not sending a response.
const max = 1000 })
function sendResults (start, end) { server.search(SUFFIX, function (req, res, next) {
start = (start < min) ? min : start if (req.dn.equals('cn=ref,' + SUFFIX)) {
end = (end > max || end < min) ? max : end res.send(res.createSearchReference('ldap://localhost'))
let i } else if (req.dn.equals('cn=bin,' + SUFFIX)) {
for (i = start; i < end; i++) { res.send(res.createSearchEntry({
objectName: req.dn,
attributes: {
'foo;binary': 'wr0gKyDCvCA9IMK+',
gb18030: Buffer.from([0xB5, 0xE7, 0xCA, 0xD3, 0xBB, 0xFA]),
objectclass: 'binary'
}
}))
} else {
const e = res.createSearchEntry({
objectName: req.dn,
attributes: {
cn: ['unit', 'test'],
SN: 'testy'
}
})
res.send(e)
res.send(e)
}
res.end()
return next()
})
server.search('cn=sizelimit', function (req, res, next) {
const sizeLimit = 200
for (let i = 0; i < 1000; i++) {
if (req.sizeLimit > 0 && i >= req.sizeLimit) {
break
} else if (i > sizeLimit) {
res.end(ldap.LDAP_SIZE_LIMIT_EXCEEDED)
return next()
}
res.send({ res.send({
dn: util.format('o=%d, cn=paged', i), dn: util.format('o=%d, cn=sizelimit', i),
attributes: { attributes: {
o: [i], o: [i],
objectclass: ['pagedResult'] objectclass: ['pagedResult']
} }
}) })
} }
return i
}
let cookie = null
let pageSize = 0
req.controls.forEach(function (control) {
if (control.type === ldap.PagedResultsControl.OID) {
pageSize = control.value.size
cookie = control.value.cookie
}
})
if (cookie && Buffer.isBuffer(cookie)) {
// Do simple paging
let first = min
if (cookie.length !== 0) {
first = parseInt(cookie.toString(), 10)
}
const last = sendResults(first, first + pageSize)
let resultCookie
if (last < max) {
resultCookie = Buffer.from(last.toString())
} else {
resultCookie = Buffer.from('')
}
res.controls.push(new ldap.PagedResultsControl({
value: {
size: pageSize, // correctness not required here
cookie: resultCookie
}
}))
res.end()
next()
} else {
// don't allow non-paged searches for this test endpoint
next(new ldap.UnwillingToPerformError())
}
})
server.search('cn=sssvlv', function (req, res, next) {
const min = 0
const max = 100
const results = []
let o = 'aa'
for (let i = min; i < max; i++) {
results.push({
dn: util.format('o=%s, cn=sssvlv', o),
attributes: {
o: [o],
objectclass: ['sssvlvResult']
}
})
o = ((parseInt(o, 36) + 1).toString(36)).replace(/0/g, 'a')
}
function sendResults (start, end, sortBy, sortDesc) {
start = (start < min) ? min : start
end = (end > max || end < min) ? max : end
const sorted = results.sort((a, b) => {
if (a.attributes[sortBy][0] < b.attributes[sortBy][0]) {
return sortDesc ? 1 : -1
} else if (a.attributes[sortBy][0] > b.attributes[sortBy][0]) {
return sortDesc ? -1 : 1
}
return 0
})
for (let i = start; i < end; i++) {
res.send(sorted[i])
}
}
let sortBy = null
let sortDesc = null
let afterCount = null
let targetOffset = null
req.controls.forEach(function (control) {
if (control.type === ldap.ServerSideSortingRequestControl.OID) {
sortBy = control.value[0].attributeType
sortDesc = control.value[0].reverseOrder
}
if (control.type === ldap.VirtualListViewRequestControl.OID) {
afterCount = control.value.afterCount
targetOffset = control.value.targetOffset
}
})
if (sortBy) {
if (afterCount && targetOffset) {
sendResults(targetOffset - 1, (targetOffset + afterCount), sortBy, sortDesc)
} else {
sendResults(min, max, sortBy, sortDesc)
}
res.end()
next()
} else {
next(new ldap.UnwillingToPerformError())
}
})
server.search('cn=pagederr', function (req, res, next) {
let cookie = null
req.controls.forEach(function (control) {
if (control.type === ldap.PagedResultsControl.OID) {
cookie = control.value.cookie
}
})
if (cookie && Buffer.isBuffer(cookie) && cookie.length === 0) {
// send first "page"
res.send({
dn: util.format('o=result, cn=pagederr'),
attributes: {
o: 'result',
objectclass: ['pagedResult']
}
})
res.controls.push(new ldap.PagedResultsControl({
value: {
size: 2,
cookie: Buffer.from('a')
}
}))
res.end() res.end()
return next() return next()
} else { })
// send error instead of second page
res.end(ldap.LDAP_SIZE_LIMIT_EXCEEDED)
return next()
}
})
server.search('dc=empty', function (req, res, next) { server.search('cn=paged', function (req, res, next) {
res.send({ const min = 0
dn: 'dc=empty', const max = 1000
attributes: {
member: [], function sendResults (start, end) {
'member;range=0-1': ['cn=user1, dc=empty', 'cn=user2, dc=empty'] start = (start < min) ? min : start
end = (end > max || end < min) ? max : end
let i
for (i = start; i < end; i++) {
res.send({
dn: util.format('o=%d, cn=paged', i),
attributes: {
o: [i],
objectclass: ['pagedResult']
}
})
}
return i
}
let cookie = null
let pageSize = 0
req.controls.forEach(function (control) {
if (control.type === ldap.PagedResultsControl.OID) {
pageSize = control.value.size
cookie = control.value.cookie
}
})
if (cookie && Buffer.isBuffer(cookie)) {
// Do simple paging
let first = min
if (cookie.length !== 0) {
first = parseInt(cookie.toString(), 10)
}
const last = sendResults(first, first + pageSize)
let resultCookie
if (last < max) {
resultCookie = Buffer.from(last.toString())
} else {
resultCookie = Buffer.from('')
}
res.controls.push(new ldap.PagedResultsControl({
value: {
size: pageSize, // correctness not required here
cookie: resultCookie
}
}))
res.end()
next()
} else {
// don't allow non-paged searches for this test endpoint
next(new ldap.UnwillingToPerformError())
}
})
server.search('cn=sssvlv', function (req, res, next) {
const min = 0
const max = 100
const results = []
let o = 'aa'
for (let i = min; i < max; i++) {
results.push({
dn: util.format('o=%s, cn=sssvlv', o),
attributes: {
o: [o],
objectclass: ['sssvlvResult']
}
})
o = ((parseInt(o, 36) + 1).toString(36)).replace(/0/g, 'a')
}
function sendResults (start, end, sortBy, sortDesc) {
start = (start < min) ? min : start
end = (end > max || end < min) ? max : end
const sorted = results.sort((a, b) => {
if (a.attributes[sortBy][0] < b.attributes[sortBy][0]) {
return sortDesc ? 1 : -1
} else if (a.attributes[sortBy][0] > b.attributes[sortBy][0]) {
return sortDesc ? -1 : 1
}
return 0
})
for (let i = start; i < end; i++) {
res.send(sorted[i])
}
}
let sortBy = null
let sortDesc = null
let afterCount = null
let targetOffset = null
req.controls.forEach(function (control) {
if (control.type === ldap.ServerSideSortingRequestControl.OID) {
sortBy = control.value[0].attributeType
sortDesc = control.value[0].reverseOrder
}
if (control.type === ldap.VirtualListViewRequestControl.OID) {
afterCount = control.value.afterCount
targetOffset = control.value.targetOffset
}
})
if (sortBy) {
if (afterCount && targetOffset) {
sendResults(targetOffset - 1, (targetOffset + afterCount), sortBy, sortDesc)
} else {
sendResults(min, max, sortBy, sortDesc)
}
res.end()
next()
} else {
next(new ldap.UnwillingToPerformError())
} }
}) })
res.end()
return next()
})
server.search('cn=busy', function (req, res, next) { server.search('cn=pagederr', function (req, res, next) {
next(new ldap.BusyError('too much to do')) let cookie = null
}) req.controls.forEach(function (control) {
if (control.type === ldap.PagedResultsControl.OID) {
cookie = control.value.cookie
}
})
if (cookie && Buffer.isBuffer(cookie) && cookie.length === 0) {
// send first "page"
res.send({
dn: util.format('o=result, cn=pagederr'),
attributes: {
o: 'result',
objectclass: ['pagedResult']
}
})
res.controls.push(new ldap.PagedResultsControl({
value: {
size: 2,
cookie: Buffer.from('a')
}
}))
res.end()
return next()
} else {
// send error instead of second page
res.end(ldap.LDAP_SIZE_LIMIT_EXCEEDED)
return next()
}
})
server.search('', function (req, res, next) { server.search('dc=empty', function (req, res, next) {
if (req.dn.toString() === '') {
res.send({ res.send({
dn: '', dn: 'dc=empty',
attributes: { attributes: {
objectclass: ['RootDSE', 'top'] member: [],
'member;range=0-1': ['cn=user1, dc=empty', 'cn=user2, dc=empty']
} }
}) })
res.end() res.end()
} else { return next()
// Turn away any other requests (since '' is the fallthrough route) })
res.errorMessage = 'No tree found for: ' + req.dn.toString()
res.end(ldap.LDAP_NO_SUCH_OBJECT) server.search('cn=busy', function (req, res, next) {
} next(new ldap.BusyError('too much to do'))
return next() })
})
server.search('', function (req, res, next) {
server.unbind(function (req, res, next) { if (req.dn.toString() === '') {
res.end() res.send({
return next() dn: '',
}) attributes: {
objectclass: ['RootDSE', 'top']
server.listen(t.context.socketPath, function () { }
const client = ldap.createClient({ })
connectTimeout: parseInt(LDAP_CONNECT_TIMEOUT, 10), res.end()
socketPath: t.context.socketPath } else {
// Turn away any other requests (since '' is the fallthrough route)
res.errorMessage = 'No tree found for: ' + req.dn.toString()
res.end(ldap.LDAP_NO_SUCH_OBJECT)
}
return next()
})
server.unbind(function (req, res, next) {
res.end()
return next()
})
server.listen(t.context.socketPath, function () {
const client = ldap.createClient({
connectTimeout: parseInt(LDAP_CONNECT_TIMEOUT, 10),
socketPath: t.context.socketPath
})
t.context.client = client
client.on('connect', () => resolve())
}) })
t.context.client = client
client.on('connect', () => done())
}) })
}) })
tap.afterEach((done, t) => { tap.afterEach((t) => {
t.context.client.unbind((err) => { return new Promise(resolve => {
t.error(err) t.context.client.unbind((err) => {
t.context.server.close(() => done()) t.error(err)
t.context.server.close(() => resolve())
})
}) })
}) })

View File

@ -21,55 +21,62 @@ function search (t, options, callback) {
}) })
} }
tap.beforeEach((done, t) => { tap.beforeEach((t) => {
const suffix = `dc=${uuid()}` return new Promise((resolve, reject) => {
const server = ldap.createServer() const suffix = `dc=${uuid()}`
const server = ldap.createServer()
t.context.server = server t.context.server = server
t.context.socketPath = getSock() t.context.socketPath = getSock()
t.context.suffix = suffix t.context.suffix = suffix
server.bind('cn=root', function (req, res, next) { server.bind('cn=root', function (req, res, next) {
res.end() res.end()
return next() return next()
}) })
server.search(suffix, function (req, res) { server.search(suffix, function (req, res) {
const entry = { const entry = {
dn: 'cn=foo, ' + suffix, dn: 'cn=foo, ' + suffix,
attributes: { attributes: {
objectclass: ['person', 'top'], objectclass: ['person', 'top'],
cn: 'Pogo Stick', cn: 'Pogo Stick',
sn: 'Stick', sn: 'Stick',
givenname: 'ogo', givenname: 'ogo',
mail: uuid() + '@pogostick.org' mail: uuid() + '@pogostick.org'
}
} }
}
if (req.filter.matches(entry.attributes)) { res.send(entry) } if (req.filter.matches(entry.attributes)) { res.send(entry) }
res.end() res.end()
})
server.listen(t.context.socketPath, function () {
t.context.client = ldap.createClient({
socketPath: t.context.socketPath
}) })
t.context.client.on('connectError', (err) => { server.listen(t.context.socketPath, function () {
t.context.server.close(() => done(err)) t.context.client = ldap.createClient({
}) socketPath: t.context.socketPath
t.context.client.on('connect', (socket) => { })
t.context.socket = socket
done() 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((done, t) => { tap.afterEach((t) => {
if (!t.context.client) return done() return new Promise((resolve, reject) => {
t.context.client.unbind(() => { if (!t.context.client) return resolve()
t.context.server.close(done) t.context.client.unbind(() => {
t.context.server.close((err) => {
if (err) return reject(err)
resolve()
})
})
}) })
}) })

View File

@ -8,11 +8,10 @@ const ldap = require('../lib')
const SERVER_PORT = process.env.SERVER_PORT || 1389 const SERVER_PORT = process.env.SERVER_PORT || 1389
const SUFFIX = 'dc=test' const SUFFIX = 'dc=test'
tap.beforeEach(function (done, t) { tap.beforeEach(function (t) {
// We do not need a `.afterEach` to clean up the sock files because that // We do not need a `.afterEach` to clean up the sock files because that
// is done when the server is destroyed. // is done when the server is destroyed.
t.context.sock = getSock() t.context.sock = getSock()
done()
}) })
tap.test('basic create', function (t) { tap.test('basic create', function (t) {