First commit

This commit is contained in:
2025-10-08 11:12:59 -04:00
commit b0605a28a9
820 changed files with 100317 additions and 0 deletions

88
node_modules/@ldapjs/controls/lib/control.js generated vendored Normal file
View File

@ -0,0 +1,88 @@
'use strict'
const { BerWriter } = require('@ldapjs/asn1')
/**
* Baseline LDAP control object. Implements
* https://tools.ietf.org/html/rfc4511#section-4.1.11
*
* @class
*/
class Control {
/**
* @typedef {object} ControlParams
* @property {string} [type=''] The dotted decimal control type value.
* @property {boolean} [criticality=false] Criticality value for the control.
* @property {string|Buffer} [value] The value for the control. If this is
* a `string` then it will be written as-is. If it is an instance of `Buffer`
* then it will be written by `value.toString()` when generating a BER
* instance.
*/
/**
* Create a new baseline LDAP control.
*
* @param {ControlParams} [options]
*/
constructor (options = {}) {
const opts = Object.assign({ type: '', criticality: false, value: null }, options)
this.type = opts.type
this.criticality = opts.criticality
this.value = opts.value
}
get [Symbol.toStringTag] () {
return 'LdapControl'
}
/**
* Serializes the control into a plain JavaScript object that can be passed
* to the constructor as an options object. If an instance has a `_pojo(obj)`
* method then the built object will be sent to that method and the resulting
* mutated object returned.
*
* @returns {object} A plain JavaScript object that represents an LDAP control.
*/
get pojo () {
const obj = {
type: this.type,
value: this.value,
criticality: this.criticality
}
if (typeof this._pojo === 'function') {
this._pojo(obj)
}
return obj
}
/**
* Converts the instance into a [BER](http://luca.ntop.org/Teaching/Appunti/asn1.html)
* representation.
*
* @param {BerWriter} [ber] An empty `BerWriter` instance to populate.
*
* @returns {object} A BER object.
*/
toBer (ber = new BerWriter()) {
ber.startSequence()
ber.writeString(this.type || '')
ber.writeBoolean(this.criticality)
/* istanbul ignore else */
if (typeof (this._toBer) === 'function') {
this._toBer(ber)
} else if (this.value !== undefined) {
if (typeof this.value === 'string') {
ber.writeString(this.value)
} else if (Buffer.isBuffer(this.value)) {
ber.writeString(this.value.toString())
}
}
ber.endSequence()
return ber
}
}
module.exports = Control

159
node_modules/@ldapjs/controls/lib/control.test.js generated vendored Normal file
View File

@ -0,0 +1,159 @@
'use strict'
const tap = require('tap')
const { BerWriter } = require('@ldapjs/asn1')
const Control = require('./control')
tap.test('constructor', t => {
t.test('new no args', function (t) {
t.ok(new Control())
t.equal(Object.prototype.toString.call(new Control()), '[object LdapControl]')
t.end()
})
t.test('new with args', function (t) {
const c = new Control({
type: '2.16.840.1.113730.3.4.2',
criticality: true
})
t.ok(c)
t.equal(c.type, '2.16.840.1.113730.3.4.2')
t.ok(c.criticality)
t.end()
})
t.end()
})
tap.test('pojo', t => {
t.test('passes through _pojo', async t => {
class Foo extends Control {
_pojo (obj) {
obj.foo = 'foo'
}
}
const control = new Foo()
t.strictSame(control.pojo, {
type: '',
value: null,
criticality: false,
foo: 'foo'
})
})
t.test('returns basic object', async t => {
const control = new Control({ type: '1.2.3', criticality: false, value: 'foo' })
t.strictSame(control.pojo, {
type: '1.2.3',
value: 'foo',
criticality: false
})
})
t.end()
})
tap.test('toBer', t => {
t.test('converts empty instance to BER', async t => {
const target = new BerWriter()
target.startSequence()
target.writeString('')
target.writeBoolean(false)
target.endSequence()
const control = new Control()
const ber = control.toBer()
t.equal(Buffer.compare(ber.buffer, target.buffer), 0)
})
t.test('converts instance to BER', async t => {
const target = new BerWriter()
target.startSequence()
target.writeString('2.16.840.1.113730.3.4.2')
target.writeBoolean(true)
target.writeString('foo')
target.endSequence()
const control = new Control({
type: '2.16.840.1.113730.3.4.2',
criticality: true,
value: Buffer.from('foo', 'utf8')
})
const ber = control.toBer()
t.equal(Buffer.compare(ber.buffer, target.buffer), 0)
})
t.test('converts instance to BER (side effect manner)', async t => {
const target = new BerWriter()
target.startSequence()
target.writeString('2.16.840.1.113730.3.4.2')
target.writeBoolean(true)
target.writeString('foo')
target.endSequence()
const control = new Control({
type: '2.16.840.1.113730.3.4.2',
criticality: true,
value: Buffer.from('foo', 'utf8')
})
const ber = new BerWriter()
control.toBer(ber)
t.equal(Buffer.compare(ber.buffer, target.buffer), 0)
})
t.test('converts instance to BER with string value', async t => {
const target = new BerWriter()
target.startSequence()
target.writeString('2.16.840.1.113730.3.4.2')
target.writeBoolean(true)
target.writeString('foo')
target.endSequence()
const control = new Control({
type: '2.16.840.1.113730.3.4.2',
criticality: true,
value: 'foo'
})
const ber = control.toBer()
t.equal(Buffer.compare(ber.buffer, target.buffer), 0)
})
t.test('ignores unrecognized value', async t => {
const target = new BerWriter()
target.startSequence()
target.writeString('2.16.840.1.113730.3.4.2')
target.writeBoolean(true)
target.writeBoolean(false)
target.endSequence()
const control = new Control({
type: '2.16.840.1.113730.3.4.2',
criticality: true,
value: false
})
const ber = control.toBer()
t.not(Buffer.compare(ber.buffer, target.buffer), 0)
})
t.test('passes through _toBer', async t => {
t.plan(2)
const target = new BerWriter()
target.startSequence()
target.writeString('')
target.writeBoolean(false)
target.endSequence()
const control = new Control()
control._toBer = (ber) => t.ok(ber)
const ber = control.toBer()
t.equal(Buffer.compare(ber.buffer, target.buffer), 0)
})
t.end()
})

View File

