wangzhibo
2026-05-09 53418655c061ac26f66058253f39200aced1ab1d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
'use strict'
 
const LdapMessage = require('../ldap-message')
const { operations } = require('@ldapjs/protocol')
 
const partIsNotNumeric = part => /^\d+$/.test(part) === false
 
/**
 * Determines if a passed in string is a dotted decimal string.
 *
 * Copied from `@ldapjs/dn`.
 *
 * @param {string} value
 *
 * @returns {boolean}
 */
function isDottedDecimal (value) {
  if (typeof value !== 'string') return false
 
  const parts = value.split('.')
  const nonNumericParts = parts.filter(partIsNotNumeric)
 
  return nonNumericParts.length === 0
}
 
/**
 * Implements the intermediate response message as described in
 * https://www.rfc-editor.org/rfc/rfc4511.html#section-4.13.
 *
 * TODO: actual implementations of this, e.g. RFC 4533 §2.5, seem to encode
 * sequences in the responseValue. That means this needs a more robust
 * implementation like is found in the ExtensionResponse implementation (i.e.
 * detection of recognized OIDs and specific sub-implementations). As of now,
 * this implementation follows the baseline spec without any sub-implementations.
 */
class IntermediateResponse extends LdapMessage {
  #responseName
  #responseValue
 
  /**
   * @typedef {LdapMessageOptions} IntermediateResponseOptions
   * @property {string} responseName
   * @property {string} responseValue
   */
 
  /**
   * @param {IntermediateResponseOptions} options
   */
  constructor (options = {}) {
    options.protocolOp = operations.LDAP_RES_INTERMEDIATE
    super(options)
 
    this.responseName = options.responseName ?? null
    this.responseValue = options.responseValue ?? null
  }
 
  /**
   * The name of the request type.
   *
   * @type {string}
   */
  get type () {
    return 'IntermediateResponse'
  }
 
  /**
   * The numeric OID that identifies the type of intermediate response.
   *
   * @returns {string | undefined}
   */
  get responseName () {
    return this.#responseName
  }
 
  /**
   * Define the numeric OID that identifies the type of intermediate response.
   *
   * @param {string | null} value
   *
   * @throws For an invalid value.
   */
  set responseName (value) {
    if (value === null) return
    if (isDottedDecimal(value) === false) {
      throw Error('responseName must be a numeric OID')
    }
    this.#responseName = value
  }
 
  /**
   * The value for the intermidate response if any.
   *
   * @returns {string | undefined}
   */
  get responseValue () {
    return this.#responseValue
  }
 
  /**
   * Define the value for the intermediate response.
   *
   * @param {string | null} value
   *
   * @throws For an invalid value.
   */
  set responseValue (value) {
    if (value === null) return
    if (typeof value !== 'string') {
      throw Error('responseValue must be a string')
    }
    this.#responseValue = value
  }
 
  /**
   * Internal use only.
   *
   * @param {import('@ldapjs/asn1').BerWriter} ber
   *
   * @returns {import('@ldapjs/asn1').BerWriter}
   */
  _toBer (ber) {
    ber.startSequence(operations.LDAP_RES_INTERMEDIATE)
 
    if (this.#responseName) {
      ber.writeString(this.#responseName, 0x80)
    }
    if (this.#responseValue) {
      ber.writeString(this.#responseValue, 0x81)
    }
 
    ber.endSequence()
    return ber
  }
 
  /**
   * Internal use only.
   *
   * @param {object}
   *
   * @returns {object}
   */
  _pojo (obj = {}) {
    obj.responseName = this.#responseName
    obj.responseValue = this.#responseValue
    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_INTERMEDIATE) {
      const op = protocolOp.toString(16).padStart(2, '0')
      throw Error(`found wrong protocol operation: 0x${op}`)
    }
 
    let responseName
    let responseValue
 
    let tag = ber.peek()
    switch (tag) {
      case 0x80: {
        responseName = ber.readString(tag)
 
        tag = ber.peek()
        /* istanbul ignore else */
        if (tag === 0x81) {
          responseValue = ber.readString(tag)
        }
        break
      }
 
      case 0x81: {
        responseValue = ber.readString(tag)
      }
    }
 
    return { protocolOp, responseName, responseValue }
  }
}
 
module.exports = IntermediateResponse