wangzhibo
2026-05-09 53418655c061ac26f66058253f39200aced1ab1d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
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()
    })
  })
})