First commit
This commit is contained in:
344
node_modules/@ldapjs/attribute/index.js
generated
vendored
Normal file
344
node_modules/@ldapjs/attribute/index.js
generated
vendored
Normal file
@ -0,0 +1,344 @@
|
||||
'use strict'
|
||||
|
||||
const { core: { LBER_SET } } = require('@ldapjs/protocol')
|
||||
const {
|
||||
BerTypes,
|
||||
BerReader,
|
||||
BerWriter
|
||||
} = require('@ldapjs/asn1')
|
||||
const warning = require('./lib/deprecations')
|
||||
|
||||
/**
|
||||
* Represents an LDAP attribute and its associated values as defined by
|
||||
* https://www.rfc-editor.org/rfc/rfc4512#section-2.5.
|
||||
*/
|
||||
class Attribute {
|
||||
#buffers = []
|
||||
#type
|
||||
|
||||
/**
|
||||
* @param {object} options
|
||||
* @param {string} [options.type=''] The name of the attribute, e.g. "cn" for
|
||||
* the common name attribute. For binary attributes, include the `;binary`
|
||||
* option, e.g. `foo;binary`.
|
||||
* @param {string|string[]} [options.values] Either a single value for the
|
||||
* attribute, or a set of values for the attribute.
|
||||
*/
|
||||
constructor (options = {}) {
|
||||
if (options.type && typeof (options.type) !== 'string') {
|
||||
throw TypeError('options.type must be a string')
|
||||
}
|
||||
this.type = options.type || ''
|
||||
|
||||
const values = options.values || options.vals || []
|
||||
if (options.vals) {
|
||||
warning.emit('LDAP_ATTRIBUTE_DEP_001')
|
||||
}
|
||||
this.values = values
|
||||
}
|
||||
|
||||
get [Symbol.toStringTag] () {
|
||||
return 'LdapAttribute'
|
||||
}
|
||||
|
||||
/**
|
||||
* A copy of the buffers that represent the values for the attribute.
|
||||
*
|
||||
* @returns {Buffer[]}
|
||||
*/
|
||||
get buffers () {
|
||||
return this.#buffers.slice(0)
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes the attribute to a plain JavaScript object representation.
|
||||
*
|
||||
* @returns {object}
|
||||
*/
|
||||
get pojo () {
|
||||
return {
|
||||
type: this.type,
|
||||
values: this.values
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The attribute name as provided during construction.
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
get type () {
|
||||
return this.#type
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the attribute name.
|
||||
*
|
||||
* @param {string} name
|
||||
*/
|
||||
set type (name) {
|
||||
this.#type = name
|
||||
}
|
||||
|
||||
/**
|
||||
* The set of attribute values as strings.
|
||||
*
|
||||
* @returns {string[]}
|
||||
*/
|
||||
get values () {
|
||||
const encoding = _bufferEncoding(this.#type)
|
||||
return this.#buffers.map(function (v) {
|
||||
return v.toString(encoding)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the attribute's associated values. This will replace any values set
|
||||
* at construction time.
|
||||
*
|
||||
* @param {string|string[]} vals
|
||||
*/
|
||||
set values (vals) {
|
||||
if (Array.isArray(vals) === false) {
|
||||
return this.addValue(vals)
|
||||
}
|
||||
for (const value of vals) {
|
||||
this.addValue(value)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use {@link values} instead.
|
||||
*
|
||||
* @deprecated
|
||||
* @returns {string[]}
|
||||
*/
|
||||
get vals () {
|
||||
warning.emit('LDAP_ATTRIBUTE_DEP_003')
|
||||
return this.values
|
||||
}
|
||||
|
||||
/**
|
||||
* Use {@link values} instead.
|
||||
*
|
||||
* @deprecated
|
||||
* @param {string|string[]} values
|
||||
*/
|
||||
set vals (values) {
|
||||
warning.emit('LDAP_ATTRIBUTE_DEP_003')
|
||||
this.values = values
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a new value, or set of values, to the current set of values
|
||||
* associated with the attributes.
|
||||
*
|
||||
* @param {string|string[]} value
|
||||
*/
|
||||
addValue (value) {
|
||||
if (Buffer.isBuffer(value)) {
|
||||
this.#buffers.push(value)
|
||||
} else {
|
||||
this.#buffers.push(
|
||||
Buffer.from(value + '', _bufferEncoding(this.#type))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces instance properties with those found in a given BER.
|
||||
*
|
||||
* @param {import('@ldapjs/asn1').BerReader} ber
|
||||
*
|
||||
* @deprecated Use {@link fromBer} instead.
|
||||
*/
|
||||
parse (ber) {
|
||||
const attr = Attribute.fromBer(ber)
|
||||
this.#type = attr.type
|
||||
this.values = attr.values
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the {@link Attribute} instance to a {@link BerReader} capable of
|
||||
* being used in an LDAP message.
|
||||
*
|
||||
* @returns {BerReader}
|
||||
*/
|
||||
toBer () {
|
||||
const ber = new BerWriter()
|
||||
|
||||
ber.startSequence()
|
||||
ber.writeString(this.type)
|
||||
ber.startSequence(LBER_SET)
|
||||
|
||||
if (this.#buffers.length > 0) {
|
||||
for (const buffer of this.#buffers) {
|
||||
ber.writeByte(BerTypes.OctetString)
|
||||
ber.writeLength(buffer.length)
|
||||
ber.appendBuffer(buffer)
|
||||
}
|
||||
} else {
|
||||
ber.writeStringArray([])
|
||||
}
|
||||
ber.endSequence()
|
||||
ber.endSequence()
|
||||
|
||||
return new BerReader(ber.buffer)
|
||||
}
|
||||
|
||||
toJSON () {
|
||||
return this.pojo
|
||||
}
|
||||
|
||||
/**
|
||||
* Given two {@link Attribute} instances, determine if they are equal or
|
||||
* different.
|
||||
*
|
||||
* @param {Attribute} attr1 The first object to compare.
|
||||
* @param {Attribute} attr2 The second object to compare.
|
||||
*
|
||||
* @returns {number} `0` if the attributes are equal in value, `-1` if
|
||||
* `attr1` should come before `attr2` when sorted, and `1` if `attr2` should
|
||||
* come before `attr1` when sorted.
|
||||
*
|
||||
* @throws When either input object is not an {@link Attribute}.
|
||||
*/
|
||||
static compare (attr1, attr2) {
|
||||
if (Attribute.isAttribute(attr1) === false || Attribute.isAttribute(attr2) === false) {
|
||||
throw TypeError('can only compare Attribute instances')
|
||||
}
|
||||
|
||||
if (attr1.type < attr2.type) return -1
|
||||
if (attr1.type > attr2.type) return 1
|
||||
|
||||
const aValues = attr1.values
|
||||
const bValues = attr2.values
|
||||
if (aValues.length < bValues.length) return -1
|
||||
if (aValues.length > bValues.length) return 1
|
||||
|
||||
for (let i = 0; i < aValues.length; i++) {
|
||||
if (aValues[i] < bValues[i]) return -1
|
||||
if (aValues[i] > bValues[i]) return 1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
/**
|
||||
* Read a BER representation of an attribute, and its values, and
|
||||
* create a new {@link Attribute} instance. The BER must start
|
||||
* at the beginning of a sequence.
|
||||
*
|
||||
* @param {import('@ldapjs/asn1').BerReader} ber
|
||||
*
|
||||
* @returns {Attribute}
|
||||
*/
|
||||
static fromBer (ber) {
|
||||
ber.readSequence()
|
||||
|
||||
const type = ber.readString()
|
||||
const values = []
|
||||
|
||||
// If the next byte represents a BER "SET" sequence...
|
||||
if (ber.peek() === LBER_SET) {
|
||||
// .. read that sequence ...
|
||||
/* istanbul ignore else */
|
||||
if (ber.readSequence(LBER_SET)) {
|
||||
const end = ber.offset + ber.length
|
||||
// ... and read all values in that set.
|
||||
while (ber.offset < end) {
|
||||
values.push(
|
||||
ber.readString(BerTypes.OctetString, true)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const result = new Attribute({
|
||||
type,
|
||||
values
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
/**
|
||||
* Given an object of attribute types mapping to attribute values, construct
|
||||
* a set of Attributes.
|
||||
*
|
||||
* @param {object} obj Each key is an attribute type, and each value is an
|
||||
* attribute value or set of values.
|
||||
*
|
||||
* @returns {Attribute[]}
|
||||
*
|
||||
* @throws If an attribute cannot be constructed correctly.
|
||||
*/
|
||||
static fromObject (obj) {
|
||||
const attributes = []
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
if (Array.isArray(value) === true) {
|
||||
attributes.push(new Attribute({
|
||||
type: key,
|
||||
values: value
|
||||
}))
|
||||
} else {
|
||||
attributes.push(new Attribute({
|
||||
type: key,
|
||||
values: [value]
|
||||
}))
|
||||
}
|
||||
}
|
||||
return attributes
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if an object represents an {@link Attribute}.
|
||||
*
|
||||
* @param {object} attr The object to check. It can be an instance of
|
||||
* {@link Attribute} or a plain JavaScript object that looks like an
|
||||
* {@link Attribute} and can be passed to the constructor to create one.
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
static isAttribute (attr) {
|
||||
if (typeof attr !== 'object') {
|
||||
return false
|
||||
}
|
||||
|
||||
if (Object.prototype.toString.call(attr) === '[object LdapAttribute]') {
|
||||
return true
|
||||
}
|
||||
|
||||
const typeOk = typeof attr.type === 'string'
|
||||
let valuesOk = Array.isArray(attr.values)
|
||||
if (valuesOk === true) {
|
||||
for (const val of attr.values) {
|
||||
if (typeof val !== 'string' && Buffer.isBuffer(val) === false) {
|
||||
valuesOk = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if (typeOk === true && valuesOk === true) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Attribute
|
||||
|
||||
/**
|
||||
* Determine the encoding for values based upon whether the binary
|
||||
* option is set on the attribute.
|
||||
*
|
||||
* @param {string} type
|
||||
*
|
||||
* @returns {string} Either "utf8" for a plain string value, or "base64" for
|
||||
* a binary attribute.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
function _bufferEncoding (type) {
|
||||
return /;binary$/.test(type) ? 'base64' : 'utf8'
|
||||
}
|
||||
Reference in New Issue
Block a user