@ldapjs/messages
Version:
API for creating and parsing LDAP messages
199 lines (178 loc) • 4.87 kB
JavaScript
'use strict'
const LdapMessage = require('../ldap-message')
const Attribute = require('@ldapjs/attribute')
const { operations } = require('@ldapjs/protocol')
const { DN } = require('@ldapjs/dn')
/**
* Implements the search result entry message as described in
* https://www.rfc-editor.org/rfc/rfc4511.html#section-4.5.2.
*/
class SearchResultEntry extends LdapMessage {
/**
* Path to the LDAP object.
*
* @type {import('@ldapjs/dn').DN}
*/
#objectName
/**
* A set of attribute objects.
*
* @type {import('@ldapjs/attribute')[]}
*/
#attributes = []
/**
* @typedef {LdapMessageOptions} SearchResultEntryOptions
* @property {string | import('@ldapjs/dn').DN} [objectName=''] The path to
* the LDAP object.
* @property {import('@ldapjs/attribute')[]} attributes A set of attributes
* to store at the `entry` path.
*/
/**
* @param {SearchResultEntryOptions} [options]
*
* @throws When the provided attributes list is invalid or the object name
* is not a valid LdapDn object or DN string.
*/
constructor (options = {}) {
options.protocolOp = operations.LDAP_RES_SEARCH_ENTRY
super(options)
this.objectName = options.objectName ?? ''
this.attributes = options.attributes ?? []
}
/**
* Alias of {@link objectName}.
*
* @type {string}
*/
get _dn () {
return this.#objectName
}
/**
* The name of the request type.
*
* @type {string}
*/
get type () {
return 'SearchResultEntry'
}
/**
* Get a copy of the attributes associated with the request.
*
* @returns {import('@ldapjs/attribute')[]}
*/
get attributes () {
return this.#attributes.slice(0)
}
/**
* Set the attributes to be added to the entry. Replaces any existing
* attributes.
*
* @param {object[] | import('@ldapjs/attribute')[]} attrs
*
* @throws If the input is not an array, or any element is not an
* {@link Attribute} or attribute-like object.
*/
set attributes (attrs) {
if (Array.isArray(attrs) === false) {
throw Error('attrs must be an array')
}
const newAttrs = []
for (const attr of attrs) {
if (Attribute.isAttribute(attr) === false) {
throw Error('attr must be an Attribute instance or Attribute-like object')
}
if (Object.prototype.toString.call(attr) !== '[object LdapAttribute]') {
newAttrs.push(new Attribute(attr))
continue
}
newAttrs.push(attr)
}
this.#attributes = newAttrs
}
/**
* The path to the LDAP entry that matched the search.
*
* @returns {import('@ldapjs/dn').DN}
*/
get objectName () {
return this.#objectName
}
/**
* Set the path to the LDAP entry that matched the search.
*
* @param {string | import('@ldapjs/dn').DN} value
*
* @throws When the input is invalid.
*/
set objectName (value) {
if (typeof value === 'string') {
this.#objectName = DN.fromString(value)
} else if (Object.prototype.toString.call(value) === '[object LdapDn]') {
this.#objectName = value
} else {
throw Error('objectName must be a DN string or an instance of LdapDn')
}
}
/**
* Internal use only.
*
* @param {import('@ldapjs/asn1').BerWriter} ber
*
* @returns {import('@ldapjs/asn1').BerWriter}
*/
_toBer (ber) {
ber.startSequence(operations.LDAP_RES_SEARCH_ENTRY)
ber.writeString(this.#objectName.toString())
ber.startSequence()
for (const attr of this.#attributes) {
const attrBer = attr.toBer()
ber.appendBuffer(attrBer.buffer)
}
ber.endSequence()
ber.endSequence()
return ber
}
/**
* Internal use only.
*
* @param {object}
*
* @returns {object}
*/
_pojo (obj = {}) {
obj.objectName = this.#objectName.toString()
obj.attributes = []
for (const attr of this.#attributes) {
obj.attributes.push(attr.pojo)
}
return obj
}
/**
* Implements the standardized `parseToPojo` method.
*
* @see LdapMessage.parseToPojo
*
* @param {import('@ldapjs/asn1').BerReader} ber
*
* @returns {object}
*/
static parseToPojo (ber) {
const protocolOp = ber.readSequence()
if (protocolOp !== operations.LDAP_RES_SEARCH_ENTRY) {
const op = protocolOp.toString(16).padStart(2, '0')
throw Error(`found wrong protocol operation: 0x${op}`)
}
const objectName = ber.readString()
const attributes = []
// Advance to the first attribute sequence in the set
// of attribute sequences.
ber.readSequence()
const endOfAttributesPos = ber.offset + ber.length
while (ber.offset < endOfAttributesPos) {
const attribute = Attribute.fromBer(ber)
attributes.push(attribute)
}
return { protocolOp, objectName, attributes }
}
}
module.exports = SearchResultEntry