@ -0,0 +1,107 @@
'use strict'
const { BerReader, BerWriter } = require('@ldapjs/asn1')
const isObject = require('../is-object')
const hasOwn = require('../has-own')
const Control = require('../control')
/**
* @typedef {object} EntryChangeNotificationControlValue
* @property {number} changeType One of 1 (add), 2 (delete), 4 (modify),
* or 8 (modifyDN).
* @property {string} previousDN Only set when operation is a modifyDN op.
* @property {number} changeNumber
*/
/**
* Implements:
* https://datatracker.ietf.org/doc/html/draft-ietf-ldapext-psearch-03.txt#section-5
*
* @extends Control
*/
class EntryChangeNotificationControl extends Control {
static OID = '2.16.840.1.113730.3.4.7'
/**
* @typedef {ControlParams} EntryChangeNotificationParams
* @property {EntryChangeNotificationControlValue | Buffer} [value]
*/
/**
* Creates a new persistent search control.
*
* @param {EntryChangeNotificationParams} [options]
*/
constructor (options = {}) {
options.type = EntryChangeNotificationControl.OID
super(options)
this._value = {
changeType: 4
}
if (hasOwn(options, 'value') === false) {
return
}
if (Buffer.isBuffer(options.value)) {
this.#parse(options.value)
} else if (isObject(options.value)) {
this._value = options.value
} else {
throw new TypeError('options.value must be a Buffer or Object')
}
}
get value () {
return this._value
}
set value (obj) {
this._value = Object.assign({}, this._value, obj)
}
/**
* Given a BER buffer that represents a
* {@link EntryChangeNotificationControlValue}, read that buffer into the
* current instance.
*/
#parse (buffer) {
const ber = new BerReader(buffer)
/* istanbul ignore else */
if (ber.readSequence()) {
this._value = {
changeType: ber.readInt()
}
/* istanbul ignore else */
if (this._value.changeType === 8) {
// If the operation was moddn, then parse the optional previousDN attr.
this._value.previousDN = ber.readString()
}
this._value.changeNumber = ber.readInt()
}
}
_toBer (ber) {
const writer = new BerWriter()
writer.startSequence()
writer.writeInt(this._value.changeType)
if (this._value.previousDN) { writer.writeString(this._value.previousDN) }
if (Object.prototype.hasOwnProperty.call(this._value, 'changeNumber')) {
writer.writeInt(parseInt(this._value.changeNumber, 10))
}
writer.endSequence()
ber.writeBuffer(writer.buffer, 0x04)
return ber
}
_updatePlainObject (obj) {
obj.controlValue = this.value
return obj
}
}
module.exports = EntryChangeNotificationControl

View File

@ -0,0 +1,133 @@
'use strict'
const tap = require('tap')
const { BerWriter } = require('@ldapjs/asn1')
const ECNC = require('./entry-change-notification-control')
const Control = require('../control')
tap.test('contructor', t => {
t.test('new no args', async t => {
const control = new ECNC()
t.ok(control)
t.type(control, ECNC)
t.type(control, Control)
t.equal(control.type, ECNC.OID)
t.same(control.value, {
changeType: 4
})
})
t.test('new with args', async t => {
const control = new ECNC({
type: '2.16.840.1.113730.3.4.7',
criticality: true,
value: {
changeType: 1
}
})
t.ok(control)
t.equal(control.type, '2.16.840.1.113730.3.4.7')
t.ok(control.criticality)
t.same(control.value, {
changeType: 1
})
})
t.test('with value buffer', async t => {
const value = new BerWriter()
value.startSequence()
value.writeInt(8)
value.writeString('dn=foo')
value.writeInt(42)
value.endSequence()
const control = new ECNC({ value: value.buffer })
t.same(control.value, {
changeType: 8,
previousDN: 'dn=foo',
changeNumber: 42
})
})
t.test('throws for bad value', async t => {
t.throws(() => new ECNC({ value: 42 }))
})
t.end()
})
tap.test('pojo', t => {
t.test('adds control value', async t => {
const control = new ECNC({
value: {
changeType: 8,
previousDN: 'dn=foo',
changeNumber: 42
}
})
t.strictSame(control.pojo, {
type: ECNC.OID,
criticality: false,
value: {
changeType: 8,
previousDN: 'dn=foo',
changeNumber: 42
}
})
})
t.end()
})
tap.test('toBer', t => {
t.test('converts empty instance to BER', async t => {
const target = new BerWriter()
target.startSequence()
target.writeString(ECNC.OID)
target.writeBoolean(false) // Control.criticality
const value = new BerWriter()
value.startSequence()
value.writeInt(4)
// value.writeInt(0)
value.endSequence()
target.writeBuffer(value.buffer, 0x04)
target.endSequence()
const control = new ECNC()
const ber = control.toBer()
t.equal(Buffer.compare(ber.buffer, target.buffer), 0)
})
t.test('converts instance with full values to BER', async t => {
const target = new BerWriter()
target.startSequence()
target.writeString(ECNC.OID)
target.writeBoolean(false) // Control.criticality
const value = new BerWriter()
value.startSequence()
value.writeInt(8)
value.writeString('dn=foo')
value.writeInt(42)
value.endSequence()
target.writeBuffer(value.buffer, 0x04)
target.endSequence()
const control = new ECNC({
value: {
changeType: 8,
previousDN: 'dn=foo',
changeNumber: 42
}
})
const ber = control.toBer()
t.equal(Buffer.compare(ber.buffer, target.buffer), 0)
})
t.end()
})

View File

@ -0,0 +1,103 @@
'use strict'
const { Ber, BerReader, BerWriter } = require('@ldapjs/asn1')
const isObject = require('../is-object')
const hasOwn = require('../has-own')
const Control = require('../control')
/**
* @typedef {object} PagedResultsControlValue
* @property {number} size The requested page size from a client, or the result
* set size estimate from the server.
* @property {Buffer} cookie Identifier for the result set.
*/
/**
* Implements:
* https://datatracker.ietf.org/doc/html/rfc2696#section-2
*
* @extends Control
*/
class PagedResultsControl extends Control {
static OID = '1.2.840.113556.1.4.319'
/**
* @typedef {ControlParams} PagedResultsParams
* @property {PagedResultsControlValue | Buffer} [value]
*/
/**
* Creates a new paged results control.
*
* @param {PagedResultsParams} [options]
*/
constructor (options = {}) {
options.type = PagedResultsControl.OID
super(options)
this._value = {
size: 0,
cookie: Buffer.alloc(0)
}
if (hasOwn(options, 'value') === false) {
return
}
if (Buffer.isBuffer(options.value)) {
this.#parse(options.value)
} else if (isObject(options.value)) {
this.value = options.value
} else {
throw new TypeError('options.value must be a Buffer or Object')
}
}
get value () {
return this._value
}
set value (obj) {
this._value = Object.assign({}, this._value, obj)
if (typeof this._value.cookie === 'string') {
this._value.cookie = Buffer.from(this._value.cookie)
}
}
#parse (buffer) {
const ber = new BerReader(buffer)
/* istanbul ignore else */
if (ber.readSequence()) {
this._value = {}
this._value.size = ber.readInt()
this._value.cookie = ber.readString(Ber.OctetString, true)
// readString returns '' instead of a zero-length buffer
if (!this._value.cookie) {
this._value.cookie = Buffer.alloc(0)
}
}
}
_toBer (ber) {
const writer = new BerWriter()
writer.startSequence()
writer.writeInt(this._value.size)
if (this._value.cookie && this._value.cookie.length > 0) {
writer.writeBuffer(this._value.cookie, Ber.OctetString)
} else {
// writeBuffer rejects zero-length buffers
writer.writeString('')
}
writer.endSequence()
ber.writeBuffer(writer.buffer, Ber.OctetString)
return ber
}
_updatePlainObject (obj) {
obj.controlValue = this.value
return obj
}
}
module.exports = PagedResultsControl

