Files
ldap-to-oauth2/node_modules/@ldapjs/dn/lib/utils/parse-string/index.js
2025-10-08 11:12:59 -04:00

106 lines
2.9 KiB
JavaScript

'use strict'
const readAttributePair = require('./read-attribute-pair')
/**
* @typedef {object} ParsedPojoRdn
* @property {string} name Either the name of an RDN attribute, or the
* equivalent numeric OID.
* @property {string | import('@ldapjs/asn1').BerReader} value The attribute
* value as a plain string, or a `BerReader` if the string value was an encoded
* hex string.
*/
/**
* Parse a string into a set of plain JavaScript object representations of
* RDNs.
*
* @example A plain string with multiple RDNs and multiple attribute assertions.
* const input = 'cn=foo+sn=bar,dc=example,dc=com
* const result = parseString(input)
* // [
* // { cn: 'foo', sn: 'bar' },
* // { dc: 'example' }
* // { dc: 'com' }
* // ]
*
* @param {string} input The RDN string to parse.
*
* @returns {ParsedPojoRdn[]}
*
* @throws When there is some problem parsing the RDN string.
*/
module.exports = function parseString (input) {
if (typeof input !== 'string') {
throw Error('input must be a string')
}
if (input.length === 0) {
// Short circuit because the input is an empty DN (i.e. "root DSE").
return []
}
const searchBuffer = Buffer.from(input, 'utf8')
const length = searchBuffer.byteLength
const rdns = []
let pos = 0
let rdn = {}
readRdnLoop:
while (pos <= length) {
if (pos === length) {
const char = searchBuffer[pos - 1]
/* istanbul ignore else */
if (char === 0x2b || char === 0x2c || char === 0x3b) {
throw Error('rdn string ends abruptly with character: ' + String.fromCharCode(char))
}
}
// Find the start of significant characters by skipping over any leading
// whitespace.
while (pos < length && searchBuffer[pos] === 0x20) {
pos += 1
}
const readAttrPairResult = readAttributePair({ searchBuffer, startPos: pos })
pos = readAttrPairResult.endPos
rdn = { ...rdn, ...readAttrPairResult.pair }
if (pos >= length) {
// We've reached the end of the string. So push the current RDN and stop.
rdns.push(rdn)
break
}
// Next, we need to determine if the next set of significant characters
// denotes another attribute pair for the current RDN, or is the indication
// of another RDN.
while (pos < length) {
const char = searchBuffer[pos]
// We don't need to skip whitespace before the separator because the
// attribute pair function has already advanced us past any such
// whitespace.
if (char === 0x2b) { // +
// We need to continue adding attribute pairs to the current RDN.
pos += 1
continue readRdnLoop
}
/* istanbul ignore else */
if (char === 0x2c || char === 0x3b) { // , or ;
// The current RDN has been fully parsed, so push it to the list,
// reset the collector, and start parsing the next RDN.
rdns.push(rdn)
rdn = {}
pos += 1
continue readRdnLoop
}
}
}
return rdns
}