123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244 |
- 'use strict'
- const LdapMessage = require('./ldap-message')
- const { resultCodes, operations } = require('@ldapjs/protocol')
- const warning = require('./deprecations')
- /**
- * Implements the base LDAP response message as defined in
- * https://www.rfc-editor.org/rfc/rfc4511.html#section-4.1.9.
- */
- class LdapResult extends LdapMessage {
- #connection = null
- #diagnosticMessage
- #matchedDN
- #referrals = []
- #status
- /**
- * @typedef {LdapMessageOptions} LdapResultOptions
- * @property {number} [status=0] An LDAP status code.
- * @param {string} [matchedDN=''] The DN that matched the request.
- * @param {string[]} [referrals=[]] A set of servers to query for references.
- * @param {string} [diagnosticMessage] A message indicating why a request
- * failed.
- */
- /**
- * @param {LdapResultOptions} options
- */
- constructor (options = {}) {
- super(options)
- this.#status = options.status ?? resultCodes.SUCCESS
- this.#matchedDN = options.matchedDN || ''
- this.#referrals = options.referrals || []
- this.#diagnosticMessage = options.diagnosticMessage || options.errorMessage || ''
- if (options.errorMessage) {
- warning.emit('LDAP_MESSAGE_DEP_004')
- }
- }
- /**
- * The failure message as returned by the server if one is present.
- *
- * @returns {string}
- */
- get diagnosticMessage () {
- return this.#diagnosticMessage
- }
- /**
- * Add a diagnostic message to the instance.
- *
- * @param {string} message
- */
- set diagnosticMessage (message) {
- this.#diagnosticMessage = message
- }
- /**
- * The DN that a request matched.
- *
- * @returns {string}
- */
- get matchedDN () {
- return this.#matchedDN
- }
- /**
- * Define which DN a request matched.
- *
- * @param {string} dn
- */
- set matchedDN (dn) {
- this.#matchedDN = dn
- }
- /**
- * A serialized representation of the message as a plain JavaScript object.
- * Specific message types must implement the `_pojo(obj)` method. The passed
- * in `obj` must be extended with the specific message's unique properties
- * and returned as the result.
- *
- * @returns {object}
- */
- get pojo () {
- let result = {
- status: this.status,
- matchedDN: this.matchedDN,
- diagnosticMessage: this.diagnosticMessage,
- referrals: this.referrals
- }
- if (typeof this._pojo === 'function') {
- result = this._pojo(result)
- }
- return result
- }
- /**
- * The list of servers that should be consulted to get an answer
- * to the query.
- *
- * @returns {string[]}
- */
- get referrals () {
- return this.#referrals.slice(0)
- }
- /**
- * The LDAP response code for the request.
- *
- * @returns {number}
- */
- get status () {
- return this.#status
- }
- /**
- * Set the response code for the request.
- *
- * @param {number} s
- */
- set status (s) {
- this.#status = s
- }
- /**
- * The name of the request type.
- *
- * @type {string}
- */
- get type () {
- return 'LdapResult'
- }
- /**
- * Add a new server to the list of servers that should be
- * consulted for an answer to the query.
- *
- * @param {string} referral
- */
- addReferral (referral) {
- this.#referrals.push(referral)
- }
- /**
- * Internal use only. Subclasses may implement a `_writeResponse`
- * method to add to the sequence after any referrals.
- *
- * @param {import('@ldapjs/asn1').BerWriter} ber
- *
- * @returns {import('@ldapjs/asn1').BerWriter}
- *
- * @private
- */
- _toBer (ber) {
- ber.startSequence(this.protocolOp)
- ber.writeEnumeration(this.status)
- ber.writeString(this.matchedDN)
- ber.writeString(this.diagnosticMessage)
- if (this.referrals.length > 0) {
- ber.startSequence(operations.LDAP_RES_REFERRAL)
- ber.writeStringArray(this.referrals)
- ber.endSequence()
- }
- if (typeof this._writeResponse === 'function') {
- this._writeResponse(ber)
- }
- ber.endSequence()
- }
- /**
- * When invoked on specific message types, e.g. {@link AddResponse}, this
- * method will parse a BER into a plain JavaScript object that is usable as
- * an options object for constructing that specific message object.
- *
- * @param {import('@ldapjs/asn1').BerReader} ber A BER to parse. The reader
- * offset must point to the start of a valid sequence, i.e. the "tag" byte
- * in the TLV tuple, that represents the message to be parsed. For example,
- * in a {@link AddResponse} the starting sequence and message identifier must
- * already be read such that the offset is at the protocol operation sequence
- * byte.
- */
- static parseToPojo (ber) {
- throw Error('Use LdapMessage.parse, or a specific message type\'s parseToPojo, instead.')
- }
- /**
- * Internal use only.
- *
- * Response messages are a little more generic to parse than request messages.
- * However, they still need to recognize the correct protocol operation. So
- * the public {@link parseToPojo} for each response object should invoke this
- * private static method to parse the BER and indicate the correct protocol
- * operation to recognize.
- *
- * @param {object} input
- * @param {number} input.opCode The expected protocol operation to look for.
- * @param {import('@ldapjs/asn1').BerReader} berReader The BER to process. It
- * must start at an offset representing a protocol operation tag.
- * @param {object} [input.pojo] A plain JavaScript object to populate with
- * the parsed keys and values.
- *
- * @returns {object}
- *
- * @private
- */
- static _parseToPojo ({ opCode, berReader, pojo = {} }) {
- const protocolOp = berReader.readSequence()
- if (protocolOp !== opCode) {
- const op = protocolOp.toString(16).padStart(2, '0')
- throw Error(`found wrong protocol operation: 0x${op}`)
- }
- const status = berReader.readEnumeration()
- const matchedDN = berReader.readString()
- const diagnosticMessage = berReader.readString()
- const referrals = []
- if (berReader.peek() === operations.LDAP_RES_REFERRAL) {
- // Advance the offset to the start of the value and
- // put the sequence length into the `reader.length` field.
- berReader.readSequence(operations.LDAP_RES_REFERRAL)
- const end = berReader.length
- while (berReader.offset < end) {
- referrals.push(berReader.readString())
- }
- }
- pojo.status = status
- pojo.matchedDN = matchedDN
- pojo.diagnosticMessage = diagnosticMessage
- pojo.referrals = referrals
- return pojo
- }
- }
- module.exports = LdapResult
|