View File

@ -0,0 +1,139 @@
'use strict'
const tap = require('tap')
const { BerWriter } = require('@ldapjs/asn1')
const PSC = require('./paged-results-control')
const Control = require('../control')
tap.test('contructor', t => {
t.test('new no args', async t => {
const control = new PSC()
t.ok(control)
t.type(control, PSC)
t.type(control, Control)
t.equal(control.type, PSC.OID)
t.equal(control.value.size, 0)
t.equal(Buffer.alloc(0).compare(control.value.cookie), 0)
})
t.test('new with args', async t => {
const control = new PSC({
type: '1.2.840.113556.1.4.319',
criticality: true,
value: {
size: 1,
cookie: 'foo'
}
})
t.ok(control)
t.equal(control.type, '1.2.840.113556.1.4.319')
t.ok(control.criticality)
t.equal(control.value.size, 1)
t.equal(Buffer.from('foo').compare(control.value.cookie), 0)
})
t.test('with value buffer', async t => {
const value = new BerWriter()
value.startSequence()
value.writeInt(1)
value.writeBuffer(Buffer.from('foo'), 0x04)
value.endSequence()
const control = new PSC({ value: value.buffer })
t.equal(control.value.size, 1)
t.equal(Buffer.from('foo').compare(control.value.cookie), 0)
})
t.test('with value buffer (empty cookie)', async t => {
const value = new BerWriter()
value.startSequence()
value.writeInt(1)
value.endSequence()
const control = new PSC({ value: value.buffer })
t.equal(control.value.size, 1)
t.equal(Buffer.alloc(0).compare(control.value.cookie), 0)
})
t.test('throws for bad value', async t => {
t.throws(() => new PSC({ value: 42 }))
})
t.end()
})
tap.test('pojo', t => {
t.test('adds control value', async t => {
const control = new PSC({
value: {
size: 1,
cookie: 'foo'
}
})
t.same(control.pojo, {
type: PSC.OID,
criticality: false,
value: {
size: 1,
cookie: Buffer.from('foo')
}
})
})
t.end()
})
tap.test('toBer', t => {
t.test('converts empty instance to BER', async t => {
const target = new BerWriter()
target.startSequence()
target.writeString(PSC.OID)
target.writeBoolean(false) // Control.criticality
const value = new BerWriter()
value.startSequence()
value.writeInt(1)
value.writeString('foo')
value.endSequence()
target.writeBuffer(value.buffer, 0x04)
target.endSequence()
const control = new PSC({
value: {
size: 1,
cookie: 'foo'
}
})
const ber = control.toBer()
t.equal(Buffer.compare(ber.buffer, target.buffer), 0)
})
t.test('converts empty instance to BER (empty cookie)', async t => {
const target = new BerWriter()
target.startSequence()
target.writeString(PSC.OID)
target.writeBoolean(false) // Control.criticality
const value = new BerWriter()
value.startSequence()
value.writeInt(1)
value.writeString('')
value.endSequence()
target.writeBuffer(value.buffer, 0x04)
target.endSequence()
const control = new PSC({
value: {
size: 1
}
})
const ber = control.toBer()
t.equal(Buffer.compare(ber.buffer, target.buffer), 0)
})
t.end()
})

View File

@ -0,0 +1,118 @@
'use strict'
const { BerReader, BerWriter } = require('@ldapjs/asn1')
const isObject = require('../is-object')
const hasOwn = require('../has-own')
const Control = require('../control')
/**
* @typedef {object} PasswordPolicyResponseControlValue
* @property {number} error One of 0 (passwordExpired), 1 (accountLocked),
* 2 (changeAfterReset), 3 (passwordModNotAllowed), 4 (mustSupplyOldPassword),
* 5 (insufficientPasswordQuality), 6 (passwordTooShort), 7 (passwordTooYoung),
* 8 (passwordInHistory), 9 (passwordTooYoung)
* @property {number} timeBeforeExpiration
* @property {number} graceAuthNsRemaining
*/
/**
* Implements both request and response controls:
* https://datatracker.ietf.org/doc/html/draft-behera-ldap-password-policy-11#name-controls-used-for-password-
*
* @extends Control
*/
class PasswordPolicyControl extends Control {
static OID = '1.3.6.1.4.1.42.2.27.8.5.1'
/**
* @typedef {ControlParams} PasswordPolicyResponseParams
* @property {PasswordPolicyResponseControlValue | Buffer} [value]
*/
/**
* Creates a new password policy control.
*
* @param {PasswordPolicyResponseParams} [options]
*/
constructor (options = {}) {
options.type = PasswordPolicyControl.OID
super(options)
this._value = {}
if (hasOwn(options, 'value') === false) {
return
}
if (Buffer.isBuffer(options.value)) {
this.#parse(options.value)
} else if (isObject(options.value)) {
if (hasOwn(options.value, 'timeBeforeExpiration') === true && hasOwn(options.value, 'graceAuthNsRemaining') === true) {
throw new Error('options.value must contain either timeBeforeExpiration or graceAuthNsRemaining, not both')
}
this._value = options.value
} else {
throw new TypeError('options.value must be a Buffer or Object')
}
}
get value () {
return this._value
}
set value (obj) {
this._value = Object.assign({}, this._value, obj)
}
/**
* Given a BER buffer that represents a
* {@link PasswordPolicyResponseControlValue}, read that buffer into the
* current instance.
*/
#parse (buffer) {
const ber = new BerReader(buffer)
if (ber.readSequence()) {
this._value = {}
if (ber.peek() === 0xa0) {
ber.readSequence(0xa0)
if (ber.peek() === 0x80) {
this._value.timeBeforeExpiration = ber._readTag(0x80)
} else if (ber.peek() === 0x81) {
this._value.graceAuthNsRemaining = ber._readTag(0x81)
}
}
if (ber.peek() === 0x81) {
this._value.error = ber._readTag(0x81)
}
}
}
_toBer (ber) {
if (!this._value || Object.keys(this._value).length === 0) { return }
const writer = new BerWriter()
writer.startSequence()
if (hasOwn(this._value, 'timeBeforeExpiration')) {
writer.startSequence(0xa0)
writer.writeInt(this._value.timeBeforeExpiration, 0x80)
writer.endSequence()
} else if (hasOwn(this._value, 'graceAuthNsRemaining')) {
writer.startSequence(0xa0)
writer.writeInt(this._value.graceAuthNsRemaining, 0x81)
writer.endSequence()
}
if (hasOwn(this._value, 'error')) {
writer.writeInt(this._value.error, 0x81)
}
writer.endSequence()
ber.writeBuffer(writer.buffer, 0x04)
return ber
}
_updatePlainObject (obj) {
obj.controlValue = this.value
return obj
}
}
module.exports = PasswordPolicyControl

