'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() }) }) }) })