rdn.test.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. 'use strict'
  2. const tap = require('tap')
  3. const warning = require('./deprecations')
  4. const { BerReader } = require('@ldapjs/asn1')
  5. const RDN = require('./rdn')
  6. // Silence the standard warning logs. We will test the messages explicitly.
  7. process.removeAllListeners('warning')
  8. tap.test('equals', t => {
  9. t.test('false for non-rdn object', async t => {
  10. const rdn = new RDN()
  11. t.equal(rdn.equals({}), false)
  12. })
  13. t.test('false for size mis-match', async t => {
  14. const rdn1 = new RDN({ cn: 'foo' })
  15. const rdn2 = new RDN({ cn: 'foo', sn: 'bar' })
  16. t.equal(rdn1.equals(rdn2), false)
  17. })
  18. t.test('false for keys mis-match', async t => {
  19. const rdn1 = new RDN({ cn: 'foo' })
  20. const rdn2 = new RDN({ sn: 'bar' })
  21. t.equal(rdn1.equals(rdn2), false)
  22. })
  23. t.test('false for value mis-match', async t => {
  24. const rdn1 = new RDN({ cn: 'foo' })
  25. const rdn2 = new RDN({ cn: 'bar' })
  26. t.equal(rdn1.equals(rdn2), false)
  27. })
  28. t.test('true for match', async t => {
  29. const rdn1 = new RDN({ cn: 'foo' })
  30. const rdn2 = new RDN({ cn: 'foo' })
  31. t.equal(rdn1.equals(rdn2), true)
  32. })
  33. t.end()
  34. })
  35. tap.test('setAttribute', async t => {
  36. t.test('throws for bad name', async t => {
  37. const rdn = new RDN()
  38. t.throws(
  39. () => rdn.setAttribute({ name: 42 }),
  40. Error('name must be a string')
  41. )
  42. t.throws(
  43. () => rdn.setAttribute({ name: '3cn', value: 'foo' }),
  44. Error('attribute name must start with an ASCII alpha character or be a numeric OID')
  45. )
  46. })
  47. t.test('throws for bad value', async t => {
  48. const rdn = new RDN()
  49. t.throws(
  50. () => rdn.setAttribute({ name: 'cn', value: 42 }),
  51. Error('value must be a string')
  52. )
  53. })
  54. t.test('throws for options', async t => {
  55. const rdn = new RDN()
  56. t.throws(
  57. () => rdn.setAttribute({ name: 'cn', value: 'foo', options: 42 }),
  58. Error('options must be an object')
  59. )
  60. })
  61. t.test('sets an attribute with value', async t => {
  62. const rdn = new RDN()
  63. rdn.setAttribute({ name: 'cn', value: 'foo' })
  64. t.equal(rdn.getValue('cn'), 'foo')
  65. })
  66. t.test('options generates warning', t => {
  67. process.on('warning', handler)
  68. t.teardown(async () => {
  69. process.removeListener('warning', handler)
  70. warning.emitted.set('LDAP_DN_DEP_001', false)
  71. })
  72. const rdn = new RDN()
  73. rdn.setAttribute({ name: 'cn', value: 'foo', options: { foo: 'bar' } })
  74. function handler (error) {
  75. t.equal(error.message, 'attribute options is deprecated and are ignored')
  76. t.end()
  77. }
  78. })
  79. t.end()
  80. })
  81. tap.test('toString', t => {
  82. t.test('basic single value', async t => {
  83. const rdn = new RDN({ cn: 'foo' })
  84. t.equal(rdn.toString(), 'cn=foo')
  85. })
  86. t.test('escaped single value', async t => {
  87. const rdn = new RDN({ cn: ' foo, bar\n' })
  88. t.equal(rdn.toString(), 'cn=\\20foo\\2c bar\\0a')
  89. })
  90. t.test('basic multi-value', async t => {
  91. const rdn = new RDN({ cn: 'foo', sn: 'bar' })
  92. t.equal(rdn.toString(), 'cn=foo+sn=bar')
  93. })
  94. t.test('escaped multi-value', async t => {
  95. const rdn = new RDN({ cn: '#foo', sn: 'bar' })
  96. t.equal(rdn.toString(), 'cn=\\23foo+sn=bar')
  97. })
  98. t.test('recognizes encoded string values', async t => {
  99. const rdn = new RDN({
  100. cn: '#foo',
  101. '1.3.6.1.4.1.1466.0': '#04024869'
  102. })
  103. t.equal(rdn.toString(), 'cn=\\23foo+1.3.6.1.4.1.1466.0=#04024869')
  104. })
  105. t.test('encodes BerReader instances', async t => {
  106. const rdn = new RDN({
  107. cn: new BerReader(Buffer.from([0x04, 0x03, 0x66, 0x6f, 0x6f]))
  108. })
  109. t.equal(rdn.toString(), 'cn=#0403666f6f')
  110. })
  111. t.test('honors unescaped options', async t => {
  112. const rdn = new RDN({
  113. ou: '研发二组'
  114. })
  115. t.equal(rdn.toString({ unescaped: true }), 'ou=研发二组')
  116. })
  117. t.end()
  118. })
  119. tap.test('deprecations', t => {
  120. t.test('format', t => {
  121. process.on('warning', handler)
  122. t.teardown(async () => {
  123. process.removeListener('warning', handler)
  124. warning.emitted.set('LDAP_DN_DEP_002', false)
  125. })
  126. const rdn = new RDN({ cn: 'foo' })
  127. t.equal(rdn.format(), 'cn=foo')
  128. function handler (error) {
  129. t.equal(error.message, '.format() is deprecated. Use .toString() instead')
  130. t.end()
  131. }
  132. })
  133. t.test('set', t => {
  134. process.on('warning', handler)
  135. t.teardown(async () => {
  136. process.removeListener('warning', handler)
  137. warning.emitted.set('LDAP_DN_DEP_002', false)
  138. })
  139. const rdn = new RDN()
  140. rdn.set('cn', 'foo', { value: 'ignored' })
  141. function handler (error) {
  142. t.equal(error.message, '.set() is deprecated. Use .setAttribute() instead')
  143. t.end()
  144. }
  145. })
  146. t.end()
  147. })
  148. tap.test('#isRdn', t => {
  149. t.test('true for instance', async t => {
  150. const rdn = new RDN()
  151. t.equal(RDN.isRdn(rdn), true)
  152. })
  153. t.test('false for non-object', async t => {
  154. t.equal(RDN.isRdn(42), false)
  155. })
  156. t.test('false for bad object', async t => {
  157. const input = { bad: 'rdn', 'non-string-value': 42 }
  158. t.equal(RDN.isRdn(input), false)
  159. })
  160. t.test('true for rdn-like with name+value keys', async t => {
  161. const input = { name: 'cn', value: 'foo' }
  162. t.equal(RDN.isRdn(input), true)
  163. })
  164. t.test('true for pojo representation', async t => {
  165. const input = { cn: 'foo', sn: 'bar' }
  166. t.equal(RDN.isRdn(input), true)
  167. })
  168. t.test('true for pojo with BerReader', async t => {
  169. const input = {
  170. foo: new BerReader(Buffer.from([0x04, 0x03, 0x66, 0x6f, 0x6f]))
  171. }
  172. t.equal(RDN.isRdn(input), true)
  173. })
  174. t.end()
  175. })