View File

@ -0,0 +1,113 @@
'use strict'
const tap = require('tap')
const { BerWriter } = require('@ldapjs/asn1')
const PPC = require('./password-policy-control')
const Control = require('../control')
tap.test('contructor', t => {
t.test('new no args', async t => {
const control = new PPC()
t.ok(control)
t.type(control, PPC)
t.type(control, Control)
t.equal(control.type, PPC.OID)
t.same(control.value, {})
})
t.test('new with args', async t => {
const control = new PPC({
type: '1.3.6.1.4.1.42.2.27.8.5.1',
criticality: true,
value: {
error: 1,
timeBeforeExpiration: 2
}
})
t.ok(control)
t.equal(control.type, '1.3.6.1.4.1.42.2.27.8.5.1')
t.ok(control.criticality)
t.same(control.value, {
error: 1,
timeBeforeExpiration: 2
})
})
t.test('with value buffer', async t => {
const value = new BerWriter()
value.startSequence()
value.writeInt(5, 0x81)
value.endSequence()
const control = new PPC({ value: value.buffer })
t.same(control.value, {
error: 5
})
})
t.test('throws for bad value', async t => {
t.throws(() => new PPC({ value: 42 }))
t.throws(() => new PPC({ value: { timeBeforeExpiration: 1, graceAuthNsRemaining: 2 } }))
})
t.end()
})
tap.test('pojo', t => {
t.test('adds control value', async t => {
const control = new PPC()
t.same(control.pojo, {
type: PPC.OID,
criticality: false,
value: {}
})
})
t.end()
})
tap.test('toBer', t => {
t.test('converts empty instance to BER', async t => {
const target = new BerWriter()
target.startSequence()
target.writeString(PPC.OID)
target.writeBoolean(false) // Control.criticality
target.endSequence()
const control = new PPC()
const ber = control.toBer()
t.equal(Buffer.compare(ber.buffer, target.buffer), 0)
})
t.test('converts full instance to BER', async t => {
const target = new BerWriter()
target.startSequence()
target.writeString(PPC.OID)
target.writeBoolean(true) // Control.criticality
const value = new BerWriter()
value.startSequence()
value.startSequence(0xa0)
value.writeInt(2, 0x81)
value.endSequence()
value.writeInt(1, 0x81)
value.endSequence()
target.writeBuffer(value.buffer, 0x04)
target.endSequence()
const control = new PPC({
criticality: true,
value: {
error: 1,
graceAuthNsRemaining: 2
}
})
const ber = control.toBer()
t.equal(Buffer.compare(ber.buffer, target.buffer), 0)
})
t.end()
})

View File

@ -0,0 +1,100 @@
'use strict'
const { BerReader, BerWriter } = require('@ldapjs/asn1')
const isObject = require('../is-object')
const hasOwn = require('../has-own')
const Control = require('../control')
/**
* @typedef {object} PersistentSearchControlValue
* @property {number} changeTypes A bitwise OR of 1 (add), 2 (delete),
* 4 (modify), and 8 (modifyDN).
* @property {boolean} changesOnly
* @property {boolean} returnECs
*/
/**
* Implements:
* https://datatracker.ietf.org/doc/html/draft-ietf-ldapext-psearch-03.txt
*
* @extends Control
*/
class PersistentSearchControl extends Control {
static OID = '2.16.840.1.113730.3.4.3'
/**
* @typedef {ControlParams} PersistentSearchParams
* @property {PersistentSearchControlValue | Buffer} [value]
*/
/**
* Creates a new persistent search control.
*
* @param {PersistentSearchParams} [options]
*/
constructor (options = {}) {
options.type = PersistentSearchControl.OID
super(options)
this._value = {
changeTypes: 15,
changesOnly: true,
returnECs: true
}
if (hasOwn(options, 'value') === false) {
return
}
if (Buffer.isBuffer(options.value)) {
this.#parse(options.value)
} else if (isObject(options.value)) {
this._value = options.value
} else {
throw new TypeError('options.value must be a Buffer or Object')
}
}
get value () {
return this._value
}
set value (obj) {
this._value = Object.assign({}, this._value, obj)
}
/**
* Given a BER buffer that represents a {@link PersistentSearchControlValue},
* read that buffer into the current instance.
*/
#parse (buffer) {
const ber = new BerReader(buffer)
/* istanbul ignore else */
if (ber.readSequence()) {
this._value = {
changeTypes: ber.readInt(),
changesOnly: ber.readBoolean(),
returnECs: ber.readBoolean()
}
}
}
_toBer (ber) {
const writer = new BerWriter()
writer.startSequence()
writer.writeInt(this._value.changeTypes)
writer.writeBoolean(this._value.changesOnly)
writer.writeBoolean(this._value.returnECs)
writer.endSequence()
ber.writeBuffer(writer.buffer, 0x04)
return ber
}
_updatePlainObject (obj) {
obj.controlValue = this.value
return obj
}
}
module.exports = PersistentSearchControl

View File

@ -0,0 +1,106 @@
'use strict'
const tap = require('tap')
const { BerWriter } = require('@ldapjs/asn1')
const PSC = require('./persistent-search-control')
const Control = require('../control')
tap.test('contructor', t => {
t.test('new no args', async t => {
const control = new PSC()
t.ok(control)
t.type(control, PSC)
t.type(control, Control)
t.equal(control.type, PSC.OID)
t.same(control.value, {
changeTypes: 15,
changesOnly: true,
returnECs: true
})
})
t.test('new with args', async t => {
const control = new PSC({
type: '2.16.840.1.113730.3.4.3',
criticality: true,
value: {
changeTypes: 1,
changesOnly: false,
returnECs: true
}
})
t.ok(control)
t.equal(control.type, '2.16.840.1.113730.3.4.3')
t.ok(control.criticality)
t.same(control.value, {
changeTypes: 1,
changesOnly: false,
returnECs: true
})
})
t.test('with value buffer', async t => {
const value = new BerWriter()
value.startSequence()
value.writeInt(2)
value.writeBoolean(true)
value.writeBoolean(false)
value.endSequence()
const control = new PSC({ value: value.buffer })
t.same(control.value, {
changeTypes: 2,
changesOnly: true,
returnECs: false
})
})
t.test('throws for bad value', async t => {
t.throws(() => new PSC({ value: 42 }))
})
t.end()
})
tap.test('pojo', t => {
t.test('adds control value', async t => {
const control = new PSC()
t.same(control.pojo, {
type: PSC.OID,
criticality: false,
value: {
changeTypes: 15,
changesOnly: true,
returnECs: true
}
})
})
t.end()
})
tap.test('toBer', t => {
t.test('converts empty instance to BER', async t => {
const target = new BerWriter()
target.startSequence()
target.writeString(PSC.OID)
target.writeBoolean(false) // Control.criticality
const value = new BerWriter()
value.startSequence()
value.writeInt(15)
value.writeBoolean(true)
value.writeBoolean(true)
value.endSequence()
target.writeBuffer(value.buffer, 0x04)
target.endSequence()
const control = new PSC()
const ber = control.toBer()
t.equal(Buffer.compare(ber.buffer, target.buffer), 0)
})
t.end()
})

