First commit
This commit is contained in:
336
node_modules/@ldapjs/dn/lib/dn.js
generated
vendored
Normal file
336
node_modules/@ldapjs/dn/lib/dn.js
generated
vendored
Normal file
@ -0,0 +1,336 @@
|
||||
'use strict'
|
||||
|
||||
const warning = require('./deprecations')
|
||||
const RDN = require('./rdn')
|
||||
const parseString = require('./utils/parse-string')
|
||||
|
||||
/**
|
||||
* Implements distinguished name strings as described in
|
||||
* https://www.rfc-editor.org/rfc/rfc4514 as an object.
|
||||
* This is the primary implementation for parsing and generating DN strings.
|
||||
*
|
||||
* @example
|
||||
* const dn = new DN({rdns: [{cn: 'jdoe', givenName: 'John'}] })
|
||||
* dn.toString() // 'cn=jdoe+givenName=John'
|
||||
*/
|
||||
class DN {
|
||||
#rdns = []
|
||||
|
||||
/**
|
||||
* @param {object} input
|
||||
* @param {RDN[]} [input.rdns=[]] A set of RDN objects that define the DN.
|
||||
* Remember that DNs are in reverse domain order. Thus, the target RDN must
|
||||
* be the first item and the top-level RDN the last item.
|
||||
*
|
||||
* @throws When the provided `rdns` array is invalid.
|
||||
*/
|
||||
constructor ({ rdns = [] } = {}) {
|
||||
if (Array.isArray(rdns) === false) {
|
||||
throw Error('rdns must be an array')
|
||||
}
|
||||
|
||||
const hasNonRdn = rdns.some(
|
||||
r => RDN.isRdn(r) === false
|
||||
)
|
||||
if (hasNonRdn === true) {
|
||||
throw Error('rdns must be an array of RDN objects')
|
||||
}
|
||||
|
||||
Array.prototype.push.apply(
|
||||
this.#rdns,
|
||||
rdns.map(r => {
|
||||
if (Object.prototype.toString.call(r) === '[object LdapRdn]') {
|
||||
return r
|
||||
}
|
||||
return new RDN(r)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
get [Symbol.toStringTag] () {
|
||||
return 'LdapDn'
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of RDNs that make up the DN.
|
||||
*
|
||||
* @returns {number}
|
||||
*/
|
||||
get length () {
|
||||
return this.#rdns.length
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the current instance is the child of another DN instance or
|
||||
* DN string.
|
||||
*
|
||||
* @param {DN|string} dn
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
childOf (dn) {
|
||||
if (typeof dn === 'string') {
|
||||
const parsedDn = DN.fromString(dn)
|
||||
return parsedDn.parentOf(this)
|
||||
}
|
||||
return dn.parentOf(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a new instance that is a replica of the current instance.
|
||||
*
|
||||
* @returns {DN}
|
||||
*/
|
||||
clone () {
|
||||
return new DN({ rdns: this.#rdns })
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the instance is equal to another DN.
|
||||
*
|
||||
* @param {DN|string} dn
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
equals (dn) {
|
||||
if (typeof dn === 'string') {
|
||||
const parsedDn = DN.fromString(dn)
|
||||
return parsedDn.equals(this)
|
||||
}
|
||||
|
||||
if (this.length !== dn.length) return false
|
||||
|
||||
for (let i = 0; i < this.length; i += 1) {
|
||||
if (this.#rdns[i].equals(dn.rdnAt(i)) === false) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use .toString() instead.
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
format () {
|
||||
warning.emit('LDAP_DN_DEP_002')
|
||||
return this.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the instance has any RDNs defined.
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isEmpty () {
|
||||
return this.#rdns.length === 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a DN representation of the parent of this instance.
|
||||
*
|
||||
* @returns {DN|undefined}
|
||||
*/
|
||||
parent () {
|
||||
if (this.length === 0) return undefined
|
||||
const save = this.shift()
|
||||
const dn = new DN({ rdns: this.#rdns })
|
||||
this.unshift(save)
|
||||
return dn
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the instance is the parent of a given DN instance or DN
|
||||
* string.
|
||||
*
|
||||
* @param {DN|string} dn
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
parentOf (dn) {
|
||||
if (typeof dn === 'string') {
|
||||
const parsedDn = DN.fromString(dn)
|
||||
return this.parentOf(parsedDn)
|
||||
}
|
||||
|
||||
if (this.length >= dn.length) {
|
||||
// If we have more RDNs in our set then we must be a descendent at least.
|
||||
return false
|
||||
}
|
||||
|
||||
const numberOfElementsDifferent = dn.length - this.length
|
||||
for (let i = this.length - 1; i >= 0; i -= 1) {
|
||||
const myRdn = this.#rdns[i]
|
||||
const theirRdn = dn.rdnAt(i + numberOfElementsDifferent)
|
||||
if (myRdn.equals(theirRdn) === false) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the last RDN from the list and returns it. This alters the
|
||||
* instance.
|
||||
*
|
||||
* @returns {RDN}
|
||||
*/
|
||||
pop () {
|
||||
return this.#rdns.pop()
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new RDN to the end of the list (i.e. the "top most" RDN in the
|
||||
* directory path) and returns the new RDN count.
|
||||
*
|
||||
* @param {RDN} rdn
|
||||
*
|
||||
* @returns {number}
|
||||
*
|
||||
* @throws When the input is not a valid RDN.
|
||||
*/
|
||||
push (rdn) {
|
||||
if (Object.prototype.toString.call(rdn) !== '[object LdapRdn]') {
|
||||
throw Error('rdn must be a RDN instance')
|
||||
}
|
||||
return this.#rdns.push(rdn)
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the RDN at the provided index in the list of RDNs associated with
|
||||
* this instance.
|
||||
*
|
||||
* @param {number} index
|
||||
*
|
||||
* @returns {RDN}
|
||||
*/
|
||||
rdnAt (index) {
|
||||
return this.#rdns[index]
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the RDNs list such that the first element becomes the last, and
|
||||
* the last becomes the first. This is useful when the RDNs were added in the
|
||||
* opposite order of how they should have been.
|
||||
*
|
||||
* This is an in-place operation. The instance is changed as a result of
|
||||
* this operation.
|
||||
*
|
||||
* @returns {DN} The current instance (i.e. this method is chainable).
|
||||
*/
|
||||
reverse () {
|
||||
this.#rdns.reverse()
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Formatting options are not supported.
|
||||
*/
|
||||
setFormat () {
|
||||
warning.emit('LDAP_DN_DEP_004')
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the first RDN from the set of RDNs and return it.
|
||||
*
|
||||
* @returns {RDN}
|
||||
*/
|
||||
shift () {
|
||||
return this.#rdns.shift()
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the DN instance as a spec compliant DN string.
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
toString () {
|
||||
let result = ''
|
||||
for (const rdn of this.#rdns) {
|
||||
const rdnString = rdn.toString()
|
||||
result += `,${rdnString}`
|
||||
}
|
||||
return result.substring(1)
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an RDN to the beginning of the RDN list and returns the new length.
|
||||
*
|
||||
* @param {RDN} rdn
|
||||
*
|
||||
* @returns {number}
|
||||
*
|
||||
* @throws When the RDN is invalid.
|
||||
*/
|
||||
unshift (rdn) {
|
||||
if (Object.prototype.toString.call(rdn) !== '[object LdapRdn]') {
|
||||
throw Error('rdn must be a RDN instance')
|
||||
}
|
||||
return this.#rdns.unshift(rdn)
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if an object is an instance of {@link DN} or is at least
|
||||
* a DN-like object. It is safer to perform a `toString` check.
|
||||
*
|
||||
* @example Valid Instance
|
||||
* const dn = new DN()
|
||||
* DN.isDn(dn) // true
|
||||
*
|
||||
* @example DN-like Instance
|
||||
* let dn = { rdns: [{name: 'cn', value: 'foo'}] }
|
||||
* DN.isDn(dn) // true
|
||||
*
|
||||
* dn = { rdns: [{cn: 'foo', sn: 'bar'}, {dc: 'example'}, {dc: 'com'}]}
|
||||
* DN.isDn(dn) // true
|
||||
*
|
||||
* @example Preferred Check
|
||||
* let dn = new DN()
|
||||
* Object.prototype.toString.call(dn) === '[object LdapDn]' // true
|
||||
*
|
||||
* dn = { rdns: [{name: 'cn', value: 'foo'}] }
|
||||
* Object.prototype.toString.call(dn) === '[object LdapDn]' // false
|
||||
*
|
||||
* @param {object} dn
|
||||
* @returns {boolean}
|
||||
*/
|
||||
static isDn (dn) {
|
||||
if (Object.prototype.toString.call(dn) === '[object LdapDn]') {
|
||||
return true
|
||||
}
|
||||
if (
|
||||
Object.prototype.toString.call(dn) !== '[object Object]' ||
|
||||
Array.isArray(dn.rdns) === false
|
||||
) {
|
||||
return false
|
||||
}
|
||||
if (dn.rdns.some(dn => RDN.isRdn(dn) === false) === true) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a DN string and returns a new {@link DN} instance.
|
||||
*
|
||||
* @example
|
||||
* const dn = DN.fromString('cn=foo,dc=example,dc=com')
|
||||
* DN.isDn(dn) // true
|
||||
*
|
||||
* @param {string} dnString
|
||||
*
|
||||
* @returns {DN}
|
||||
*
|
||||
* @throws If the string is not parseable.
|
||||
*/
|
||||
static fromString (dnString) {
|
||||
const rdns = parseString(dnString)
|
||||
return new DN({ rdns })
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DN
|
||||
Reference in New Issue
Block a user