@ldapjs/filter
Version:
API for handling LDAP-style filters
189 lines (158 loc) • 4.68 kB
JavaScript
'use strict'
const FilterString = require('../filter-string')
const { search } = require('@ldapjs/protocol')
const { BerReader } = require('@ldapjs/asn1')
/**
* Represents an extensible LDAP filter as defined in
* https://www.rfc-editor.org/rfc/rfc2251.html#section-4.5.1.
*/
class ExtensibleFilter extends FilterString {
#dnAttributes
#rule
/**
* @typedef {FilterStringParams} ExtensibleParams
* @property {string|undefined} [attribute=''] Name of the attribute to
* match against, if any.
* @property {*} [value=''] Value to test for.
* @property {string} [rule] A matching rule OID if a speficic matching
* rule is to be used.
* @property {string|undefined} [matchType=''] An alias for `attribute`.
* This parameter is provided for backward compatibility. Use `attribute`
* instead.
* @property {boolean} [dnAttributes=false] Indicates if all attributes
* of a matching distinguished name should be tested.
*/
/**
* @param {ExtensibleParams} input
*
* @throws When `dnAttbributes` or `rule` are not valid.
*/
constructor ({ attribute, value, rule, matchType, dnAttributes = false } = {}) {
// `attribute` and `matchType` are allowed to be `undefined` per the
// RFC. When either is not provided, an empty string will be used.
// This is covered in the `toString` and `toBer` methods.
if (typeof dnAttributes !== 'boolean') {
throw Error('dnAttributes must be a boolean value')
}
if (rule && typeof rule !== 'string') {
throw Error('rule must be a string')
}
super({ attribute, value })
if (matchType !== undefined) {
this.attribute = matchType
}
this.#dnAttributes = dnAttributes
this.#rule = rule
this.value = value ?? ''
Object.defineProperties(this, {
TAG: { value: search.FILTER_EXT },
type: { value: 'ExtensibleFilter' }
})
}
get json () {
return {
type: this.type,
matchRule: this.#rule,
matchType: this.attribute,
matchValue: this.value,
dnAttributes: this.#dnAttributes
}
}
get dnAttributes () {
return this.#dnAttributes
}
get matchingRule () {
return this.#rule
}
get matchValue () {
return this.value
}
get matchType () {
return this.attribute
}
toString () {
let result = '('
if (this.attribute) {
result += this.attribute
}
result += ':'
if (this.#dnAttributes === true) {
result += 'dn:'
}
if (this.#rule) {
result += this.#rule + ':'
}
result += '=' + this.value + ')'
return result
}
/**
* Not implemented.
*
* @throws In all cases.
*/
matches () {
throw Error('not implemented')
}
_toBer (ber) {
if (this.#rule) { ber.writeString(this.#rule, 0x81) }
if (this.attribute) { ber.writeString(this.attribute, 0x82) }
ber.writeString(this.value, 0x83)
if (this.#dnAttributes === true) {
ber.writeBoolean(this.#dnAttributes, 0x84)
}
return ber
}
/**
* Parses a BER encoded `Buffer` and returns a new filter.
*
* @param {Buffer} buffer BER encoded buffer.
*
* @throws When the buffer does not start with the proper BER tag, or an
* invalid context specific tag is encountered.
*
* @returns {ExtensibleFilter}
*/
static parse (buffer) {
const reader = new BerReader(buffer)
const seq = reader.readSequence()
if (seq !== search.FILTER_EXT) {
const expected = '0x' + search.FILTER_EXT.toString(16).padStart(2, '0')
const found = '0x' + seq.toString(16).padStart(2, '0')
throw Error(`expected extensible filter sequence ${expected}, got ${found}`)
}
let rule
let attribute
let value
let dnAttributes
// Must set end outside of loop as the reader will update the
// length property as the buffer is read.
const end = reader.buffer.length
while (reader.offset < end) {
// Read the context specific tag and act accordingly.
const tag = reader.peek()
switch (tag) {
case 0x81: {
rule = reader.readString(tag)
break
}
case 0x82: {
attribute = reader.readString(tag)
break
}
case 0x83: {
value = reader.readString(tag)
break
}
case 0x84: {
dnAttributes = reader.readBoolean(tag)
break
}
default: {
throw Error('invalid extensible filter type: 0x' + tag.toString(16).padStart(2, '0'))
}
}
}
return new ExtensibleFilter({ attribute, value, rule, dnAttributes })
}
}
module.exports = ExtensibleFilter