View File

@ -0,0 +1,132 @@
'use strict'
const { Ber, BerReader, BerWriter } = require('@ldapjs/asn1')
const isObject = require('../is-object')
const hasOwn = require('../has-own')
const Control = require('../control')
/**
* @typedef {object} SortKeyItem
* @property {string} attributeType
* @property {string} orderingRule
* @property {boolean} reverseOrder
*/
/**
* @typedef {SortKeyItem[]} ServerSideSortingRequestControlValue
*/
/**
* Implements:
* https://datatracker.ietf.org/doc/html/draft-ietf-ldapext-sorting#section-3.1
*
* @extends Control
*/
class ServerSideSortingRequestControl extends Control {
static OID = '1.2.840.113556.1.4.473'
/**
* @typedef {ControlParams} ServerSideSortingRequestParams
* @property {ServerSideSortingRequestControlValue | SortKeyItem | Buffer} [value]
*/
/**
* Creates a new server side sorting request control.
*
* @param {ServerSideSortingRequestParams} [options]
*/
constructor (options = { value: [] }) {
options.type = ServerSideSortingRequestControl.OID
super(options)
const inputValue = options.value ?? []
if (Buffer.isBuffer(inputValue)) {
this.#parse(inputValue)
} else if (Array.isArray(inputValue)) {
for (const obj of inputValue) {
if (isObject(obj) === false) {
throw new Error('Control value must be an object')
}
if (hasOwn(obj, 'attributeType') === false) {
throw new Error('Missing required key: attributeType')
}
}
this.value = inputValue
} else if (isObject(inputValue)) {
if (hasOwn(inputValue, 'attributeType') === false) {
throw new Error('Missing required key: attributeType')
}
this.value = [inputValue]
} else {
throw new TypeError('options.value must be a Buffer, Array or Object')
}
}
get value () {
return this._value
}
set value (items) {
if (Buffer.isBuffer(items) === true) return
if (Array.isArray(items) === false) {
this._value = [items]
return
}
this._value = items
}
#parse (buffer) {
const ber = new BerReader(buffer)
let item
/* istanbul ignore else */
if (ber.readSequence(0x30)) {
this.value = []
while (ber.readSequence(0x30)) {
item = {}
item.attributeType = ber.readString(Ber.OctetString)
/* istanbul ignore else */
if (ber.peek() === 0x80) {
item.orderingRule = ber.readString(0x80)
}
/* istanbul ignore else */
if (ber.peek() === 0x81) {
item.reverseOrder = (ber._readTag(0x81) !== 0)
}
this.value.push(item)
}
}
}
_pojo (obj) {
obj.value = this.value
return obj
}
_toBer (ber) {
if (this.value.length === 0) { return }
const writer = new BerWriter()
writer.startSequence(0x30)
for (let i = 0; i < this.value.length; i++) {
const item = this.value[i]
writer.startSequence(0x30)
/* istanbul ignore else */
if (hasOwn(item, 'attributeType')) {
writer.writeString(item.attributeType, Ber.OctetString)
}
/* istanbul ignore else */
if (hasOwn(item, 'orderingRule')) {
writer.writeString(item.orderingRule, 0x80)
}
/* istanbul ignore else */
if (hasOwn(item, 'reverseOrder')) {
writer.writeBoolean(item.reverseOrder, 0x81)
}
writer.endSequence()
}
writer.endSequence()
ber.writeBuffer(writer.buffer, 0x04)
}
}
module.exports = ServerSideSortingRequestControl

View File

@ -0,0 +1,144 @@
'use strict'
const tap = require('tap')
const { BerWriter } = require('@ldapjs/asn1')
const SSSRC = require('./server-side-sorting-request-control')
const Control = require('../control')
tap.test('contructor', t => {
t.test('new no args', async t => {
const control = new SSSRC()
t.ok(control)
t.type(control, SSSRC)
t.type(control, Control)
t.equal(control.type, SSSRC.OID)
t.same(control.value, [])
})
t.test('new with args', async t => {
const control = new SSSRC({
type: '1.2.840.113556.1.4.473',
criticality: true,
value: [{ attributeType: 'foo' }]
})
t.ok(control)
t.equal(control.type, '1.2.840.113556.1.4.473')
t.ok(control.criticality)
t.same(control.value, [{ attributeType: 'foo' }])
})
t.test('new with object', async t => {
const control = new SSSRC({
type: '1.2.840.113556.1.4.473',
criticality: true,
value: { attributeType: 'foo' }
})
t.ok(control)
t.equal(control.type, '1.2.840.113556.1.4.473')
t.ok(control.criticality)
t.same(control.value, [{ attributeType: 'foo' }])
})
t.test('with value buffer', async t => {
const value = new BerWriter()
value.startSequence(0x30) // Open "array"
value.startSequence(0x30) // Start "item"
value.writeString('foo', 0x04)
value.writeString('bar', 0x80)
value.writeBoolean(false, 0x81)
value.endSequence() // End item
value.endSequence() // Close array
const control = new SSSRC({ value: value.buffer })
t.same(control.value, [{
attributeType: 'foo',
orderingRule: 'bar',
reverseOrder: false
}])
})
t.test('throws for bad value', async t => {
t.throws(() => new SSSRC({ value: 42 }))
})
t.test('throws for bad object value', async t => {
t.throws(() => new SSSRC({ value: { foo: 'bar' } }))
})
t.test('throws for bad array value', async t => {
t.throws(() => new SSSRC({ value: [42] }))
t.throws(() => new SSSRC({ value: [{ foo: 'bar' }] }))
})
t.end()
})
tap.test('pojo', t => {
t.test('adds control value', async t => {
const control = new SSSRC()
t.same(control.pojo, {
type: SSSRC.OID,
criticality: false,
value: []
})
})
t.test('_pojo', async t => {
const control = new SSSRC()
const obj = control._pojo({ value: 'change_me' })
t.strictSame(obj, { value: [] })
})
t.end()
})
tap.test('toBer', t => {
t.test('converts empty instance to BER', async t => {
const target = new BerWriter()
target.startSequence()
target.writeString(SSSRC.OID)
target.writeBoolean(false) // Control.criticality
target.endSequence()
const control = new SSSRC()
const ber = control.toBer()
t.equal(Buffer.compare(ber.buffer, target.buffer), 0)
})
t.test('converts full instance BER', async t => {
const target = new BerWriter()
target.startSequence()
target.writeString(SSSRC.OID)
target.writeBoolean(false) // Control.criticality
const value = new BerWriter()
value.startSequence(0x30) // Open "array"
value.startSequence(0x30) // Start "item"
value.writeString('one', 0x04)
value.writeString('one', 0x80)
value.writeBoolean(false, 0x81)
value.endSequence() // End item
value.startSequence(0x30) // Start "item"
value.writeString('two', 0x04)
value.writeString('two', 0x80)
value.writeBoolean(true, 0x81)
value.endSequence() // End item
value.endSequence() // Close array
target.writeBuffer(value.buffer, 0x04)
target.endSequence()
const control = new SSSRC({
value: [
{ attributeType: 'one', orderingRule: 'one', reverseOrder: false },
{ attributeType: 'two', orderingRule: 'two', reverseOrder: true }
]
})
const ber = control.toBer()
t.equal(Buffer.compare(Buffer.from(target.buffer), ber.buffer), 0)
})
t.end()
})

