ldap-result.test.js 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. 'use strict'
  2. const tap = require('tap')
  3. const warning = require('./deprecations')
  4. const { resultCodes, operations } = require('@ldapjs/protocol')
  5. const { BerReader } = require('@ldapjs/asn1')
  6. const {
  7. addResponseBasicBytes,
  8. addResponseNoSuchObjectBytes,
  9. addResponseReferralsBytes,
  10. extensionDisconnectionNotificationResponseBytes
  11. } = require('./messages/_fixtures/message-byte-arrays')
  12. const RECOGNIZED_OIDS = require('./messages/extension-utils/recognized-oids')
  13. const LdapResult = require('./ldap-result')
  14. const ExtensionResponse = require('./messages/extension-response')
  15. // Silence the standard warning logs. We will test the messages explicitly.
  16. process.removeAllListeners('warning')
  17. tap.test('constructor', t => {
  18. t.test('no args', async t => {
  19. const res = new LdapResult()
  20. t.equal(res.status, 0)
  21. t.equal(res.matchedDN, '')
  22. t.strictSame(res.referrals, [])
  23. t.equal(res.diagnosticMessage, '')
  24. })
  25. t.test('emits warning for abandonID', t => {
  26. process.on('warning', handler)
  27. t.teardown(async () => {
  28. process.removeListener('warning', handler)
  29. warning.emitted.set('LDAP_MESSAGE_DEP_004', false)
  30. })
  31. const res = new LdapResult({
  32. errorMessage: 'foo'
  33. })
  34. t.ok(res)
  35. function handler (error) {
  36. t.equal(
  37. error.message,
  38. 'errorMessage is deprecated. Use diagnosticMessage instead.'
  39. )
  40. t.end()
  41. }
  42. })
  43. t.test('with options', async t => {
  44. const res = new LdapResult({
  45. status: 1,
  46. matchedDN: 'foo',
  47. referrals: ['foo.example.com'],
  48. diagnosticMessage: 'bar'
  49. })
  50. t.equal(res.status, 1)
  51. t.equal(res.matchedDN, 'foo')
  52. t.strictSame(res.referrals, ['foo.example.com'])
  53. t.equal(res.diagnosticMessage, 'bar')
  54. })
  55. t.end()
  56. })
  57. tap.test('.diagnosticMessage', t => {
  58. t.test('sets and gets', async t => {
  59. const res = new LdapResult()
  60. t.equal(res.diagnosticMessage, '')
  61. res.diagnosticMessage = 'foo'
  62. t.equal(res.diagnosticMessage, 'foo')
  63. })
  64. t.end()
  65. })
  66. tap.test('.matchedDN', t => {
  67. t.test('sets and gets', async t => {
  68. const res = new LdapResult()
  69. t.equal(res.matchedDN, '')
  70. res.matchedDN = 'foo'
  71. t.equal(res.matchedDN, 'foo')
  72. })
  73. t.end()
  74. })
  75. tap.test('.pojo', t => {
  76. t.test('returns a plain JavaScript object', async t => {
  77. const res = new LdapResult()
  78. t.strictSame(res.pojo, {
  79. status: 0,
  80. matchedDN: '',
  81. diagnosticMessage: '',
  82. referrals: []
  83. })
  84. })
  85. t.test('returns a plain JavaScript object from subclass', async t => {
  86. class Foo extends LdapResult {
  87. _pojo (obj) {
  88. obj.foo = 'foo'
  89. return obj
  90. }
  91. }
  92. const res = new Foo()
  93. t.strictSame(res.pojo, {
  94. status: 0,
  95. matchedDN: '',
  96. diagnosticMessage: '',
  97. referrals: [],
  98. foo: 'foo'
  99. })
  100. })
  101. t.end()
  102. })
  103. tap.test('.referrals', t => {
  104. t.test('gets', async t => {
  105. const res = new LdapResult({ referrals: ['foo'] })
  106. t.strictSame(res.referrals, ['foo'])
  107. })
  108. t.end()
  109. })
  110. tap.test('.status', t => {
  111. t.test('sets and gets', async t => {
  112. const res = new LdapResult()
  113. t.equal(res.status, 0)
  114. res.status = 1
  115. t.equal(res.status, 1)
  116. })
  117. t.end()
  118. })
  119. tap.test('.type', t => {
  120. t.test('gets', async t => {
  121. const res = new LdapResult()
  122. t.equal(res.type, 'LdapResult')
  123. })
  124. t.end()
  125. })
  126. tap.test('addReferral', t => {
  127. t.test('adds to existing list', async t => {
  128. const res = new LdapResult({ referrals: ['foo'] })
  129. t.strictSame(res.referrals, ['foo'])
  130. res.addReferral('bar')
  131. t.strictSame(res.referrals, ['foo', 'bar'])
  132. })
  133. t.end()
  134. })
  135. tap.test('_toBer', t => {
  136. t.test('returns basic bytes', async t => {
  137. const res = new LdapResult({
  138. protocolOp: operations.LDAP_RES_ADD,
  139. messageId: 2
  140. })
  141. const ber = res.toBer()
  142. const expected = Buffer.from(addResponseBasicBytes)
  143. t.equal(expected.compare(ber.buffer), 0)
  144. })
  145. t.test('returns bytes with referrals', async t => {
  146. const res = new LdapResult({
  147. protocolOp: operations.LDAP_RES_ADD,
  148. messageId: 3,
  149. status: resultCodes.REFERRAL,
  150. diagnosticMessage: 'This server is read-only. Try a different one.',
  151. referrals: [
  152. 'ldap://alternate1.example.com:389/uid=jdoe,ou=Remote,dc=example,dc=com',
  153. 'ldap://alternate2.example.com:389/uid=jdoe,ou=Remote,dc=example,dc=com'
  154. ]
  155. })
  156. const ber = res.toBer()
  157. const expected = Buffer.from(addResponseReferralsBytes)
  158. t.equal(expected.compare(ber.buffer), 0)
  159. })
  160. t.test('hands off to _writeResponse', async t => {
  161. const res = new ExtensionResponse({
  162. protocolOp: operations.LDAP_RES_EXTENSION,
  163. messageId: 0,
  164. status: resultCodes.UNAVAILABLE,
  165. diagnosticMessage: 'The Directory Server is shutting down',
  166. referrals: [],
  167. responseName: RECOGNIZED_OIDS.get('DISCONNECTION_NOTIFICATION')
  168. })
  169. const ber = res.toBer()
  170. const expected = Buffer.from(extensionDisconnectionNotificationResponseBytes)
  171. t.equal(expected.compare(ber.buffer), 0)
  172. })
  173. t.end()
  174. })
  175. tap.test('#parseToPojo', t => {
  176. t.test('throws because not implemented', async t => {
  177. const expected = Error('Use LdapMessage.parse, or a specific message type\'s parseToPojo, instead.')
  178. t.throws(
  179. () => LdapResult.parseToPojo(),
  180. expected
  181. )
  182. })
  183. t.end()
  184. })
  185. tap.test('#_parseToPojo', async t => {
  186. t.test('throws if protocol op is wrong', async t => {
  187. const bytes = addResponseBasicBytes.slice(5)
  188. bytes[0] = 0x68
  189. const berReader = new BerReader(Buffer.from(bytes))
  190. t.throws(
  191. () => LdapResult._parseToPojo({
  192. opCode: operations.LDAP_RES_ADD,
  193. berReader
  194. }),
  195. Error('found wrong protocol operation: 0x68')
  196. )
  197. })
  198. t.test('parses a basic object', async t => {
  199. const bytes = addResponseBasicBytes.slice(5)
  200. const berReader = new BerReader(Buffer.from(bytes))
  201. const pojo = { foo: 'foo' }
  202. LdapResult._parseToPojo({
  203. opCode: operations.LDAP_RES_ADD,
  204. berReader,
  205. pojo
  206. })
  207. t.strictSame(pojo, {
  208. status: 0,
  209. matchedDN: '',
  210. diagnosticMessage: '',
  211. referrals: [],
  212. foo: 'foo'
  213. })
  214. })
  215. t.test('parses object with matched dn and diagnostic message', async t => {
  216. const bytes = addResponseNoSuchObjectBytes.slice(6)
  217. const berReader = new BerReader(Buffer.from(bytes))
  218. const pojo = LdapResult._parseToPojo({
  219. opCode: operations.LDAP_RES_ADD,
  220. berReader
  221. })
  222. t.strictSame(pojo, {
  223. status: resultCodes.NO_SUCH_OBJECT,
  224. referrals: [],
  225. matchedDN: 'ou=People, dc=example, dc=com',
  226. diagnosticMessage: [
  227. 'Entry uid=missing1, ou=missing2, ou=People, dc=example, dc=com cannot',
  228. ' be created because its parent does not exist.'
  229. ].join('')
  230. })
  231. })
  232. t.test('parses object with referrals', async t => {
  233. const bytes = addResponseReferralsBytes.slice(6)
  234. const berReader = new BerReader(Buffer.from(bytes))
  235. const pojo = LdapResult._parseToPojo({
  236. opCode: operations.LDAP_RES_ADD,
  237. berReader
  238. })
  239. t.strictSame(pojo, {
  240. status: resultCodes.REFERRAL,
  241. referrals: [
  242. 'ldap://alternate1.example.com:389/uid=jdoe,ou=Remote,dc=example,dc=com',
  243. 'ldap://alternate2.example.com:389/uid=jdoe,ou=Remote,dc=example,dc=com'
  244. ],
  245. matchedDN: '',
  246. diagnosticMessage: 'This server is read-only. Try a different one.'
  247. })
  248. })
  249. })