index.test.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. 'use strict'
  2. const tap = require('tap')
  3. const { BerReader } = require('@ldapjs/asn1')
  4. const parseString = require('./index')
  5. tap.test('throws for non-string input', async t => {
  6. const input = ['cn=foo']
  7. t.throws(
  8. () => parseString(input),
  9. 'input must be a string'
  10. )
  11. })
  12. tap.test('short circuits for root dse', async t => {
  13. t.same(parseString(''), [])
  14. })
  15. tap.test('parses basic single rdn', async t => {
  16. const input = 'cn=foo'
  17. const result = parseString(input)
  18. t.same(result, [{ cn: 'foo' }])
  19. })
  20. tap.test('skips leading whitespace', async t => {
  21. const input = ' cn=foo'
  22. const result = parseString(input)
  23. t.same(result, [{ cn: 'foo' }])
  24. })
  25. tap.test('parses basic multiple rdns', async t => {
  26. let input = 'dc=example,dc=com'
  27. let result = parseString(input)
  28. t.same(
  29. result,
  30. [
  31. { dc: 'example' },
  32. { dc: 'com' }
  33. ]
  34. )
  35. // RFC 2253 §4 separator is supported.
  36. input = 'dc=example;dc=com'
  37. result = parseString(input)
  38. t.same(
  39. result,
  40. [
  41. { dc: 'example' },
  42. { dc: 'com' }
  43. ]
  44. )
  45. })
  46. tap.test('handles multivalued rdn', async t => {
  47. const input = 'foo=bar+baz=bif'
  48. const result = parseString(input)
  49. t.same(result, [{ foo: 'bar', baz: 'bif' }])
  50. })
  51. tap.test('abruptly ending strings throw', async t => {
  52. const baseError = 'rdn string ends abruptly with character: '
  53. const tests = [
  54. { input: 'foo=bar+', expected: baseError + '+' },
  55. { input: 'foo=bar,', expected: baseError + ',' },
  56. { input: 'foo=bar;', expected: baseError + ';' }
  57. ]
  58. for (const test of tests) {
  59. t.throws(() => parseString(test.input), test.expected)
  60. }
  61. })
  62. tap.test('adds rdn with trailing whitespace', async t => {
  63. const input = 'foo=bar '
  64. const result = parseString(input)
  65. t.same(result, [{ foo: 'bar' }])
  66. })
  67. tap.test('parses rdn with attribute name in OID form', async t => {
  68. const input = '0.9.2342.19200300.100.1.25=Example'
  69. const result = parseString(input)
  70. t.same(result, [{ '0.9.2342.19200300.100.1.25': 'Example' }])
  71. })
  72. tap.test('throws for invalid attribute type name', async t => {
  73. let input = '3foo=bar'
  74. t.throws(
  75. () => parseString(input),
  76. 'invalid attribute type name: 3foo'
  77. )
  78. input = '1.2.3.abc=bar'
  79. t.throws(
  80. () => parseString(input),
  81. 'invalid attribute type name: 1.2.3.abc=bar'
  82. )
  83. input = 'føø=bar'
  84. t.throws(
  85. () => parseString(input),
  86. 'invalid attribute type name: føø'
  87. )
  88. })
  89. tap.test('throws for abrupt end', async t => {
  90. const input = 'foo=bar,'
  91. t.throws(
  92. () => parseString(input),
  93. 'rdn string ends abruptly with character: ,'
  94. )
  95. })
  96. tap.test('rfc 4514 §4 examples', async t => {
  97. const tests = [
  98. {
  99. input: 'UID=jsmith,DC=example,DC=net',
  100. expected: [{ UID: 'jsmith' }, { DC: 'example' }, { DC: 'net' }]
  101. },
  102. {
  103. input: 'OU=Sales+CN=J. Smith,DC=example,DC=net',
  104. expected: [
  105. { OU: 'Sales', CN: 'J. Smith' },
  106. { DC: 'example' },
  107. { DC: 'net' }
  108. ]
  109. },
  110. {
  111. input: 'CN=James \\"Jim\\" Smith\\, III,DC=example,DC=net',
  112. expected: [{ CN: 'James "Jim" Smith, III' }, { DC: 'example' }, { DC: 'net' }]
  113. },
  114. {
  115. input: 'CN=Before\\0dAfter,DC=example,DC=net',
  116. expected: [{ CN: 'Before\rAfter' }, { DC: 'example' }, { DC: 'net' }]
  117. },
  118. {
  119. checkBuffer: true,
  120. input: '1.3.6.1.4.1.1466.0=#04024869',
  121. expected: [{ '1.3.6.1.4.1.1466.0': new BerReader(Buffer.from([0x04, 0x02, 0x48, 0x69])) }]
  122. },
  123. {
  124. input: 'CN=Lu\\C4\\8Di\\C4\\87',
  125. expected: [{ CN: 'Lučić' }]
  126. }
  127. ]
  128. for (const test of tests) {
  129. const result = parseString(test.input)
  130. if (test.checkBuffer) {
  131. for (const [i, rdn] of test.expected.entries()) {
  132. for (const key of Object.keys(rdn)) {
  133. t.equal(
  134. rdn[key].buffer.compare(result[i][key].buffer),
  135. 0
  136. )
  137. }
  138. }
  139. } else {
  140. t.same(result, test.expected)
  141. }
  142. }
  143. })
  144. tap.test('rfc 2253 §5 examples', async t => {
  145. const tests = [
  146. {
  147. input: 'CN=Steve Kille,O=Isode Limited,C=GB',
  148. expected: [{ CN: 'Steve Kille' }, { O: 'Isode Limited' }, { C: 'GB' }]
  149. },
  150. {
  151. input: 'OU=Sales+CN=J. Smith,O=Widget Inc.,C=US',
  152. expected: [{ OU: 'Sales', CN: 'J. Smith' }, { O: 'Widget Inc.' }, { C: 'US' }]
  153. },
  154. {
  155. input: 'CN=L. Eagle,O=Sue\\, Grabbit and Runn,C=GB',
  156. expected: [{ CN: 'L. Eagle' }, { O: 'Sue, Grabbit and Runn' }, { C: 'GB' }]
  157. },
  158. {
  159. input: 'CN=Before\\0DAfter,O=Test,C=GB',
  160. expected: [{ CN: 'Before\rAfter' }, { O: 'Test' }, { C: 'GB' }]
  161. },
  162. {
  163. checkBuffer: true,
  164. input: '1.3.6.1.4.1.1466.0=#04024869,O=Test,C=GB',
  165. expected: [
  166. { '1.3.6.1.4.1.1466.0': new BerReader(Buffer.from([0x04, 0x02, 0x48, 0x69])) },
  167. { O: 'Test' },
  168. { C: 'GB' }
  169. ]
  170. },
  171. {
  172. input: 'SN=Lu\\C4\\8Di\\C4\\87',
  173. expected: [{ SN: 'Lučić' }]
  174. }
  175. ]
  176. for (const test of tests) {
  177. const result = parseString(test.input)
  178. if (test.checkBuffer) {
  179. for (const [i, rdn] of test.expected.entries()) {
  180. for (const key of Object.keys(rdn)) {
  181. if (typeof rdn[key] !== 'string') {
  182. t.equal(
  183. rdn[key].buffer.compare(result[i][key].buffer),
  184. 0
  185. )
  186. } else {
  187. t.equal(rdn[key], result[i][key])
  188. }
  189. }
  190. }
  191. } else {
  192. t.same(result, test.expected)
  193. }
  194. }
  195. })