View File

@ -0,0 +1,129 @@
'use strict'
const { BerReader, BerWriter } = require('@ldapjs/asn1')
const Control = require('../control')
const isObject = require('../is-object')
const hasOwn = require('../has-own')
const { resultCodes: RESULT_CODES } = require('@ldapjs/protocol')
const validCodeNames = [
'SUCCESS',
'OPERATIONS_ERROR',
'TIME_LIMIT_EXCEEDED',
'STRONGER_AUTH_REQUIRED',
'ADMIN_LIMIT_EXCEEDED',
'NO_SUCH_ATTRIBUTE',
'INAPPROPRIATE_MATCHING',
'INSUFFICIENT_ACCESS_RIGHTS',
'BUSY',
'UNWILLING_TO_PERFORM',
'OTHER'
]
const filteredCodes = Object.entries(RESULT_CODES).filter(([k, v]) => validCodeNames.includes(k))
const VALID_CODES = new Map([
...filteredCodes,
...filteredCodes.map(([k, v]) => { return [v, k] })
])
/**
* @typedef {object} ServerSideSortingResponseControlResult
* @property {number} result
* @property {string} failedAttribute
*/
/**
* Implements:
* https://datatracker.ietf.org/doc/html/draft-ietf-ldapext-sorting#section-3.2
*
* @extends Control
*/
class ServerSideSortingResponseControl extends Control {
static OID = '1.2.840.113556.1.4.474'
/**
* A map of possible response codes. Includes `CODE => VALUE` and
* `VALUE => CODE`. For example, `RESPONSE_CODES.get(0)` returns
* `LDAP_SUCCESS`, and `RESPONSE_CODES.get('LDAP_SUCCESS')` returns `0`.
*/
static RESPONSE_CODES = Object.freeze(VALID_CODES)
/**
* @typedef {ControlParams} ServerSideSortingResponseParams
* @property {ServerSideSortingResponseControlResult | Buffer} value
*/
/**
* Creates a new server side sorting response control.
*
* @param {ServerSideSortingResponseParams} [options]
*/
constructor (options = {}) {
options.type = ServerSideSortingResponseControl.OID
options.criticality = false
super(options)
this.value = {}
if (hasOwn(options, 'value') === false || !options.value) {
return
}
const value = options.value
if (Buffer.isBuffer(value)) {
this.#parse(value)
} else if (isObject(value)) {
if (VALID_CODES.has(value.result) === false) {
throw new Error('Invalid result code')
}
if (hasOwn(value, 'failedAttribute') && (typeof value.failedAttribute) !== 'string') {
throw new Error('failedAttribute must be String')
}
this.value = value
} else {
throw new TypeError('options.value must be a Buffer or Object')
}
}
get value () {
return this._value
}
set value (obj) {
this._value = Object.assign({}, this._value, obj)
}
#parse (buffer) {
const ber = new BerReader(buffer)
/* istanbul ignore else */
if (ber.readSequence(0x30)) {
this._value = {}
this._value.result = ber.readEnumeration()
/* istanbul ignore else */
if (ber.peek() === 0x80) {
this._value.failedAttribute = ber.readString(0x80)
}
}
}
_pojo (obj) {
obj.value = this.value
return obj
}
_toBer (ber) {
if (!this._value || Object.keys(this._value).length === 0) { return }
const writer = new BerWriter()
writer.startSequence(0x30)
writer.writeEnumeration(this.value.result)
/* istanbul ignore else */
if (this.value.result !== RESULT_CODES.SUCCESS && this.value.failedAttribute) {
writer.writeString(this.value.failedAttribute, 0x80)
}
writer.endSequence()
ber.writeBuffer(writer.buffer, 0x04)
}
}
module.exports = ServerSideSortingResponseControl

View File

@ -0,0 +1,125 @@
'use strict'
const tap = require('tap')
const { BerWriter } = require('@ldapjs/asn1')
const SSSRC = require('./server-side-sorting-response-control')
const Control = require('../control')
tap.test('constructor', t => {
t.test('new no args', async t => {
const control = new SSSRC()
t.ok(control)
t.type(control, SSSRC)
t.type(control, Control)
t.equal(control.type, SSSRC.OID)
t.same(control.value, {})
})
t.test('new with args', async t => {
const control = new SSSRC({
type: '1.2.840.113556.1.4.474',
criticality: true,
value: {
result: SSSRC.RESPONSE_CODES.get('OPERATIONS_ERROR'),
failedAttribute: 'foo'
}
})
t.ok(control)
t.equal(control.type, '1.2.840.113556.1.4.474')
t.equal(control.criticality, false)
t.same(control.value, {
result: 1,
failedAttribute: 'foo'
})
})
t.test('with value buffer', async t => {
const value = new BerWriter()
value.startSequence(0x30)
value.writeEnumeration(1)
value.writeString('foo', 0x80)
value.endSequence()
const control = new SSSRC({ value: value.buffer })
t.same(control.value, {
result: 1,
failedAttribute: 'foo'
})
})
t.test('throws for bad value', async t => {
t.throws(() => new SSSRC({ value: 42 }))
t.throws(() => new SSSRC({ value: {} }))
t.throws(() => new SSSRC({
value: {
result: 1,
failedAttribute: 42
}
}))
})
t.end()
})
tap.test('pojo', t => {
t.test('adds control value', async t => {
const control = new SSSRC()
t.same(control.pojo, {
type: SSSRC.OID,
criticality: false,
value: {}
})
})
t.test('_pojo', async t => {
const control = new SSSRC()
t.strictSame(control._pojo({ value: 'change_me' }), {
value: {}
})
})
t.end()
})
tap.test('toBer', t => {
t.test('converts empty instance to BER', async t => {
const target = new BerWriter()
target.startSequence()
target.writeString(SSSRC.OID)
target.writeBoolean(false) // Control.criticality
target.endSequence()
const control = new SSSRC()
const ber = control.toBer()
t.equal(Buffer.compare(ber.buffer, target.buffer), 0)
})
t.test('converts full instance to BER', async t => {
const target = new BerWriter()
target.startSequence()
target.writeString(SSSRC.OID)
target.writeBoolean(false) // Control.criticality
const value = new BerWriter()
value.startSequence(0x30)
value.writeEnumeration(1)
value.writeString('foo', 0x80)
value.endSequence()
target.writeBuffer(value.buffer, 0x04)
target.endSequence()
const control = new SSSRC({
value: {
result: SSSRC.RESPONSE_CODES.get('OPERATIONS_ERROR'),
failedAttribute: 'foo'
}
})
const ber = control.toBer()
t.equal(Buffer.compare(ber.buffer, target.buffer), 0)
})
t.end()
})

