From 824dd2cb570344a2ad6674e364c4a325e5aadf1c Mon Sep 17 00:00:00 2001 From: James Sumners Date: Wed, 8 Mar 2023 17:03:41 -0500 Subject: [PATCH] Resolve issue #845 This issue resolves issue #845 by updating `@ldapjs/messages` to the latest version and adding a test that shows how the module should be used to evaluate search request scopes. --- package.json | 2 +- test/issue-845.test.js | 116 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 test/issue-845.test.js diff --git a/package.json b/package.json index c11431a..16cfcb0 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "@ldapjs/controls": "2.0.0", "@ldapjs/dn": "1.0.0", "@ldapjs/filter": "2.0.0", - "@ldapjs/messages": "1.0.0", + "@ldapjs/messages": "1.0.1", "@ldapjs/protocol": "^1.2.1", "abstract-logging": "^2.0.1", "assert-plus": "^1.0.0", diff --git a/test/issue-845.test.js b/test/issue-845.test.js new file mode 100644 index 0000000..6b32b9d --- /dev/null +++ b/test/issue-845.test.js @@ -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() + }) + }) +})