View File

@ -0,0 +1,116 @@
'use strict'
const { BerReader, BerWriter } = require('@ldapjs/asn1')
const isObject = require('../is-object')
const hasOwn = require('../has-own')
const Control = require('../control')
/**
* @typedef {object} VirtualListViewControlValue
* @property {number} beforeCount
* @property {number} afterCount
*
*/
/**
* Implements:
* https://datatracker.ietf.org/doc/html/draft-ietf-ldapext-ldapv3-vlv-07#section-6.1
*
* @extends Control
*/
class VirtualListViewRequestControl extends Control {
static OID = '2.16.840.1.113730.3.4.9'
/**
* @typedef {ControlParams} VirtualListViewRequestParams
* @property {Buffer|VirtualListViewControlValue} [value]
*/
/**
* @param {VirtualListViewRequestParams} [options]
*/
constructor (options = {}) {
options.type = VirtualListViewRequestControl.OID
super(options)
if (hasOwn(options, 'value') === false) {
// return
throw Error('control is not enabled')
}
if (Buffer.isBuffer(options.value)) {
this.#parse(options.value)
} else if (isObject(options.value)) {
if (Object.prototype.hasOwnProperty.call(options.value, 'beforeCount') === false) {
throw new Error('Missing required key: beforeCount')
}
if (Object.prototype.hasOwnProperty.call(options.value, 'afterCount') === false) {
throw new Error('Missing required key: afterCount')
}
this._value = options.value
} else {
throw new TypeError('options.value must be a Buffer or Object')
}
throw Error('control is not enabled')
}
get value () {
return this._value
}
set value (items) {
if (Buffer.isBuffer(items) === true) return
if (Array.isArray(items) === false) {
this._value = [items]
return
}
this._value = items
}
#parse (buffer) {
const ber = new BerReader(buffer)
if (ber.readSequence()) {
this._value = {}
this._value.beforeCount = ber.readInt()
this._value.afterCount = ber.readInt()
if (ber.peek() === 0xa0) {
if (ber.readSequence(0xa0)) {
this._value.targetOffset = ber.readInt()
this._value.contentCount = ber.readInt()
}
}
if (ber.peek() === 0x81) {
this._value.greaterThanOrEqual = ber.readString(0x81)
}
return true
}
return false
}
_pojo (obj) {
obj.value = this.value
return obj
}
_toBer (ber) {
if (!this._value || this._value.length === 0) {
return
}
const writer = new BerWriter()
writer.startSequence(0x30)
writer.writeInt(this._value.beforeCount)
writer.writeInt(this._value.afterCount)
if (this._value.targetOffset !== undefined) {
writer.startSequence(0xa0)
writer.writeInt(this._value.targetOffset)
writer.writeInt(this._value.contentCount)
writer.endSequence()
} else if (this._value.greaterThanOrEqual !== undefined) {
writer.writeString(this._value.greaterThanOrEqual, 0x81)
}
writer.endSequence()
ber.writeBuffer(writer.buffer, 0x04)
}
}
module.exports = VirtualListViewRequestControl

View File

@ -0,0 +1,110 @@
'use strict'
const tap = require('tap')
tap.test('stubbed', async t => {
t.pass()
})
/**
* This test is disabled. The commented code below is directly copied from
* the original test file in the core `node-ldapjs` repo. The actual test
* suite should follow the patterns of the
* server-side-sorting-request-control.test.js test suite.
*
* See https://github.com/ldapjs/node-ldapjs/pull/797#issuecomment-1094132289
*/
// 'use strict'
// const { test } = require('tap')
// const { BerReader, BerWriter } = require('@ldapjs/asn1')
// const { getControl, VirtualListViewRequestControl: VLVRControl } = require('../../lib')
// test('VLV request - new no args', function (t) {
// t.ok(new VLVRControl())
// t.end()
// })
// test('VLV request - new with args', function (t) {
// const c = new VLVRControl({
// criticality: true,
// value: {
// beforeCount: 0,
// afterCount: 3,
// targetOffset: 1,
// contentCount: 0
// }
// })
// t.ok(c)
// t.equal(c.type, '2.16.840.1.113730.3.4.9')
// t.ok(c.criticality)
// t.equal(c.value.beforeCount, 0)
// t.equal(c.value.afterCount, 3)
// t.equal(c.value.targetOffset, 1)
// t.equal(c.value.contentCount, 0)
// t.end()
// })
// test('VLV request - toBer - with offset', function (t) {
// const vlvc = new VLVRControl({
// criticality: true,
// value: {
// beforeCount: 0,
// afterCount: 3,
// targetOffset: 1,
// contentCount: 0
// }
// })
// const ber = new BerWriter()
// vlvc.toBer(ber)
// const c = getControl(new BerReader(ber.buffer))
// t.ok(c)
// t.equal(c.type, '2.16.840.1.113730.3.4.9')
// t.ok(c.criticality)
// t.equal(c.value.beforeCount, 0)
// t.equal(c.value.afterCount, 3)
// t.equal(c.value.targetOffset, 1)
// t.equal(c.value.contentCount, 0)
// t.end()
// })
// test('VLV request - toBer - with assertion', function (t) {
// const vlvc = new VLVRControl({
// criticality: true,
// value: {
// beforeCount: 0,
// afterCount: 3,
// greaterThanOrEqual: '*foo*'
// }
// })
// const ber = new BerWriter()
// vlvc.toBer(ber)
// const c = getControl(new BerReader(ber.buffer))
// t.ok(c)
// t.equal(c.type, '2.16.840.1.113730.3.4.9')
// t.ok(c.criticality)
// t.equal(c.value.beforeCount, 0)
// t.equal(c.value.afterCount, 3)
// t.equal(c.value.greaterThanOrEqual, '*foo*')
// t.end()
// })
// test('VLV request - toBer - empty', function (t) {
// const vlvc = new VLVRControl()
// const ber = new BerWriter()
// vlvc.toBer(ber)
// const c = getControl(new BerReader(ber.buffer))
// t.ok(c)
// t.equal(c.type, '2.16.840.1.113730.3.4.9')
// t.equal(c.criticality, false)
// t.notOk(c.value.result)
// t.end()
// })

View File

@ -0,0 +1,150 @@
'use strict'
const { Ber, BerReader, BerWriter } = require('@ldapjs/asn1')
const isObject = require('../is-object')
const hasOwn = require('../has-own')
const Control = require('../control')
const { resultCodes: RESULT_CODES } = require('@ldapjs/protocol')
const validCodeNames = [
'SUCCESS',
'OPERATIONS_ERROR',
'UNWILLING_TO_PERFORM',
'INSUFFICIENT_ACCESS_RIGHTS',
'BUSY',
'TIME_LIMIT_EXCEEDED',
'STRONGER_AUTH_REQUIRED',
'ADMIN_LIMIT_EXCEEDED',
'SORT_CONTROL_MISSING',
'OFFSET_RANGE_ERROR',
'CONTROL_ERROR',
'OTHER'
]
const filteredCodes = Object.entries(RESULT_CODES).filter(([k, v]) => validCodeNames.includes(k))
const VALID_CODES = new Map([
...filteredCodes,
...filteredCodes.map(([k, v]) => { return [v, k] })
])
// TODO: complete this doc block based on the "implements" spec link
/**
* @typedef {object} VirtualListViewResponseControlValue
* @property {number} result A valid LDAP response code for the control.
*/
/**
* Implements:
* https://datatracker.ietf.org/doc/html/draft-ietf-ldapext-ldapv3-vlv-07#section-6.2
*
* @extends Control
*/
class VirtualListViewResponseControl extends Control {
static OID = '2.16.840.1.113730.3.4.10'
/**
* A map of possible response codes. Includes `CODE => VALUE` and
* `VALUE => CODE`. For example, `RESPONSE_CODES.get(0)` returns
* `LDAP_SUCCESS`, and `RESPONSE_CODES.get('LDAP_SUCCESS')` returns `0`.
*/
static RESPONSE_CODES = Object.freeze(VALID_CODES)
/**
* @typedef {ControlParams} VirtualListViewResponseParams
* @property {Buffer|VirtualListViewResponseControlValue} [value]
*/
/**
* @param {VirtualListViewResponseParams} options
*/
constructor (options = {}) {
options.type = VirtualListViewResponseControl.OID
options.criticality = false
super(options)
this.value = {}
if (hasOwn(options, 'value') === false || !options.value) {
// return
throw Error('control not enabled')
}
const value = options.value
if (Buffer.isBuffer(value)) {
this.#parse(options.value)
} else if (isObject(value)) {
if (VALID_CODES.has(value.result) === false) {
throw new Error('Invalid result code')
}
this.value = options.value
} else {
throw new TypeError('options.value must be a Buffer or Object')
}
throw Error('control not enabled')
}
get value () {
return this._value
}
set value (obj) {
this._value = Object.assign({}, this._value, obj)
}
#parse (buffer) {
const ber = new BerReader(buffer)
if (ber.readSequence()) {
this._value = {}
if (ber.peek(0x02)) {
this._value.targetPosition = ber.readInt()
}
if (ber.peek(0x02)) {
this._value.contentCount = ber.readInt()
}
this._value.result = ber.readEnumeration()
this._value.cookie = ber.readString(Ber.OctetString, true)
// readString returns '' instead of a zero-length buffer
if (!this._value.cookie) {
this._value.cookie = Buffer.alloc(0)
}
return true
}
return false
}
_pojo (obj) {
obj.value = this.value
return obj
}
_toBer (ber) {
if (this.value.length === 0) { return }
const writer = new BerWriter()
writer.startSequence()
if (this.value.targetPosition !== undefined) {
writer.writeInt(this.value.targetPosition)
}
if (this.value.contentCount !== undefined) {
writer.writeInt(this.value.contentCount)
}
writer.writeEnumeration(this.value.result)
if (this.value.cookie && this.value.cookie.length > 0) {
writer.writeBuffer(this.value.cookie, Ber.OctetString)
} else {
writer.writeString('') // writeBuffer rejects zero-length buffers
}
writer.endSequence()
ber.writeBuffer(writer.buffer, 0x04)
}
}
module.exports = VirtualListViewResponseControl

View File

@ -0,0 +1,84 @@
'use strict'
const tap = require('tap')
tap.test('stubbed', async t => {
t.pass()
})
/**
* This test is disabled. The commented code below is directly copied from
* the original test file in the core `node-ldapjs` repo. The actual test
* suite should follow the patterns of the
* server-side-sorting-response-control.test.js test suite.
*
* See https://github.com/ldapjs/node-ldapjs/pull/797#issuecomment-1094132289
*/
// 'use strict'
// const { test } = require('tap')
// const { BerReader, BerWriter } = require('@ldapjs/asn1')
// const ldap = require('../../lib')
// const { getControl, VirtualListViewResponseControl: VLVResponseControl } = require('../../lib')
// const OID = '2.16.840.1.113730.3.4.10'
// test('VLV response - new no args', function (t) {
// const c = new VLVResponseControl()
// t.ok(c)
// t.equal(c.type, OID)
// t.equal(c.criticality, false)
// t.end()
// })
// test('VLV response - new with args', function (t) {
// const c = new VLVResponseControl({
// criticality: true,
// value: {
// result: ldap.LDAP_SUCCESS,
// targetPosition: 0,
// contentCount: 10
// }
// })
// t.ok(c)
// t.equal(c.type, OID)
// t.equal(c.criticality, false)
// t.equal(c.value.result, ldap.LDAP_SUCCESS)
// t.equal(c.value.targetPosition, 0)
// t.equal(c.value.contentCount, 10)
// t.end()
// })
// test('VLV response - toBer', function (t) {
// const vlpc = new VLVResponseControl({
// value: {
// targetPosition: 0,
// contentCount: 10,
// result: ldap.LDAP_SUCCESS
// }
// })
// const ber = new BerWriter()
// vlpc.toBer(ber)
// const c = getControl(new BerReader(ber.buffer))
// t.ok(c)
// t.equal(c.type, OID)
// t.equal(c.criticality, false)
// t.equal(c.value.result, ldap.LDAP_SUCCESS)
// t.equal(c.value.targetPosition, 0)
// t.equal(c.value.contentCount, 10)
// t.end()
// })
// test('VLV response - toBer - empty', function (t) {
// const vlpc = new VLVResponseControl()
// const ber = new BerWriter()
// vlpc.toBer(ber)
// const c = getControl(new BerReader(ber.buffer))
// t.ok(c)
// t.equal(c.type, OID)
// t.equal(c.criticality, false)
// t.notOk(c.value.result)
// t.end()
// })

5
node_modules/@ldapjs/controls/lib/has-own.js generated vendored Normal file
View File

@ -0,0 +1,5 @@
'use strict'
module.exports = function hasOwn (obj, prop) {
return Object.prototype.hasOwnProperty.call(obj, prop)
}

5
node_modules/@ldapjs/controls/lib/is-object.js generated vendored Normal file
View File

@ -0,0 +1,5 @@
'use strict'
module.exports = function isObject (input) {
return Object.prototype.toString.call(input) === '[object Object]'
}