Replace messages with @ldapjs/messages
This commit is contained in:
parent
685465843d
commit
f18dee40a2
|
@ -4,6 +4,9 @@ module.exports = {
|
||||||
es2021: true,
|
es2021: true,
|
||||||
node: true
|
node: true
|
||||||
},
|
},
|
||||||
|
parserOptions: {
|
||||||
|
ecmaVersion: 'latest'
|
||||||
|
},
|
||||||
extends: [
|
extends: [
|
||||||
'standard'
|
'standard'
|
||||||
],
|
],
|
||||||
|
|
4
.npmrc
4
.npmrc
|
@ -1,7 +1,3 @@
|
||||||
# npm general settings
|
# npm general settings
|
||||||
package-lock=false
|
package-lock=false
|
||||||
legacy-peer-deps=true
|
legacy-peer-deps=true
|
||||||
|
|
||||||
# pnpm specific settings
|
|
||||||
hoist=false
|
|
||||||
public-hoist-pattern[]=*eslint*
|
|
||||||
|
|
|
@ -41,7 +41,6 @@ client is:
|
||||||
|connectTimeout |Milliseconds client should wait before timing out on TCP connections (Default: OS default)|
|
|connectTimeout |Milliseconds client should wait before timing out on TCP connections (Default: OS default)|
|
||||||
|tlsOptions |Additional options passed to TLS connection layer when connecting via `ldaps://` (See: The TLS docs for node.js)|
|
|tlsOptions |Additional options passed to TLS connection layer when connecting via `ldaps://` (See: The TLS docs for node.js)|
|
||||||
|idleTimeout |Milliseconds after last activity before client emits idle event|
|
|idleTimeout |Milliseconds after last activity before client emits idle event|
|
||||||
|strictDN |Force strict DN parsing for client methods (Default is true)|
|
|
||||||
|reconnect |Try to reconnect when the connection gets lost (Default is false)|
|
|reconnect |Try to reconnect when the connection gets lost (Default is false)|
|
||||||
|
|
||||||
### url
|
### url
|
||||||
|
@ -287,7 +286,7 @@ Responses inside callback of the `search` method are an `EventEmitter` where you
|
||||||
each `searchEntry` that comes back from the server. You will additionally be able to listen for a `searchRequest`
|
each `searchEntry` that comes back from the server. You will additionally be able to listen for a `searchRequest`
|
||||||
, `searchReference`, `error` and `end` event.
|
, `searchReference`, `error` and `end` event.
|
||||||
`searchRequest` is emitted immediately after every `SearchRequest` is sent with a `SearchRequest` parameter. You can do operations
|
`searchRequest` is emitted immediately after every `SearchRequest` is sent with a `SearchRequest` parameter. You can do operations
|
||||||
like `client.abandon` with `searchRequest.messageID` to abandon this search request. Note that the `error` event will
|
like `client.abandon` with `searchRequest.messageId` to abandon this search request. Note that the `error` event will
|
||||||
only be for client/TCP errors, not LDAP error codes like the other APIs. You'll want to check the LDAP status code
|
only be for client/TCP errors, not LDAP error codes like the other APIs. You'll want to check the LDAP status code
|
||||||
(likely for `0`) on the `end` event to assert success. LDAP search results can give you a lot of status codes, such as
|
(likely for `0`) on the `end` event to assert success. LDAP search results can give you a lot of status codes, such as
|
||||||
time or size exceeded, busy, inappropriate matching, etc., which is why this method doesn't try to wrap up the code
|
time or size exceeded, busy, inappropriate matching, etc., which is why this method doesn't try to wrap up the code
|
||||||
|
@ -306,7 +305,7 @@ client.search('o=example', opts, (err, res) => {
|
||||||
assert.ifError(err);
|
assert.ifError(err);
|
||||||
|
|
||||||
res.on('searchRequest', (searchRequest) => {
|
res.on('searchRequest', (searchRequest) => {
|
||||||
console.log('searchRequest: ', searchRequest.messageID);
|
console.log('searchRequest: ', searchRequest.messageId);
|
||||||
});
|
});
|
||||||
res.on('searchEntry', (entry) => {
|
res.on('searchEntry', (entry) => {
|
||||||
console.log('entry: ' + JSON.stringify(entry.object));
|
console.log('entry: ' + JSON.stringify(entry.object));
|
||||||
|
|
|
@ -197,7 +197,7 @@ authenticated as on this connection. If the client didn't bind, then a DN object
|
||||||
will be there defaulted to `cn=anonymous`.
|
will be there defaulted to `cn=anonymous`.
|
||||||
|
|
||||||
Additionally, request will have a `logId` parameter you can use to uniquely
|
Additionally, request will have a `logId` parameter you can use to uniquely
|
||||||
identify the request/connection pair in logs (includes the LDAP messageID).
|
identify the request/connection pair in logs (includes the LDAP messageId).
|
||||||
|
|
||||||
## Common Response Elements
|
## Common Response Elements
|
||||||
|
|
||||||
|
|
|
@ -1,54 +0,0 @@
|
||||||
// Copyright 2015 Joyent, Inc.
|
|
||||||
|
|
||||||
const assert = require('assert')
|
|
||||||
const util = require('util')
|
|
||||||
|
|
||||||
const isDN = require('./dn').DN.isDN
|
|
||||||
const isAttribute = require('./attribute').isAttribute
|
|
||||||
|
|
||||||
/// --- Helpers
|
|
||||||
|
|
||||||
// Copied from mcavage/node-assert-plus
|
|
||||||
function _assert (arg, type, name) {
|
|
||||||
name = name || type
|
|
||||||
throw new assert.AssertionError({
|
|
||||||
message: util.format('%s (%s) required', name, type),
|
|
||||||
actual: typeof (arg),
|
|
||||||
expected: type,
|
|
||||||
operator: '===',
|
|
||||||
stackStartFunction: _assert.caller
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// --- API
|
|
||||||
|
|
||||||
function stringDN (input, name) {
|
|
||||||
if (isDN(input) || typeof (input) === 'string') { return }
|
|
||||||
_assert(input, 'DN or string', name)
|
|
||||||
}
|
|
||||||
|
|
||||||
function optionalStringDN (input, name) {
|
|
||||||
if (input === undefined || isDN(input) || typeof (input) === 'string') { return }
|
|
||||||
_assert(input, 'DN or string', name)
|
|
||||||
}
|
|
||||||
|
|
||||||
function optionalDN (input, name) {
|
|
||||||
if (input !== undefined && !isDN(input)) { _assert(input, 'DN', name) }
|
|
||||||
}
|
|
||||||
|
|
||||||
function optionalArrayOfAttribute (input, name) {
|
|
||||||
if (input === undefined) { return }
|
|
||||||
if (!Array.isArray(input) ||
|
|
||||||
input.some(function (v) { return !isAttribute(v) })) {
|
|
||||||
_assert(input, 'array of Attribute', name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// --- Exports
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
stringDN: stringDN,
|
|
||||||
optionalStringDN: optionalStringDN,
|
|
||||||
optionalDN: optionalDN,
|
|
||||||
optionalArrayOfAttribute: optionalArrayOfAttribute
|
|
||||||
}
|
|
160
lib/attribute.js
160
lib/attribute.js
|
@ -1,160 +0,0 @@
|
||||||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
|
||||||
|
|
||||||
const assert = require('assert')
|
|
||||||
|
|
||||||
const asn1 = require('@ldapjs/asn1')
|
|
||||||
|
|
||||||
const Protocol = require('@ldapjs/protocol')
|
|
||||||
|
|
||||||
/// --- API
|
|
||||||
|
|
||||||
function Attribute (options) {
|
|
||||||
if (options) {
|
|
||||||
if (typeof (options) !== 'object') { throw new TypeError('options must be an object') }
|
|
||||||
if (options.type && typeof (options.type) !== 'string') { throw new TypeError('options.type must be a string') }
|
|
||||||
} else {
|
|
||||||
options = {}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.type = options.type || ''
|
|
||||||
this._vals = []
|
|
||||||
|
|
||||||
if (options.vals !== undefined && options.vals !== null) { this.vals = options.vals }
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = Attribute
|
|
||||||
|
|
||||||
Object.defineProperties(Attribute.prototype, {
|
|
||||||
buffers: {
|
|
||||||
get: function getBuffers () {
|
|
||||||
return this._vals
|
|
||||||
},
|
|
||||||
configurable: false
|
|
||||||
},
|
|
||||||
json: {
|
|
||||||
get: function getJson () {
|
|
||||||
return {
|
|
||||||
type: this.type,
|
|
||||||
vals: this.vals
|
|
||||||
}
|
|
||||||
},
|
|
||||||
configurable: false
|
|
||||||
},
|
|
||||||
vals: {
|
|
||||||
get: function getVals () {
|
|
||||||
const eType = _bufferEncoding(this.type)
|
|
||||||
return this._vals.map(function (v) {
|
|
||||||
return v.toString(eType)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
set: function setVals (vals) {
|
|
||||||
const self = this
|
|
||||||
this._vals = []
|
|
||||||
if (Array.isArray(vals)) {
|
|
||||||
vals.forEach(function (v) {
|
|
||||||
self.addValue(v)
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
self.addValue(vals)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
configurable: false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
Attribute.prototype.addValue = function addValue (val) {
|
|
||||||
if (Buffer.isBuffer(val)) {
|
|
||||||
this._vals.push(val)
|
|
||||||
} else {
|
|
||||||
this._vals.push(Buffer.from(val + '', _bufferEncoding(this.type)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* BEGIN JSSTYLED */
|
|
||||||
Attribute.compare = function compare (a, b) {
|
|
||||||
if (!(Attribute.isAttribute(a)) || !(Attribute.isAttribute(b))) {
|
|
||||||
throw new TypeError('can only compare Attributes')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (a.type < b.type) return -1
|
|
||||||
if (a.type > b.type) return 1
|
|
||||||
if (a.vals.length < b.vals.length) return -1
|
|
||||||
if (a.vals.length > b.vals.length) return 1
|
|
||||||
|
|
||||||
for (let i = 0; i < a.vals.length; i++) {
|
|
||||||
if (a.vals[i] < b.vals[i]) return -1
|
|
||||||
if (a.vals[i] > b.vals[i]) return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
/* END JSSTYLED */
|
|
||||||
|
|
||||||
Attribute.prototype.parse = function parse (ber) {
|
|
||||||
assert.ok(ber)
|
|
||||||
|
|
||||||
ber.readSequence()
|
|
||||||
this.type = ber.readString()
|
|
||||||
|
|
||||||
if (ber.peek() === Protocol.core.LBER_SET) {
|
|
||||||
if (ber.readSequence(Protocol.core.LBER_SET)) {
|
|
||||||
const end = ber.offset + ber.length
|
|
||||||
while (ber.offset < end) { this._vals.push(ber.readString(asn1.Ber.OctetString, true)) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
Attribute.prototype.toBer = function toBer (ber) {
|
|
||||||
assert.ok(ber)
|
|
||||||
|
|
||||||
ber.startSequence()
|
|
||||||
ber.writeString(this.type)
|
|
||||||
ber.startSequence(Protocol.core.LBER_SET)
|
|
||||||
if (this._vals.length) {
|
|
||||||
this._vals.forEach(function (b) {
|
|
||||||
ber.writeByte(asn1.Ber.OctetString)
|
|
||||||
ber.writeLength(b.length)
|
|
||||||
for (let i = 0; i < b.length; i++) { ber.writeByte(b[i]) }
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
ber.writeStringArray([])
|
|
||||||
}
|
|
||||||
ber.endSequence()
|
|
||||||
ber.endSequence()
|
|
||||||
|
|
||||||
return ber
|
|
||||||
}
|
|
||||||
|
|
||||||
Attribute.prototype.toString = function () {
|
|
||||||
return JSON.stringify(this.json)
|
|
||||||
}
|
|
||||||
|
|
||||||
Attribute.toBer = function (attr, ber) {
|
|
||||||
return Attribute.prototype.toBer.call(attr, ber)
|
|
||||||
}
|
|
||||||
|
|
||||||
Attribute.isAttribute = function isAttribute (attr) {
|
|
||||||
if (!attr || typeof (attr) !== 'object') {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if (attr instanceof Attribute) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if ((typeof (attr.toBer) === 'function') &&
|
|
||||||
(typeof (attr.type) === 'string') &&
|
|
||||||
(Array.isArray(attr.vals)) &&
|
|
||||||
(attr.vals.filter(function (item) {
|
|
||||||
return (typeof (item) === 'string' ||
|
|
||||||
Buffer.isBuffer(item))
|
|
||||||
}).length === attr.vals.length)) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
function _bufferEncoding (type) {
|
|
||||||
/* JSSTYLED */
|
|
||||||
return /;binary$/.test(type) ? 'base64' : 'utf8'
|
|
||||||
}
|
|
212
lib/change.js
212
lib/change.js
|
@ -1,212 +0,0 @@
|
||||||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
|
||||||
|
|
||||||
const assert = require('assert-plus')
|
|
||||||
|
|
||||||
const Attribute = require('./attribute')
|
|
||||||
|
|
||||||
/// --- API
|
|
||||||
|
|
||||||
function Change (options) {
|
|
||||||
if (options) {
|
|
||||||
assert.object(options)
|
|
||||||
assert.optionalString(options.operation)
|
|
||||||
} else {
|
|
||||||
options = {}
|
|
||||||
}
|
|
||||||
|
|
||||||
this._modification = false
|
|
||||||
this.operation = options.operation || options.type || 'add'
|
|
||||||
this.modification = options.modification || {}
|
|
||||||
}
|
|
||||||
Object.defineProperties(Change.prototype, {
|
|
||||||
operation: {
|
|
||||||
get: function getOperation () {
|
|
||||||
switch (this._operation) {
|
|
||||||
case 0x00: return 'add'
|
|
||||||
case 0x01: return 'delete'
|
|
||||||
case 0x02: return 'replace'
|
|
||||||
default:
|
|
||||||
throw new Error('0x' + this._operation.toString(16) + ' is invalid')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
set: function setOperation (val) {
|
|
||||||
assert.string(val)
|
|
||||||
switch (val.toLowerCase()) {
|
|
||||||
case 'add':
|
|
||||||
this._operation = 0x00
|
|
||||||
break
|
|
||||||
case 'delete':
|
|
||||||
this._operation = 0x01
|
|
||||||
break
|
|
||||||
case 'replace':
|
|
||||||
this._operation = 0x02
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
throw new Error('Invalid operation type: 0x' + val.toString(16))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
configurable: false
|
|
||||||
},
|
|
||||||
modification: {
|
|
||||||
get: function getModification () {
|
|
||||||
return this._modification
|
|
||||||
},
|
|
||||||
set: function setModification (val) {
|
|
||||||
if (Attribute.isAttribute(val)) {
|
|
||||||
this._modification = val
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// Does it have an attribute-like structure
|
|
||||||
if (Object.keys(val).length === 2 &&
|
|
||||||
typeof (val.type) === 'string' &&
|
|
||||||
Array.isArray(val.vals)) {
|
|
||||||
this._modification = new Attribute({
|
|
||||||
type: val.type,
|
|
||||||
vals: val.vals
|
|
||||||
})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const keys = Object.keys(val)
|
|
||||||
if (keys.length > 1) {
|
|
||||||
throw new Error('Only one attribute per Change allowed')
|
|
||||||
} else if (keys.length === 0) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const k = keys[0]
|
|
||||||
const _attr = new Attribute({ type: k })
|
|
||||||
if (Array.isArray(val[k])) {
|
|
||||||
val[k].forEach(function (v) {
|
|
||||||
_attr.addValue(v.toString())
|
|
||||||
})
|
|
||||||
} else if (Buffer.isBuffer(val[k])) {
|
|
||||||
_attr.addValue(val[k])
|
|
||||||
} else if (val[k] !== undefined && val[k] !== null) {
|
|
||||||
_attr.addValue(val[k].toString())
|
|
||||||
}
|
|
||||||
this._modification = _attr
|
|
||||||
},
|
|
||||||
configurable: false
|
|
||||||
},
|
|
||||||
json: {
|
|
||||||
get: function getJSON () {
|
|
||||||
return {
|
|
||||||
operation: this.operation,
|
|
||||||
modification: this._modification ? this._modification.json : {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
configurable: false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
Change.isChange = function isChange (change) {
|
|
||||||
if (!change || typeof (change) !== 'object') {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if ((change instanceof Change) ||
|
|
||||||
((typeof (change.toBer) === 'function') &&
|
|
||||||
(change.modification !== undefined) &&
|
|
||||||
(change.operation !== undefined))) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
Change.compare = function (a, b) {
|
|
||||||
if (!Change.isChange(a) || !Change.isChange(b)) { throw new TypeError('can only compare Changes') }
|
|
||||||
|
|
||||||
if (a.operation < b.operation) { return -1 }
|
|
||||||
if (a.operation > b.operation) { return 1 }
|
|
||||||
|
|
||||||
return Attribute.compare(a.modification, b.modification)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Apply a Change to properties of an object.
|
|
||||||
*
|
|
||||||
* @param {Object} change the change to apply.
|
|
||||||
* @param {Object} obj the object to apply it to.
|
|
||||||
* @param {Boolean} scalar convert single-item arrays to scalars. Default: false
|
|
||||||
*/
|
|
||||||
Change.apply = function apply (change, obj, scalar) {
|
|
||||||
assert.string(change.operation)
|
|
||||||
assert.string(change.modification.type)
|
|
||||||
assert.ok(Array.isArray(change.modification.vals))
|
|
||||||
assert.object(obj)
|
|
||||||
|
|
||||||
const type = change.modification.type
|
|
||||||
const vals = change.modification.vals
|
|
||||||
let data = obj[type]
|
|
||||||
if (data !== undefined) {
|
|
||||||
if (!Array.isArray(data)) {
|
|
||||||
data = [data]
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
data = []
|
|
||||||
}
|
|
||||||
switch (change.operation) {
|
|
||||||
case 'replace':
|
|
||||||
if (vals.length === 0) {
|
|
||||||
// replace empty is a delete
|
|
||||||
delete obj[type]
|
|
||||||
return obj
|
|
||||||
} else {
|
|
||||||
data = vals
|
|
||||||
}
|
|
||||||
break
|
|
||||||
case 'add': {
|
|
||||||
// add only new unique entries
|
|
||||||
const newValues = vals.filter(function (entry) {
|
|
||||||
return (data.indexOf(entry) === -1)
|
|
||||||
})
|
|
||||||
data = data.concat(newValues)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case 'delete':
|
|
||||||
data = data.filter(function (entry) {
|
|
||||||
return (vals.indexOf(entry) === -1)
|
|
||||||
})
|
|
||||||
if (data.length === 0) {
|
|
||||||
// Erase the attribute if empty
|
|
||||||
delete obj[type]
|
|
||||||
return obj
|
|
||||||
}
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if (scalar && data.length === 1) {
|
|
||||||
// store single-value outputs as scalars, if requested
|
|
||||||
obj[type] = data[0]
|
|
||||||
} else {
|
|
||||||
obj[type] = data
|
|
||||||
}
|
|
||||||
return obj
|
|
||||||
}
|
|
||||||
|
|
||||||
Change.prototype.parse = function (ber) {
|
|
||||||
assert.ok(ber)
|
|
||||||
|
|
||||||
ber.readSequence()
|
|
||||||
this._operation = ber.readEnumeration()
|
|
||||||
this._modification = new Attribute()
|
|
||||||
this._modification.parse(ber)
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
Change.prototype.toBer = function (ber) {
|
|
||||||
assert.ok(ber)
|
|
||||||
|
|
||||||
ber.startSequence()
|
|
||||||
ber.writeEnumeration(this._operation)
|
|
||||||
ber = this._modification.toBer(ber)
|
|
||||||
ber.endSequence()
|
|
||||||
|
|
||||||
return ber
|
|
||||||
}
|
|
||||||
|
|
||||||
/// --- Exports
|
|
||||||
|
|
||||||
module.exports = Change
|
|
|
@ -15,38 +15,37 @@ const vasync = require('vasync')
|
||||||
const assert = require('assert-plus')
|
const assert = require('assert-plus')
|
||||||
const VError = require('verror').VError
|
const VError = require('verror').VError
|
||||||
|
|
||||||
const Attribute = require('../attribute')
|
const Attribute = require('@ldapjs/attribute')
|
||||||
const Change = require('../change')
|
const Change = require('@ldapjs/change')
|
||||||
const Control = require('../controls/index').Control
|
const Control = require('../controls/index').Control
|
||||||
const { Control: LdapControl } = require('@ldapjs/controls')
|
const { Control: LdapControl } = require('@ldapjs/controls')
|
||||||
const SearchPager = require('./search_pager')
|
const SearchPager = require('./search_pager')
|
||||||
const Protocol = require('@ldapjs/protocol')
|
const Protocol = require('@ldapjs/protocol')
|
||||||
const dn = require('../dn')
|
const { DN } = require('@ldapjs/dn')
|
||||||
const errors = require('../errors')
|
const errors = require('../errors')
|
||||||
const filters = require('@ldapjs/filter')
|
const filters = require('@ldapjs/filter')
|
||||||
const messages = require('../messages')
|
const Parser = require('../messages/parser')
|
||||||
const url = require('../url')
|
const url = require('../url')
|
||||||
const CorkedEmitter = require('../corked_emitter')
|
const CorkedEmitter = require('../corked_emitter')
|
||||||
|
|
||||||
/// --- Globals
|
/// --- Globals
|
||||||
|
|
||||||
const AbandonRequest = messages.AbandonRequest
|
const messages = require('@ldapjs/messages')
|
||||||
const AddRequest = messages.AddRequest
|
const {
|
||||||
const BindRequest = messages.BindRequest
|
AbandonRequest,
|
||||||
const CompareRequest = messages.CompareRequest
|
AddRequest,
|
||||||
const DeleteRequest = messages.DeleteRequest
|
BindRequest,
|
||||||
const ExtendedRequest = messages.ExtendedRequest
|
CompareRequest,
|
||||||
const ModifyRequest = messages.ModifyRequest
|
DeleteRequest,
|
||||||
const ModifyDNRequest = messages.ModifyDNRequest
|
ExtensionRequest: ExtendedRequest,
|
||||||
const SearchRequest = messages.SearchRequest
|
ModifyRequest,
|
||||||
const UnbindRequest = messages.UnbindRequest
|
ModifyDnRequest: ModifyDNRequest,
|
||||||
const UnbindResponse = messages.UnbindResponse
|
SearchRequest,
|
||||||
|
UnbindRequest,
|
||||||
const LDAPResult = messages.LDAPResult
|
LdapResult: LDAPResult,
|
||||||
const SearchEntry = messages.SearchEntry
|
SearchResultEntry: SearchEntry,
|
||||||
const SearchReference = messages.SearchReference
|
SearchResultReference: SearchReference
|
||||||
// var SearchResponse = messages.SearchResponse
|
} = messages
|
||||||
const Parser = messages.Parser
|
|
||||||
|
|
||||||
const PresenceFilter = filters.PresenceFilter
|
const PresenceFilter = filters.PresenceFilter
|
||||||
|
|
||||||
|
@ -79,13 +78,11 @@ function validateControls (controls) {
|
||||||
return controls
|
return controls
|
||||||
}
|
}
|
||||||
|
|
||||||
function ensureDN (input, strict) {
|
function ensureDN (input) {
|
||||||
if (dn.DN.isDN(input)) {
|
if (DN.isDn(input)) {
|
||||||
return dn
|
return DN
|
||||||
} else if (strict) {
|
|
||||||
return dn.parse(input)
|
|
||||||
} else if (typeof (input) === 'string') {
|
} else if (typeof (input) === 'string') {
|
||||||
return input
|
return DN.fromString(input)
|
||||||
} else {
|
} else {
|
||||||
throw new Error('invalid DN')
|
throw new Error('invalid DN')
|
||||||
}
|
}
|
||||||
|
@ -137,7 +134,6 @@ function Client (options) {
|
||||||
failAfter: parseInt(rOpts.failAfter, 10) || Infinity
|
failAfter: parseInt(rOpts.failAfter, 10) || Infinity
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.strictDN = (options.strictDN !== undefined) ? options.strictDN : true
|
|
||||||
|
|
||||||
this.queue = requestQueueFactory({
|
this.queue = requestQueueFactory({
|
||||||
size: parseInt((options.queueSize || 0), 10),
|
size: parseInt((options.queueSize || 0), 10),
|
||||||
|
@ -178,13 +174,13 @@ module.exports = Client
|
||||||
* The callback will be invoked as soon as the data is flushed out to the
|
* The callback will be invoked as soon as the data is flushed out to the
|
||||||
* network, as there is never a response from abandon.
|
* network, as there is never a response from abandon.
|
||||||
*
|
*
|
||||||
* @param {Number} messageID the messageID to abandon.
|
* @param {Number} messageId the messageId to abandon.
|
||||||
* @param {Control} controls (optional) either a Control or [Control].
|
* @param {Control} controls (optional) either a Control or [Control].
|
||||||
* @param {Function} callback of the form f(err).
|
* @param {Function} callback of the form f(err).
|
||||||
* @throws {TypeError} on invalid input.
|
* @throws {TypeError} on invalid input.
|
||||||
*/
|
*/
|
||||||
Client.prototype.abandon = function abandon (messageID, controls, callback) {
|
Client.prototype.abandon = function abandon (messageId, controls, callback) {
|
||||||
assert.number(messageID, 'messageID')
|
assert.number(messageId, 'messageId')
|
||||||
if (typeof (controls) === 'function') {
|
if (typeof (controls) === 'function') {
|
||||||
callback = controls
|
callback = controls
|
||||||
controls = []
|
controls = []
|
||||||
|
@ -194,7 +190,7 @@ Client.prototype.abandon = function abandon (messageID, controls, callback) {
|
||||||
assert.func(callback, 'callback')
|
assert.func(callback, 'callback')
|
||||||
|
|
||||||
const req = new AbandonRequest({
|
const req = new AbandonRequest({
|
||||||
abandonID: messageID,
|
abandonId: messageId,
|
||||||
controls: controls
|
controls: controls
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -249,7 +245,7 @@ Client.prototype.add = function add (name, entry, controls, callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const req = new AddRequest({
|
const req = new AddRequest({
|
||||||
entry: ensureDN(name, this.strictDN),
|
entry: ensureDN(name),
|
||||||
attributes: entry,
|
attributes: entry,
|
||||||
controls: controls
|
controls: controls
|
||||||
})
|
})
|
||||||
|
@ -271,7 +267,12 @@ Client.prototype.bind = function bind (name,
|
||||||
controls,
|
controls,
|
||||||
callback,
|
callback,
|
||||||
_bypass) {
|
_bypass) {
|
||||||
if (typeof (name) !== 'string' && !(name instanceof dn.DN)) { throw new TypeError('name (string) required') }
|
if (
|
||||||
|
typeof (name) !== 'string' &&
|
||||||
|
Object.prototype.toString.call(name) !== '[object LdapDn]'
|
||||||
|
) {
|
||||||
|
throw new TypeError('name (string) required')
|
||||||
|
}
|
||||||
assert.optionalString(credentials, 'credentials')
|
assert.optionalString(credentials, 'credentials')
|
||||||
if (typeof (controls) === 'function') {
|
if (typeof (controls) === 'function') {
|
||||||
callback = controls
|
callback = controls
|
||||||
|
@ -326,7 +327,7 @@ Client.prototype.compare = function compare (name,
|
||||||
assert.func(callback, 'callback')
|
assert.func(callback, 'callback')
|
||||||
|
|
||||||
const req = new CompareRequest({
|
const req = new CompareRequest({
|
||||||
entry: ensureDN(name, this.strictDN),
|
entry: ensureDN(name),
|
||||||
attribute: attr,
|
attribute: attr,
|
||||||
value: value,
|
value: value,
|
||||||
controls: controls
|
controls: controls
|
||||||
|
@ -358,7 +359,7 @@ Client.prototype.del = function del (name, controls, callback) {
|
||||||
assert.func(callback, 'callback')
|
assert.func(callback, 'callback')
|
||||||
|
|
||||||
const req = new DeleteRequest({
|
const req = new DeleteRequest({
|
||||||
entry: ensureDN(name, this.strictDN),
|
entry: ensureDN(name),
|
||||||
controls: controls
|
controls: controls
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -469,7 +470,7 @@ Client.prototype.modify = function modify (name, change, controls, callback) {
|
||||||
assert.func(callback, 'callback')
|
assert.func(callback, 'callback')
|
||||||
|
|
||||||
const req = new ModifyRequest({
|
const req = new ModifyRequest({
|
||||||
object: ensureDN(name, this.strictDN),
|
object: ensureDN(name),
|
||||||
changes: changes,
|
changes: changes,
|
||||||
controls: controls
|
controls: controls
|
||||||
})
|
})
|
||||||
|
@ -505,18 +506,16 @@ Client.prototype.modifyDN = function modifyDN (name,
|
||||||
}
|
}
|
||||||
assert.func(callback)
|
assert.func(callback)
|
||||||
|
|
||||||
const DN = ensureDN(name)
|
const newDN = DN.fromString(newName)
|
||||||
// TODO: is non-strict handling desired here?
|
|
||||||
const newDN = dn.parse(newName)
|
|
||||||
|
|
||||||
const req = new ModifyDNRequest({
|
const req = new ModifyDNRequest({
|
||||||
entry: DN,
|
entry: DN.fromString(name),
|
||||||
deleteOldRdn: true,
|
deleteOldRdn: true,
|
||||||
controls: controls
|
controls: controls
|
||||||
})
|
})
|
||||||
|
|
||||||
if (newDN.length !== 1) {
|
if (newDN.length !== 1) {
|
||||||
req.newRdn = dn.parse(newDN.rdns.shift().toString())
|
req.newRdn = DN.fromString(newDN.shift().toString())
|
||||||
req.newSuperior = newDN
|
req.newSuperior = newDN
|
||||||
} else {
|
} else {
|
||||||
req.newRdn = newDN
|
req.newRdn = newDN
|
||||||
|
@ -594,7 +593,7 @@ Client.prototype.search = function search (base,
|
||||||
}
|
}
|
||||||
|
|
||||||
const self = this
|
const self = this
|
||||||
const baseDN = ensureDN(base, this.strictDN)
|
const baseDN = ensureDN(base)
|
||||||
|
|
||||||
function sendRequest (ctrls, emitter, cb) {
|
function sendRequest (ctrls, emitter, cb) {
|
||||||
const req = new SearchRequest({
|
const req = new SearchRequest({
|
||||||
|
@ -877,15 +876,46 @@ Client.prototype.connect = function connect () {
|
||||||
})
|
})
|
||||||
|
|
||||||
// The "router"
|
// The "router"
|
||||||
|
//
|
||||||
|
// This is invoked after the incoming BER has been parsed into a JavaScript
|
||||||
|
// object.
|
||||||
tracker.parser.on('message', function onMessage (message) {
|
tracker.parser.on('message', function onMessage (message) {
|
||||||
message.connection = self._socket
|
message.connection = self._socket
|
||||||
const callback = tracker.fetch(message.messageID)
|
const { message: trackedMessage, callback } = tracker.fetch(message.messageId)
|
||||||
|
|
||||||
if (!callback) {
|
if (!callback) {
|
||||||
log.error({ message: message.json }, 'unsolicited message')
|
log.error({ message: message.pojo }, 'unsolicited message')
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Some message types have narrower implementations and require extra
|
||||||
|
// parsing to be complete. In particular, ExtensionRequest messages will
|
||||||
|
// return responses that do not identify the request that generated them.
|
||||||
|
// Therefore, we have to match the response to the request and handle
|
||||||
|
// the extra processing accordingly.
|
||||||
|
switch (trackedMessage.type) {
|
||||||
|
case 'ExtensionRequest': {
|
||||||
|
const extensionType = ExtendedRequest.recognizedOIDs().lookupName(trackedMessage.requestName)
|
||||||
|
switch (extensionType) {
|
||||||
|
case 'PASSWORD_MODIFY': {
|
||||||
|
message = messages.PasswordModifyResponse.fromResponse(message)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'WHO_AM_I': {
|
||||||
|
message = messages.WhoAmIResponse.fromResponse(message)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
return callback(message)
|
return callback(message)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -1084,8 +1114,16 @@ Client.prototype._onClose = function _onClose (closeError) {
|
||||||
return cb(new ConnectionError(tracker.id + ' closed'))
|
return cb(new ConnectionError(tracker.id + ' closed'))
|
||||||
} else {
|
} else {
|
||||||
// Unbinds will be communicated as a success since we're closed
|
// Unbinds will be communicated as a success since we're closed
|
||||||
const unbind = new UnbindResponse({ messageID: msgid })
|
// TODO: we are faking this "UnbindResponse" object in order to make
|
||||||
unbind.status = 'unbind'
|
// tests pass. There is no such thing as an "unbind response" in the LDAP
|
||||||
|
// protocol. When the client is revamped, this logic should be removed.
|
||||||
|
// ~ jsumners 2023-02-16
|
||||||
|
const Unbind = class extends LDAPResult {
|
||||||
|
messageID = msgid
|
||||||
|
messageId = msgid
|
||||||
|
status = 'unbind'
|
||||||
|
}
|
||||||
|
const unbind = new Unbind()
|
||||||
return cb(unbind)
|
return cb(unbind)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -1203,21 +1241,23 @@ Client.prototype._sendSocket = function _sendSocket (message,
|
||||||
function messageCallback (msg) {
|
function messageCallback (msg) {
|
||||||
if (timer) { clearTimeout(timer) }
|
if (timer) { clearTimeout(timer) }
|
||||||
|
|
||||||
log.trace({ msg: msg ? msg.json : null }, 'response received')
|
log.trace({ msg: msg ? msg.pojo : null }, 'response received')
|
||||||
|
|
||||||
if (expect === 'abandon') { return sendResult('end', null) }
|
if (expect === 'abandon') { return sendResult('end', null) }
|
||||||
|
|
||||||
if (msg instanceof SearchEntry || msg instanceof SearchReference) {
|
if (msg instanceof SearchEntry || msg instanceof SearchReference) {
|
||||||
let event = msg.constructor.name
|
let event = msg.constructor.name
|
||||||
event = event[0].toLowerCase() + event.slice(1)
|
// Generate the event name for the event emitter, i.e. "searchEntry"
|
||||||
|
// and "searchReference".
|
||||||
|
event = (event[0].toLowerCase() + event.slice(1)).replaceAll('Result', '')
|
||||||
return sendResult(event, msg)
|
return sendResult(event, msg)
|
||||||
} else {
|
} else {
|
||||||
tracker.remove(message.messageID)
|
tracker.remove(message.messageId)
|
||||||
// Potentially mark client as idle
|
// Potentially mark client as idle
|
||||||
self._updateIdle()
|
self._updateIdle()
|
||||||
|
|
||||||
if (msg instanceof LDAPResult) {
|
if (msg instanceof LDAPResult) {
|
||||||
if (expect.indexOf(msg.status) === -1) {
|
if (msg.status !== 0 && expect.indexOf(msg.status) === -1) {
|
||||||
return sendResult('error', errors.getError(msg))
|
return sendResult('error', errors.getError(msg))
|
||||||
}
|
}
|
||||||
return sendResult('end', msg)
|
return sendResult('end', msg)
|
||||||
|
@ -1231,7 +1271,7 @@ Client.prototype._sendSocket = function _sendSocket (message,
|
||||||
|
|
||||||
function onRequestTimeout () {
|
function onRequestTimeout () {
|
||||||
self.emit('timeout', message)
|
self.emit('timeout', message)
|
||||||
const cb = tracker.fetch(message.messageID)
|
const { callback: cb } = tracker.fetch(message.messageId)
|
||||||
if (cb) {
|
if (cb) {
|
||||||
// FIXME: the timed-out request should be abandoned
|
// FIXME: the timed-out request should be abandoned
|
||||||
cb(new errors.TimeoutError('request timeout (client interrupt)'))
|
cb(new errors.TimeoutError('request timeout (client interrupt)'))
|
||||||
|
@ -1240,8 +1280,8 @@ Client.prototype._sendSocket = function _sendSocket (message,
|
||||||
|
|
||||||
function writeCallback () {
|
function writeCallback () {
|
||||||
if (expect === 'abandon') {
|
if (expect === 'abandon') {
|
||||||
// Mark the messageID specified as abandoned
|
// Mark the messageId specified as abandoned
|
||||||
tracker.abandon(message.abandonID)
|
tracker.abandon(message.abandonId)
|
||||||
// No need to track the abandon request itself
|
// No need to track the abandon request itself
|
||||||
tracker.remove(message.id)
|
tracker.remove(message.id)
|
||||||
return callback(null)
|
return callback(null)
|
||||||
|
@ -1273,10 +1313,11 @@ Client.prototype._sendSocket = function _sendSocket (message,
|
||||||
timer = setTimeout(onRequestTimeout, self.timeout)
|
timer = setTimeout(onRequestTimeout, self.timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.trace('sending request %j', message.json)
|
log.trace('sending request %j', message.pojo)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return conn.write(message.toBer(), writeCallback)
|
const messageBer = message.toBer()
|
||||||
|
return conn.write(messageBer.buffer, writeCallback)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (timer) { clearTimeout(timer) }
|
if (timer) { clearTimeout(timer) }
|
||||||
|
|
||||||
|
|
|
@ -62,13 +62,23 @@ module.exports = function messageTrackerFactory (options) {
|
||||||
*/
|
*/
|
||||||
tracker.abandon = function abandonMessage (msgID) {
|
tracker.abandon = function abandonMessage (msgID) {
|
||||||
if (messages.has(msgID) === false) return false
|
if (messages.has(msgID) === false) return false
|
||||||
|
const toAbandon = messages.get(msgID)
|
||||||
abandoned.set(msgID, {
|
abandoned.set(msgID, {
|
||||||
age: currentID,
|
age: currentID,
|
||||||
cb: messages.get(msgID)
|
message: toAbandon.message,
|
||||||
|
cb: toAbandon.callback
|
||||||
})
|
})
|
||||||
return messages.delete(msgID)
|
return messages.delete(msgID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {object} Tracked
|
||||||
|
* @property {object} message The tracked message. Usually the outgoing
|
||||||
|
* request object.
|
||||||
|
* @property {Function} callback The handler to use when receiving a
|
||||||
|
* response to the tracked message.
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the message handler for a message. Removes abandoned messages
|
* Retrieves the message handler for a message. Removes abandoned messages
|
||||||
* that have been given time to be resolved.
|
* that have been given time to be resolved.
|
||||||
|
@ -79,10 +89,10 @@ module.exports = function messageTrackerFactory (options) {
|
||||||
* @method fetch
|
* @method fetch
|
||||||
*/
|
*/
|
||||||
tracker.fetch = function fetchMessage (msgID) {
|
tracker.fetch = function fetchMessage (msgID) {
|
||||||
const messageCB = messages.get(msgID)
|
const tracked = messages.get(msgID)
|
||||||
if (messageCB) {
|
if (tracked) {
|
||||||
purgeAbandoned(msgID, abandoned)
|
purgeAbandoned(msgID, abandoned)
|
||||||
return messageCB
|
return tracked
|
||||||
}
|
}
|
||||||
|
|
||||||
// We sent an abandon request but the server either wasn't able to process
|
// We sent an abandon request but the server either wasn't able to process
|
||||||
|
@ -91,7 +101,7 @@ module.exports = function messageTrackerFactory (options) {
|
||||||
// to be processed normally.
|
// to be processed normally.
|
||||||
const abandonedMsg = abandoned.get(msgID)
|
const abandonedMsg = abandoned.get(msgID)
|
||||||
if (abandonedMsg) {
|
if (abandonedMsg) {
|
||||||
return abandonedMsg.cb
|
return { message: abandonedMsg, callback: abandonedMsg.cb }
|
||||||
}
|
}
|
||||||
|
|
||||||
return null
|
return null
|
||||||
|
@ -110,7 +120,7 @@ module.exports = function messageTrackerFactory (options) {
|
||||||
messages.forEach((val, key) => {
|
messages.forEach((val, key) => {
|
||||||
purgeAbandoned(key, abandoned)
|
purgeAbandoned(key, abandoned)
|
||||||
tracker.remove(key)
|
tracker.remove(key)
|
||||||
cb(key, val)
|
cb(key, val.callback)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,7 +142,7 @@ module.exports = function messageTrackerFactory (options) {
|
||||||
* Add a message handler to be tracked.
|
* Add a message handler to be tracked.
|
||||||
*
|
*
|
||||||
* @param {object} message The message object to be tracked. This object will
|
* @param {object} message The message object to be tracked. This object will
|
||||||
* have a new property added to it: `messageID`.
|
* have a new property added to it: `messageId`.
|
||||||
* @param {function} callback The handler for the message.
|
* @param {function} callback The handler for the message.
|
||||||
*
|
*
|
||||||
* @memberof MessageTracker
|
* @memberof MessageTracker
|
||||||
|
@ -143,8 +153,8 @@ module.exports = function messageTrackerFactory (options) {
|
||||||
// This side effect is not ideal but the client doesn't attach the tracker
|
// This side effect is not ideal but the client doesn't attach the tracker
|
||||||
// to itself until after the `.connect` method has fired. If this can be
|
// to itself until after the `.connect` method has fired. If this can be
|
||||||
// refactored later, then we can possibly get rid of this side effect.
|
// refactored later, then we can possibly get rid of this side effect.
|
||||||
message.messageID = currentID
|
message.messageId = currentID
|
||||||
messages.set(currentID, callback)
|
messages.set(currentID, { callback, message })
|
||||||
}
|
}
|
||||||
|
|
||||||
return tracker
|
return tracker
|
||||||
|
|
|
@ -2,13 +2,8 @@
|
||||||
|
|
||||||
const EventEmitter = require('events').EventEmitter
|
const EventEmitter = require('events').EventEmitter
|
||||||
const util = require('util')
|
const util = require('util')
|
||||||
|
|
||||||
const assert = require('assert-plus')
|
const assert = require('assert-plus')
|
||||||
|
|
||||||
// var dn = require('../dn')
|
|
||||||
// var messages = require('../messages/index')
|
|
||||||
const { PagedResultsControl } = require('@ldapjs/controls')
|
const { PagedResultsControl } = require('@ldapjs/controls')
|
||||||
|
|
||||||
const CorkedEmitter = require('../corked_emitter.js')
|
const CorkedEmitter = require('../corked_emitter.js')
|
||||||
|
|
||||||
/// --- API
|
/// --- API
|
||||||
|
@ -94,13 +89,13 @@ SearchPager.prototype._onEnd = function _onEnd (res) {
|
||||||
if (this.listeners('pageError').length > 0) {
|
if (this.listeners('pageError').length > 0) {
|
||||||
this.emit('pageError', err)
|
this.emit('pageError', err)
|
||||||
// If the consumer as subscribed to pageError, SearchPager is absolved
|
// If the consumer as subscribed to pageError, SearchPager is absolved
|
||||||
// from deliverying the fault via the 'error' event. Emitting an 'end'
|
// from delivering the fault via the 'error' event. Emitting an 'end'
|
||||||
// event after 'error' breaks the contract that the standard client
|
// event after 'error' breaks the contract that the standard client
|
||||||
// provides, so it's only a possibility if 'pageError' is used instead.
|
// provides, so it's only a possibility if 'pageError' is used instead.
|
||||||
this.emit('end', res)
|
this.emit('end', res)
|
||||||
} else {
|
} else {
|
||||||
this.emit('error', err)
|
this.emit('error', err)
|
||||||
// No end event possible per explaination above.
|
// No end event possible per explanation above.
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
473
lib/dn.js
473
lib/dn.js
|
@ -1,473 +0,0 @@
|
||||||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
|
||||||
|
|
||||||
const assert = require('assert-plus')
|
|
||||||
|
|
||||||
/// --- Helpers
|
|
||||||
|
|
||||||
function invalidDN (name) {
|
|
||||||
const e = new Error()
|
|
||||||
e.name = 'InvalidDistinguishedNameError'
|
|
||||||
e.message = name
|
|
||||||
return e
|
|
||||||
}
|
|
||||||
|
|
||||||
function isAlphaNumeric (c) {
|
|
||||||
const re = /[A-Za-z0-9]/
|
|
||||||
return re.test(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
function isWhitespace (c) {
|
|
||||||
const re = /\s/
|
|
||||||
return re.test(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
function repeatChar (c, n) {
|
|
||||||
let out = ''
|
|
||||||
const max = n || 0
|
|
||||||
for (let i = 0; i < max; i++) { out += c }
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
/// --- API
|
|
||||||
|
|
||||||
function RDN (obj) {
|
|
||||||
const self = this
|
|
||||||
this.attrs = {}
|
|
||||||
|
|
||||||
if (obj) {
|
|
||||||
Object.keys(obj).forEach(function (k) {
|
|
||||||
self.set(k, obj[k])
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RDN.prototype.set = function rdnSet (name, value, opts) {
|
|
||||||
assert.string(name, 'name (string) required')
|
|
||||||
assert.string(value, 'value (string) required')
|
|
||||||
|
|
||||||
const self = this
|
|
||||||
const lname = name.toLowerCase()
|
|
||||||
this.attrs[lname] = {
|
|
||||||
value: value,
|
|
||||||
name: name
|
|
||||||
}
|
|
||||||
if (opts && typeof (opts) === 'object') {
|
|
||||||
Object.keys(opts).forEach(function (k) {
|
|
||||||
if (k !== 'value') { self.attrs[lname][k] = opts[k] }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RDN.prototype.equals = function rdnEquals (rdn) {
|
|
||||||
if (typeof (rdn) !== 'object') { return false }
|
|
||||||
|
|
||||||
const ourKeys = Object.keys(this.attrs)
|
|
||||||
const theirKeys = Object.keys(rdn.attrs)
|
|
||||||
if (ourKeys.length !== theirKeys.length) { return false }
|
|
||||||
|
|
||||||
ourKeys.sort()
|
|
||||||
theirKeys.sort()
|
|
||||||
|
|
||||||
for (let i = 0; i < ourKeys.length; i++) {
|
|
||||||
if (ourKeys[i] !== theirKeys[i]) { return false }
|
|
||||||
if (this.attrs[ourKeys[i]].value !== rdn.attrs[ourKeys[i]].value) { return false }
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert RDN to string according to specified formatting options.
|
|
||||||
* (see: DN.format for option details)
|
|
||||||
*/
|
|
||||||
RDN.prototype.format = function rdnFormat (options) {
|
|
||||||
assert.optionalObject(options, 'options must be an object')
|
|
||||||
options = options || {}
|
|
||||||
|
|
||||||
const self = this
|
|
||||||
let str = ''
|
|
||||||
|
|
||||||
function escapeValue (val, forceQuote) {
|
|
||||||
let out = ''
|
|
||||||
let cur = 0
|
|
||||||
const len = val.length
|
|
||||||
let quoted = false
|
|
||||||
/* BEGIN JSSTYLED */
|
|
||||||
// TODO: figure out what this regex is actually trying to test for and
|
|
||||||
// fix it to appease the linter.
|
|
||||||
/* eslint-disable-next-line no-useless-escape */
|
|
||||||
const escaped = /[\\\"]/
|
|
||||||
const special = /[,=+<>#;]/
|
|
||||||
/* END JSSTYLED */
|
|
||||||
|
|
||||||
if (len > 0) {
|
|
||||||
// Wrap strings with trailing or leading spaces in quotes
|
|
||||||
quoted = forceQuote || (val[0] === ' ' || val[len - 1] === ' ')
|
|
||||||
}
|
|
||||||
|
|
||||||
while (cur < len) {
|
|
||||||
if (escaped.test(val[cur]) || (!quoted && special.test(val[cur]))) {
|
|
||||||
out += '\\'
|
|
||||||
}
|
|
||||||
out += val[cur++]
|
|
||||||
}
|
|
||||||
if (quoted) { out = '"' + out + '"' }
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
function sortParsed (a, b) {
|
|
||||||
return self.attrs[a].order - self.attrs[b].order
|
|
||||||
}
|
|
||||||
function sortStandard (a, b) {
|
|
||||||
const nameCompare = a.localeCompare(b)
|
|
||||||
if (nameCompare === 0) {
|
|
||||||
// TODO: Handle binary values
|
|
||||||
return self.attrs[a].value.localeCompare(self.attrs[b].value)
|
|
||||||
} else {
|
|
||||||
return nameCompare
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const keys = Object.keys(this.attrs)
|
|
||||||
if (options.keepOrder) {
|
|
||||||
keys.sort(sortParsed)
|
|
||||||
} else {
|
|
||||||
keys.sort(sortStandard)
|
|
||||||
}
|
|
||||||
|
|
||||||
keys.forEach(function (key) {
|
|
||||||
const attr = self.attrs[key]
|
|
||||||
if (str.length) { str += '+' }
|
|
||||||
|
|
||||||
if (options.keepCase) {
|
|
||||||
str += attr.name
|
|
||||||
} else {
|
|
||||||
if (options.upperName) { str += key.toUpperCase() } else { str += key }
|
|
||||||
}
|
|
||||||
|
|
||||||
str += '=' + escapeValue(attr.value, (options.keepQuote && attr.quoted))
|
|
||||||
})
|
|
||||||
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
|
|
||||||
RDN.prototype.toString = function rdnToString () {
|
|
||||||
return this.format()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Thank you OpenJDK!
|
|
||||||
function parse (name) {
|
|
||||||
if (typeof (name) !== 'string') { throw new TypeError('name (string) required') }
|
|
||||||
|
|
||||||
let cur = 0
|
|
||||||
const len = name.length
|
|
||||||
|
|
||||||
function parseRdn () {
|
|
||||||
const rdn = new RDN()
|
|
||||||
let order = 0
|
|
||||||
rdn.spLead = trim()
|
|
||||||
while (cur < len) {
|
|
||||||
const opts = {
|
|
||||||
order: order
|
|
||||||
}
|
|
||||||
const attr = parseAttrType()
|
|
||||||
trim()
|
|
||||||
if (cur >= len || name[cur++] !== '=') { throw invalidDN(name) }
|
|
||||||
|
|
||||||
trim()
|
|
||||||
// Parameters about RDN value are set in 'opts' by parseAttrValue
|
|
||||||
const value = parseAttrValue(opts)
|
|
||||||
rdn.set(attr, value, opts)
|
|
||||||
rdn.spTrail = trim()
|
|
||||||
if (cur >= len || name[cur] !== '+') { break }
|
|
||||||
++cur
|
|
||||||
++order
|
|
||||||
}
|
|
||||||
return rdn
|
|
||||||
}
|
|
||||||
|
|
||||||
function trim () {
|
|
||||||
let count = 0
|
|
||||||
while ((cur < len) && isWhitespace(name[cur])) {
|
|
||||||
++cur
|
|
||||||
count++
|
|
||||||
}
|
|
||||||
return count
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseAttrType () {
|
|
||||||
const beg = cur
|
|
||||||
while (cur < len) {
|
|
||||||
const c = name[cur]
|
|
||||||
if (isAlphaNumeric(c) ||
|
|
||||||
c === '.' ||
|
|
||||||
c === '-' ||
|
|
||||||
c === ' ') {
|
|
||||||
++cur
|
|
||||||
} else {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Back out any trailing spaces.
|
|
||||||
while ((cur > beg) && (name[cur - 1] === ' ')) { --cur }
|
|
||||||
|
|
||||||
if (beg === cur) { throw invalidDN(name) }
|
|
||||||
|
|
||||||
return name.slice(beg, cur)
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseAttrValue (opts) {
|
|
||||||
if (cur < len && name[cur] === '#') {
|
|
||||||
opts.binary = true
|
|
||||||
return parseBinaryAttrValue()
|
|
||||||
} else if (cur < len && name[cur] === '"') {
|
|
||||||
opts.quoted = true
|
|
||||||
return parseQuotedAttrValue()
|
|
||||||
} else {
|
|
||||||
return parseStringAttrValue()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseBinaryAttrValue () {
|
|
||||||
const beg = cur++
|
|
||||||
while (cur < len && isAlphaNumeric(name[cur])) { ++cur }
|
|
||||||
|
|
||||||
return name.slice(beg, cur)
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseQuotedAttrValue () {
|
|
||||||
let str = ''
|
|
||||||
++cur // Consume the first quote
|
|
||||||
|
|
||||||
while ((cur < len) && name[cur] !== '"') {
|
|
||||||
if (name[cur] === '\\') { cur++ }
|
|
||||||
str += name[cur++]
|
|
||||||
}
|
|
||||||
if (cur++ >= len) {
|
|
||||||
// no closing quote
|
|
||||||
throw invalidDN(name)
|
|
||||||
}
|
|
||||||
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseStringAttrValue () {
|
|
||||||
const beg = cur
|
|
||||||
let str = ''
|
|
||||||
let esc = -1
|
|
||||||
|
|
||||||
while ((cur < len) && !atTerminator()) {
|
|
||||||
if (name[cur] === '\\') {
|
|
||||||
// Consume the backslash and mark its place just in case it's escaping
|
|
||||||
// whitespace which needs to be preserved.
|
|
||||||
esc = cur++
|
|
||||||
}
|
|
||||||
if (cur === len) {
|
|
||||||
// backslash followed by nothing
|
|
||||||
throw invalidDN(name)
|
|
||||||
}
|
|
||||||
str += name[cur++]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Trim off (unescaped) trailing whitespace and rewind cursor to the end of
|
|
||||||
// the AttrValue to record whitespace length.
|
|
||||||
for (; cur > beg; cur--) {
|
|
||||||
if (!isWhitespace(name[cur - 1]) || (esc === (cur - 1))) { break }
|
|
||||||
}
|
|
||||||
return str.slice(0, cur - beg)
|
|
||||||
}
|
|
||||||
|
|
||||||
function atTerminator () {
|
|
||||||
return (cur < len &&
|
|
||||||
(name[cur] === ',' ||
|
|
||||||
name[cur] === ';' ||
|
|
||||||
name[cur] === '+'))
|
|
||||||
}
|
|
||||||
|
|
||||||
const rdns = []
|
|
||||||
|
|
||||||
// Short-circuit for empty DNs
|
|
||||||
if (len === 0) { return new DN(rdns) }
|
|
||||||
|
|
||||||
rdns.push(parseRdn())
|
|
||||||
while (cur < len) {
|
|
||||||
if (name[cur] === ',' || name[cur] === ';') {
|
|
||||||
++cur
|
|
||||||
rdns.push(parseRdn())
|
|
||||||
} else {
|
|
||||||
throw invalidDN(name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new DN(rdns)
|
|
||||||
}
|
|
||||||
|
|
||||||
function DN (rdns) {
|
|
||||||
assert.optionalArrayOfObject(rdns, '[object] required')
|
|
||||||
|
|
||||||
this.rdns = rdns ? rdns.slice() : []
|
|
||||||
this._format = {}
|
|
||||||
}
|
|
||||||
Object.defineProperties(DN.prototype, {
|
|
||||||
length: {
|
|
||||||
get: function getLength () { return this.rdns.length },
|
|
||||||
configurable: false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert DN to string according to specified formatting options.
|
|
||||||
*
|
|
||||||
* Parameters:
|
|
||||||
* - options: formatting parameters (optional, details below)
|
|
||||||
*
|
|
||||||
* Options are divided into two types:
|
|
||||||
* - Preservation options: Using data recorded during parsing, details of the
|
|
||||||
* original DN are preserved when converting back into a string.
|
|
||||||
* - Modification options: Alter string formatting defaults.
|
|
||||||
*
|
|
||||||
* Preservation options _always_ take precedence over modification options.
|
|
||||||
*
|
|
||||||
* Preservation Options:
|
|
||||||
* - keepOrder: Order of multi-value RDNs.
|
|
||||||
* - keepQuote: RDN values which were quoted will remain so.
|
|
||||||
* - keepSpace: Leading/trailing spaces will be output.
|
|
||||||
* - keepCase: Parsed attr name will be output instead of lowercased version.
|
|
||||||
*
|
|
||||||
* Modification Options:
|
|
||||||
* - upperName: RDN names will be uppercased instead of lowercased.
|
|
||||||
* - skipSpace: Disable trailing space after RDN separators
|
|
||||||
*/
|
|
||||||
DN.prototype.format = function dnFormat (options) {
|
|
||||||
assert.optionalObject(options, 'options must be an object')
|
|
||||||
options = options || this._format
|
|
||||||
|
|
||||||
let str = ''
|
|
||||||
this.rdns.forEach(function (rdn) {
|
|
||||||
const rdnString = rdn.format(options)
|
|
||||||
if (str.length !== 0) {
|
|
||||||
str += ','
|
|
||||||
}
|
|
||||||
if (options.keepSpace) {
|
|
||||||
str += (repeatChar(' ', rdn.spLead) +
|
|
||||||
rdnString + repeatChar(' ', rdn.spTrail))
|
|
||||||
} else if (options.skipSpace === true || str.length === 0) {
|
|
||||||
str += rdnString
|
|
||||||
} else {
|
|
||||||
str += ' ' + rdnString
|
|
||||||
}
|
|
||||||
})
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set default string formatting options.
|
|
||||||
*/
|
|
||||||
DN.prototype.setFormat = function setFormat (options) {
|
|
||||||
assert.object(options, 'options must be an object')
|
|
||||||
|
|
||||||
this._format = options
|
|
||||||
}
|
|
||||||
|
|
||||||
DN.prototype.toString = function dnToString () {
|
|
||||||
return this.format()
|
|
||||||
}
|
|
||||||
|
|
||||||
DN.prototype.parentOf = function parentOf (dn) {
|
|
||||||
if (typeof (dn) !== 'object') { dn = parse(dn) }
|
|
||||||
|
|
||||||
if (this.rdns.length >= dn.rdns.length) { return false }
|
|
||||||
|
|
||||||
const diff = dn.rdns.length - this.rdns.length
|
|
||||||
for (let i = this.rdns.length - 1; i >= 0; i--) {
|
|
||||||
const myRDN = this.rdns[i]
|
|
||||||
const theirRDN = dn.rdns[i + diff]
|
|
||||||
|
|
||||||
if (!myRDN.equals(theirRDN)) { return false }
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
DN.prototype.childOf = function childOf (dn) {
|
|
||||||
if (typeof (dn) !== 'object') { dn = parse(dn) }
|
|
||||||
return dn.parentOf(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
DN.prototype.isEmpty = function isEmpty () {
|
|
||||||
return (this.rdns.length === 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
DN.prototype.equals = function dnEquals (dn) {
|
|
||||||
if (typeof (dn) !== 'object') { dn = parse(dn) }
|
|
||||||
|
|
||||||
if (this.rdns.length !== dn.rdns.length) { return false }
|
|
||||||
|
|
||||||
for (let i = 0; i < this.rdns.length; i++) {
|
|
||||||
if (!this.rdns[i].equals(dn.rdns[i])) { return false }
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
DN.prototype.parent = function dnParent () {
|
|
||||||
if (this.rdns.length !== 0) {
|
|
||||||
const save = this.rdns.shift()
|
|
||||||
const dn = new DN(this.rdns)
|
|
||||||
this.rdns.unshift(save)
|
|
||||||
return dn
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
DN.prototype.clone = function dnClone () {
|
|
||||||
const dn = new DN(this.rdns)
|
|
||||||
dn._format = this._format
|
|
||||||
return dn
|
|
||||||
}
|
|
||||||
|
|
||||||
DN.prototype.reverse = function dnReverse () {
|
|
||||||
this.rdns.reverse()
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
DN.prototype.pop = function dnPop () {
|
|
||||||
return this.rdns.pop()
|
|
||||||
}
|
|
||||||
|
|
||||||
DN.prototype.push = function dnPush (rdn) {
|
|
||||||
assert.object(rdn, 'rdn (RDN) required')
|
|
||||||
|
|
||||||
return this.rdns.push(rdn)
|
|
||||||
}
|
|
||||||
|
|
||||||
DN.prototype.shift = function dnShift () {
|
|
||||||
return this.rdns.shift()
|
|
||||||
}
|
|
||||||
|
|
||||||
DN.prototype.unshift = function dnUnshift (rdn) {
|
|
||||||
assert.object(rdn, 'rdn (RDN) required')
|
|
||||||
|
|
||||||
return this.rdns.unshift(rdn)
|
|
||||||
}
|
|
||||||
|
|
||||||
DN.isDN = function isDN (dn) {
|
|
||||||
if (!dn || typeof (dn) !== 'object') {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if (dn instanceof DN) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if (Array.isArray(dn.rdns)) {
|
|
||||||
// Really simple duck-typing for now
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
/// --- Exports
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
parse: parse,
|
|
||||||
DN: DN,
|
|
||||||
RDN: RDN
|
|
||||||
}
|
|
|
@ -3,14 +3,14 @@
|
||||||
const logger = require('./logger')
|
const logger = require('./logger')
|
||||||
|
|
||||||
const client = require('./client')
|
const client = require('./client')
|
||||||
const Attribute = require('./attribute')
|
const Attribute = require('@ldapjs/attribute')
|
||||||
const Change = require('./change')
|
const Change = require('@ldapjs/change')
|
||||||
const Protocol = require('@ldapjs/protocol')
|
const Protocol = require('@ldapjs/protocol')
|
||||||
const Server = require('./server')
|
const Server = require('./server')
|
||||||
|
|
||||||
const controls = require('./controls')
|
const controls = require('./controls')
|
||||||
const persistentSearch = require('./persistent_search')
|
const persistentSearch = require('./persistent_search')
|
||||||
const dn = require('./dn')
|
const dn = require('@ldapjs/dn')
|
||||||
const errors = require('./errors')
|
const errors = require('./errors')
|
||||||
const filters = require('@ldapjs/filter')
|
const filters = require('@ldapjs/filter')
|
||||||
const messages = require('./messages')
|
const messages = require('./messages')
|
||||||
|
@ -43,7 +43,7 @@ module.exports = {
|
||||||
dn: dn,
|
dn: dn,
|
||||||
DN: dn.DN,
|
DN: dn.DN,
|
||||||
RDN: dn.RDN,
|
RDN: dn.RDN,
|
||||||
parseDN: dn.parse,
|
parseDN: dn.DN.fromString,
|
||||||
|
|
||||||
persistentSearch: persistentSearch,
|
persistentSearch: persistentSearch,
|
||||||
PersistentSearchCache: persistentSearch.PersistentSearchCache,
|
PersistentSearchCache: persistentSearch.PersistentSearchCache,
|
||||||
|
|
|
@ -1,87 +0,0 @@
|
||||||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
|
||||||
|
|
||||||
const assert = require('assert-plus')
|
|
||||||
const util = require('util')
|
|
||||||
|
|
||||||
const LDAPMessage = require('./message')
|
|
||||||
const Protocol = require('@ldapjs/protocol')
|
|
||||||
|
|
||||||
/// --- API
|
|
||||||
|
|
||||||
function AbandonRequest (options) {
|
|
||||||
options = options || {}
|
|
||||||
assert.object(options)
|
|
||||||
assert.optionalNumber(options.abandonID)
|
|
||||||
|
|
||||||
options.protocolOp = Protocol.operations.LDAP_REQ_ABANDON
|
|
||||||
LDAPMessage.call(this, options)
|
|
||||||
|
|
||||||
this.abandonID = options.abandonID || 0
|
|
||||||
}
|
|
||||||
util.inherits(AbandonRequest, LDAPMessage)
|
|
||||||
Object.defineProperties(AbandonRequest.prototype, {
|
|
||||||
type: {
|
|
||||||
get: function getType () { return 'AbandonRequest' },
|
|
||||||
configurable: false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
AbandonRequest.prototype._parse = function (ber, length) {
|
|
||||||
assert.ok(ber)
|
|
||||||
assert.ok(length)
|
|
||||||
|
|
||||||
// What a PITA - have to replicate ASN.1 integer logic to work around the
|
|
||||||
// way abandon is encoded and the way ldapjs framework handles "normal"
|
|
||||||
// messages
|
|
||||||
|
|
||||||
const buf = ber.buffer
|
|
||||||
let offset = 0
|
|
||||||
let value = 0
|
|
||||||
|
|
||||||
const fb = buf[offset++]
|
|
||||||
value = fb & 0x7F
|
|
||||||
for (let i = 1; i < length; i++) {
|
|
||||||
value <<= 8
|
|
||||||
value |= (buf[offset++] & 0xff)
|
|
||||||
}
|
|
||||||
if ((fb & 0x80) === 0x80) { value = -value }
|
|
||||||
|
|
||||||
ber._offset += length
|
|
||||||
|
|
||||||
this.abandonID = value
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
AbandonRequest.prototype._toBer = function (ber) {
|
|
||||||
assert.ok(ber)
|
|
||||||
|
|
||||||
let i = this.abandonID
|
|
||||||
let sz = 4
|
|
||||||
|
|
||||||
while ((((i & 0xff800000) === 0) || ((i & 0xff800000) === 0xff800000)) &&
|
|
||||||
(sz > 1)) {
|
|
||||||
sz--
|
|
||||||
i <<= 8
|
|
||||||
}
|
|
||||||
assert.ok(sz <= 4)
|
|
||||||
|
|
||||||
while (sz-- > 0) {
|
|
||||||
ber.writeByte((i & 0xff000000) >> 24)
|
|
||||||
i <<= 8
|
|
||||||
}
|
|
||||||
|
|
||||||
return ber
|
|
||||||
}
|
|
||||||
|
|
||||||
AbandonRequest.prototype._json = function (j) {
|
|
||||||
assert.ok(j)
|
|
||||||
|
|
||||||
j.abandonID = this.abandonID
|
|
||||||
|
|
||||||
return j
|
|
||||||
}
|
|
||||||
|
|
||||||
/// --- Exports
|
|
||||||
|
|
||||||
module.exports = AbandonRequest
|
|
|
@ -1,33 +0,0 @@
|
||||||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
|
||||||
|
|
||||||
const assert = require('assert-plus')
|
|
||||||
const util = require('util')
|
|
||||||
|
|
||||||
const LDAPMessage = require('./result')
|
|
||||||
|
|
||||||
/// --- API
|
|
||||||
|
|
||||||
function AbandonResponse (options) {
|
|
||||||
options = options || {}
|
|
||||||
assert.object(options)
|
|
||||||
|
|
||||||
options.protocolOp = 0
|
|
||||||
LDAPMessage.call(this, options)
|
|
||||||
}
|
|
||||||
util.inherits(AbandonResponse, LDAPMessage)
|
|
||||||
Object.defineProperties(AbandonResponse.prototype, {
|
|
||||||
type: {
|
|
||||||
get: function getType () { return 'AbandonResponse' },
|
|
||||||
configurable: false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
AbandonResponse.prototype.end = function (_status) {}
|
|
||||||
|
|
||||||
AbandonResponse.prototype._json = function (j) {
|
|
||||||
return j
|
|
||||||
}
|
|
||||||
|
|
||||||
/// --- Exports
|
|
||||||
|
|
||||||
module.exports = AbandonResponse
|
|
|
@ -1,159 +0,0 @@
|
||||||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
|
||||||
|
|
||||||
const assert = require('assert-plus')
|
|
||||||
const util = require('util')
|
|
||||||
|
|
||||||
const LDAPMessage = require('./message')
|
|
||||||
const Attribute = require('../attribute')
|
|
||||||
const Protocol = require('@ldapjs/protocol')
|
|
||||||
const lassert = require('../assert')
|
|
||||||
|
|
||||||
/// --- API
|
|
||||||
|
|
||||||
function AddRequest (options) {
|
|
||||||
options = options || {}
|
|
||||||
assert.object(options)
|
|
||||||
lassert.optionalStringDN(options.entry)
|
|
||||||
lassert.optionalArrayOfAttribute(options.attributes)
|
|
||||||
|
|
||||||
options.protocolOp = Protocol.operations.LDAP_REQ_ADD
|
|
||||||
LDAPMessage.call(this, options)
|
|
||||||
|
|
||||||
this.entry = options.entry || null
|
|
||||||
this.attributes = options.attributes ? options.attributes.slice(0) : []
|
|
||||||
}
|
|
||||||
util.inherits(AddRequest, LDAPMessage)
|
|
||||||
Object.defineProperties(AddRequest.prototype, {
|
|
||||||
type: {
|
|
||||||
get: function getType () { return 'AddRequest' },
|
|
||||||
configurable: false
|
|
||||||
},
|
|
||||||
_dn: {
|
|
||||||
get: function getDN () { return this.entry },
|
|
||||||
configurable: false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
AddRequest.prototype._parse = function (ber) {
|
|
||||||
assert.ok(ber)
|
|
||||||
|
|
||||||
this.entry = ber.readString()
|
|
||||||
|
|
||||||
ber.readSequence()
|
|
||||||
|
|
||||||
const end = ber.offset + ber.length
|
|
||||||
while (ber.offset < end) {
|
|
||||||
const a = new Attribute()
|
|
||||||
a.parse(ber)
|
|
||||||
a.type = a.type.toLowerCase()
|
|
||||||
if (a.type === 'objectclass') {
|
|
||||||
for (let i = 0; i < a.vals.length; i++) { a.vals[i] = a.vals[i].toLowerCase() }
|
|
||||||
}
|
|
||||||
this.attributes.push(a)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.attributes.sort(Attribute.compare)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
AddRequest.prototype._toBer = function (ber) {
|
|
||||||
assert.ok(ber)
|
|
||||||
|
|
||||||
ber.writeString(this.entry.toString())
|
|
||||||
ber.startSequence()
|
|
||||||
this.attributes.forEach(function (a) {
|
|
||||||
a.toBer(ber)
|
|
||||||
})
|
|
||||||
ber.endSequence()
|
|
||||||
|
|
||||||
return ber
|
|
||||||
}
|
|
||||||
|
|
||||||
AddRequest.prototype._json = function (j) {
|
|
||||||
assert.ok(j)
|
|
||||||
|
|
||||||
j.entry = this.entry.toString()
|
|
||||||
j.attributes = []
|
|
||||||
|
|
||||||
this.attributes.forEach(function (a) {
|
|
||||||
j.attributes.push(a.json)
|
|
||||||
})
|
|
||||||
|
|
||||||
return j
|
|
||||||
}
|
|
||||||
|
|
||||||
AddRequest.prototype.indexOf = function (attr) {
|
|
||||||
if (!attr || typeof (attr) !== 'string') { throw new TypeError('attr (string) required') }
|
|
||||||
|
|
||||||
for (let i = 0; i < this.attributes.length; i++) {
|
|
||||||
if (this.attributes[i].type === attr) { return i }
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
AddRequest.prototype.attributeNames = function () {
|
|
||||||
const attrs = []
|
|
||||||
|
|
||||||
for (let i = 0; i < this.attributes.length; i++) { attrs.push(this.attributes[i].type.toLowerCase()) }
|
|
||||||
|
|
||||||
return attrs
|
|
||||||
}
|
|
||||||
|
|
||||||
AddRequest.prototype.getAttribute = function (name) {
|
|
||||||
if (!name || typeof (name) !== 'string') { throw new TypeError('attribute name (string) required') }
|
|
||||||
|
|
||||||
name = name.toLowerCase()
|
|
||||||
|
|
||||||
for (let i = 0; i < this.attributes.length; i++) {
|
|
||||||
if (this.attributes[i].type === name) { return this.attributes[i] }
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
AddRequest.prototype.addAttribute = function (attr) {
|
|
||||||
if (!(attr instanceof Attribute)) { throw new TypeError('attribute (Attribute) required') }
|
|
||||||
|
|
||||||
return this.attributes.push(attr)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a "pure" JS representation of this object.
|
|
||||||
*
|
|
||||||
* An example object would look like:
|
|
||||||
*
|
|
||||||
* {
|
|
||||||
* "dn": "cn=unit, dc=test",
|
|
||||||
* "attributes": {
|
|
||||||
* "cn": ["unit", "foo"],
|
|
||||||
* "objectclass": ["top", "person"]
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* @return {Object} that looks like the above.
|
|
||||||
*/
|
|
||||||
AddRequest.prototype.toObject = function () {
|
|
||||||
const self = this
|
|
||||||
|
|
||||||
const obj = {
|
|
||||||
dn: self.entry ? self.entry.toString() : '',
|
|
||||||
attributes: {}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.attributes || !this.attributes.length) { return obj }
|
|
||||||
|
|
||||||
this.attributes.forEach(function (a) {
|
|
||||||
if (!obj.attributes[a.type]) { obj.attributes[a.type] = [] }
|
|
||||||
|
|
||||||
a.vals.forEach(function (v) {
|
|
||||||
if (obj.attributes[a.type].indexOf(v) === -1) { obj.attributes[a.type].push(v) }
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
return obj
|
|
||||||
}
|
|
||||||
|
|
||||||
/// --- Exports
|
|
||||||
|
|
||||||
module.exports = AddRequest
|
|
|
@ -1,22 +0,0 @@
|
||||||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
|
||||||
|
|
||||||
const assert = require('assert-plus')
|
|
||||||
const util = require('util')
|
|
||||||
|
|
||||||
const LDAPResult = require('./result')
|
|
||||||
const Protocol = require('@ldapjs/protocol')
|
|
||||||
|
|
||||||
/// --- API
|
|
||||||
|
|
||||||
function AddResponse (options) {
|
|
||||||
options = options || {}
|
|
||||||
assert.object(options)
|
|
||||||
|
|
||||||
options.protocolOp = Protocol.operations.LDAP_RES_ADD
|
|
||||||
LDAPResult.call(this, options)
|
|
||||||
}
|
|
||||||
util.inherits(AddResponse, LDAPResult)
|
|
||||||
|
|
||||||
/// --- Exports
|
|
||||||
|
|
||||||
module.exports = AddResponse
|
|
|
@ -1,84 +0,0 @@
|
||||||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
|
||||||
|
|
||||||
const assert = require('assert-plus')
|
|
||||||
const util = require('util')
|
|
||||||
|
|
||||||
const asn1 = require('@ldapjs/asn1')
|
|
||||||
|
|
||||||
const LDAPMessage = require('./message')
|
|
||||||
const Protocol = require('@ldapjs/protocol')
|
|
||||||
|
|
||||||
/// --- Globals
|
|
||||||
|
|
||||||
const Ber = asn1.Ber
|
|
||||||
const LDAP_BIND_SIMPLE = 'simple'
|
|
||||||
// var LDAP_BIND_SASL = 'sasl'
|
|
||||||
|
|
||||||
/// --- API
|
|
||||||
|
|
||||||
function BindRequest (options) {
|
|
||||||
options = options || {}
|
|
||||||
assert.object(options)
|
|
||||||
|
|
||||||
options.protocolOp = Protocol.operations.LDAP_REQ_BIND
|
|
||||||
LDAPMessage.call(this, options)
|
|
||||||
|
|
||||||
this.version = options.version || 0x03
|
|
||||||
this.name = options.name || null
|
|
||||||
this.authentication = options.authentication || LDAP_BIND_SIMPLE
|
|
||||||
this.credentials = options.credentials || ''
|
|
||||||
}
|
|
||||||
util.inherits(BindRequest, LDAPMessage)
|
|
||||||
Object.defineProperties(BindRequest.prototype, {
|
|
||||||
type: {
|
|
||||||
get: function getType () { return 'BindRequest' },
|
|
||||||
configurable: false
|
|
||||||
},
|
|
||||||
_dn: {
|
|
||||||
get: function getDN () { return this.name },
|
|
||||||
configurable: false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
BindRequest.prototype._parse = function (ber) {
|
|
||||||
assert.ok(ber)
|
|
||||||
|
|
||||||
this.version = ber.readInt()
|
|
||||||
this.name = ber.readString()
|
|
||||||
|
|
||||||
const t = ber.peek()
|
|
||||||
|
|
||||||
// TODO add support for SASL et al
|
|
||||||
if (t !== Ber.Context) { throw new Error('authentication 0x' + t.toString(16) + ' not supported') }
|
|
||||||
|
|
||||||
this.authentication = LDAP_BIND_SIMPLE
|
|
||||||
this.credentials = ber.readString(Ber.Context)
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
BindRequest.prototype._toBer = function (ber) {
|
|
||||||
assert.ok(ber)
|
|
||||||
|
|
||||||
ber.writeInt(this.version)
|
|
||||||
ber.writeString((this.name || '').toString())
|
|
||||||
// TODO add support for SASL et al
|
|
||||||
ber.writeString((this.credentials || ''), Ber.Context)
|
|
||||||
|
|
||||||
return ber
|
|
||||||
}
|
|
||||||
|
|
||||||
BindRequest.prototype._json = function (j) {
|
|
||||||
assert.ok(j)
|
|
||||||
|
|
||||||
j.version = this.version
|
|
||||||
j.name = this.name
|
|
||||||
j.authenticationType = this.authentication
|
|
||||||
j.credentials = this.credentials
|
|
||||||
|
|
||||||
return j
|
|
||||||
}
|
|
||||||
|
|
||||||
/// --- Exports
|
|
||||||
|
|
||||||
module.exports = BindRequest
|
|
|
@ -1,22 +0,0 @@
|
||||||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
|
||||||
|
|
||||||
const assert = require('assert-plus')
|
|
||||||
const util = require('util')
|
|
||||||
|
|
||||||
const LDAPResult = require('./result')
|
|
||||||
const Protocol = require('@ldapjs/protocol')
|
|
||||||
|
|
||||||
/// --- API
|
|
||||||
|
|
||||||
function BindResponse (options) {
|
|
||||||
options = options || {}
|
|
||||||
assert.object(options)
|
|
||||||
|
|
||||||
options.protocolOp = Protocol.operations.LDAP_RES_BIND
|
|
||||||
LDAPResult.call(this, options)
|
|
||||||
}
|
|
||||||
util.inherits(BindResponse, LDAPResult)
|
|
||||||
|
|
||||||
/// --- Exports
|
|
||||||
|
|
||||||
module.exports = BindResponse
|
|
|
@ -1,74 +0,0 @@
|
||||||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
|
||||||
|
|
||||||
const assert = require('assert-plus')
|
|
||||||
const util = require('util')
|
|
||||||
|
|
||||||
const LDAPMessage = require('./message')
|
|
||||||
const Protocol = require('@ldapjs/protocol')
|
|
||||||
const lassert = require('../assert')
|
|
||||||
|
|
||||||
/// --- API
|
|
||||||
|
|
||||||
function CompareRequest (options) {
|
|
||||||
options = options || {}
|
|
||||||
assert.object(options)
|
|
||||||
assert.optionalString(options.attribute)
|
|
||||||
assert.optionalString(options.value)
|
|
||||||
lassert.optionalStringDN(options.entry)
|
|
||||||
|
|
||||||
options.protocolOp = Protocol.operations.LDAP_REQ_COMPARE
|
|
||||||
LDAPMessage.call(this, options)
|
|
||||||
|
|
||||||
this.entry = options.entry || null
|
|
||||||
this.attribute = options.attribute || ''
|
|
||||||
this.value = options.value || ''
|
|
||||||
}
|
|
||||||
util.inherits(CompareRequest, LDAPMessage)
|
|
||||||
Object.defineProperties(CompareRequest.prototype, {
|
|
||||||
type: {
|
|
||||||
get: function getType () { return 'CompareRequest' },
|
|
||||||
configurable: false
|
|
||||||
},
|
|
||||||
_dn: {
|
|
||||||
get: function getDN () { return this.entry },
|
|
||||||
configurable: false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
CompareRequest.prototype._parse = function (ber) {
|
|
||||||
assert.ok(ber)
|
|
||||||
|
|
||||||
this.entry = ber.readString()
|
|
||||||
|
|
||||||
ber.readSequence()
|
|
||||||
this.attribute = ber.readString().toLowerCase()
|
|
||||||
this.value = ber.readString()
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
CompareRequest.prototype._toBer = function (ber) {
|
|
||||||
assert.ok(ber)
|
|
||||||
|
|
||||||
ber.writeString(this.entry.toString())
|
|
||||||
ber.startSequence()
|
|
||||||
ber.writeString(this.attribute)
|
|
||||||
ber.writeString(this.value)
|
|
||||||
ber.endSequence()
|
|
||||||
|
|
||||||
return ber
|
|
||||||
}
|
|
||||||
|
|
||||||
CompareRequest.prototype._json = function (j) {
|
|
||||||
assert.ok(j)
|
|
||||||
|
|
||||||
j.entry = this.entry.toString()
|
|
||||||
j.attribute = this.attribute
|
|
||||||
j.value = this.value
|
|
||||||
|
|
||||||
return j
|
|
||||||
}
|
|
||||||
|
|
||||||
/// --- Exports
|
|
||||||
|
|
||||||
module.exports = CompareRequest
|
|
|
@ -1,33 +0,0 @@
|
||||||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
|
||||||
|
|
||||||
const assert = require('assert-plus')
|
|
||||||
const util = require('util')
|
|
||||||
|
|
||||||
const LDAPResult = require('./result')
|
|
||||||
const Protocol = require('@ldapjs/protocol')
|
|
||||||
|
|
||||||
/// --- API
|
|
||||||
|
|
||||||
function CompareResponse (options) {
|
|
||||||
options = options || {}
|
|
||||||
assert.object(options)
|
|
||||||
|
|
||||||
options.protocolOp = Protocol.operations.LDAP_RES_COMPARE
|
|
||||||
LDAPResult.call(this, options)
|
|
||||||
}
|
|
||||||
util.inherits(CompareResponse, LDAPResult)
|
|
||||||
|
|
||||||
CompareResponse.prototype.end = function (matches) {
|
|
||||||
let status = 0x06
|
|
||||||
if (typeof (matches) === 'boolean') {
|
|
||||||
if (!matches) { status = 0x05 } // Compare false
|
|
||||||
} else {
|
|
||||||
status = matches
|
|
||||||
}
|
|
||||||
|
|
||||||
return LDAPResult.prototype.end.call(this, status)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// --- Exports
|
|
||||||
|
|
||||||
module.exports = CompareResponse
|
|
|
@ -1,62 +0,0 @@
|
||||||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
|
||||||
|
|
||||||
const assert = require('assert-plus')
|
|
||||||
const util = require('util')
|
|
||||||
|
|
||||||
const LDAPMessage = require('./message')
|
|
||||||
const Protocol = require('@ldapjs/protocol')
|
|
||||||
const lassert = require('../assert')
|
|
||||||
|
|
||||||
/// --- API
|
|
||||||
|
|
||||||
function DeleteRequest (options) {
|
|
||||||
options = options || {}
|
|
||||||
assert.object(options)
|
|
||||||
lassert.optionalStringDN(options.entry)
|
|
||||||
|
|
||||||
options.protocolOp = Protocol.operations.LDAP_REQ_DELETE
|
|
||||||
LDAPMessage.call(this, options)
|
|
||||||
|
|
||||||
this.entry = options.entry || null
|
|
||||||
}
|
|
||||||
util.inherits(DeleteRequest, LDAPMessage)
|
|
||||||
Object.defineProperties(DeleteRequest.prototype, {
|
|
||||||
type: {
|
|
||||||
get: function getType () { return 'DeleteRequest' },
|
|
||||||
configurable: false
|
|
||||||
},
|
|
||||||
_dn: {
|
|
||||||
get: function getDN () { return this.entry },
|
|
||||||
configurable: false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
DeleteRequest.prototype._parse = function (ber, length) {
|
|
||||||
assert.ok(ber)
|
|
||||||
|
|
||||||
this.entry = ber.buffer.slice(0, length).toString('utf8')
|
|
||||||
ber._offset += ber.length
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
DeleteRequest.prototype._toBer = function (ber) {
|
|
||||||
assert.ok(ber)
|
|
||||||
|
|
||||||
const buf = Buffer.from(this.entry.toString())
|
|
||||||
for (let i = 0; i < buf.length; i++) { ber.writeByte(buf[i]) }
|
|
||||||
|
|
||||||
return ber
|
|
||||||
}
|
|
||||||
|
|
||||||
DeleteRequest.prototype._json = function (j) {
|
|
||||||
assert.ok(j)
|
|
||||||
|
|
||||||
j.entry = this.entry
|
|
||||||
|
|
||||||
return j
|
|
||||||
}
|
|
||||||
|
|
||||||
/// --- Exports
|
|
||||||
|
|
||||||
module.exports = DeleteRequest
|
|
|
@ -1,22 +0,0 @@
|
||||||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
|
||||||
|
|
||||||
const assert = require('assert-plus')
|
|
||||||
const util = require('util')
|
|
||||||
|
|
||||||
const LDAPResult = require('./result')
|
|
||||||
const Protocol = require('@ldapjs/protocol')
|
|
||||||
|
|
||||||
/// --- API
|
|
||||||
|
|
||||||
function DeleteResponse (options) {
|
|
||||||
options = options || {}
|
|
||||||
assert.object(options)
|
|
||||||
|
|
||||||
options.protocolOp = Protocol.operations.LDAP_RES_DELETE
|
|
||||||
LDAPResult.call(this, options)
|
|
||||||
}
|
|
||||||
util.inherits(DeleteResponse, LDAPResult)
|
|
||||||
|
|
||||||
/// --- Exports
|
|
||||||
|
|
||||||
module.exports = DeleteResponse
|
|
|
@ -1,117 +0,0 @@
|
||||||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
|
||||||
|
|
||||||
const assert = require('assert-plus')
|
|
||||||
const util = require('util')
|
|
||||||
|
|
||||||
const LDAPMessage = require('./message')
|
|
||||||
const Protocol = require('@ldapjs/protocol')
|
|
||||||
|
|
||||||
/// --- API
|
|
||||||
|
|
||||||
function ExtendedRequest (options) {
|
|
||||||
options = options || {}
|
|
||||||
assert.object(options)
|
|
||||||
assert.optionalString(options.requestName)
|
|
||||||
if (options.requestValue &&
|
|
||||||
!(Buffer.isBuffer(options.requestValue) ||
|
|
||||||
typeof (options.requestValue) === 'string')) {
|
|
||||||
throw new TypeError('options.requestValue must be a buffer or a string')
|
|
||||||
}
|
|
||||||
|
|
||||||
options.protocolOp = Protocol.operations.LDAP_REQ_EXTENSION
|
|
||||||
LDAPMessage.call(this, options)
|
|
||||||
|
|
||||||
this.requestName = options.requestName || ''
|
|
||||||
this.requestValue = options.requestValue
|
|
||||||
|
|
||||||
if (Buffer.isBuffer(this.requestValue)) {
|
|
||||||
this.requestValueBuffer = this.requestValue
|
|
||||||
} else {
|
|
||||||
this.requestValueBuffer = Buffer.from(this.requestValue || '', 'utf8')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
util.inherits(ExtendedRequest, LDAPMessage)
|
|
||||||
Object.defineProperties(ExtendedRequest.prototype, {
|
|
||||||
type: {
|
|
||||||
get: function getType () { return 'ExtendedRequest' },
|
|
||||||
configurable: false
|
|
||||||
},
|
|
||||||
_dn: {
|
|
||||||
get: function getDN () { return this.requestName },
|
|
||||||
configurable: false
|
|
||||||
},
|
|
||||||
name: {
|
|
||||||
get: function getName () { return this.requestName },
|
|
||||||
set: function setName (val) {
|
|
||||||
assert.string(val)
|
|
||||||
this.requestName = val
|
|
||||||
},
|
|
||||||
configurable: false
|
|
||||||
},
|
|
||||||
value: {
|
|
||||||
get: function getValue () { return this.requestValue },
|
|
||||||
set: function setValue (val) {
|
|
||||||
if (!(Buffer.isBuffer(val) || typeof (val) === 'string')) { throw new TypeError('value must be a buffer or a string') }
|
|
||||||
|
|
||||||
if (Buffer.isBuffer(val)) {
|
|
||||||
this.requestValueBuffer = val
|
|
||||||
} else {
|
|
||||||
this.requestValueBuffer = Buffer.from(val, 'utf8')
|
|
||||||
}
|
|
||||||
|
|
||||||
this.requestValue = val
|
|
||||||
},
|
|
||||||
configurable: false
|
|
||||||
},
|
|
||||||
valueBuffer: {
|
|
||||||
get: function getValueBuffer () {
|
|
||||||
return this.requestValueBuffer
|
|
||||||
},
|
|
||||||
set: function setValueBuffer (val) {
|
|
||||||
if (!Buffer.isBuffer(val)) { throw new TypeError('valueBuffer must be a buffer') }
|
|
||||||
|
|
||||||
this.value = val
|
|
||||||
},
|
|
||||||
configurable: false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
ExtendedRequest.prototype._parse = function (ber) {
|
|
||||||
assert.ok(ber)
|
|
||||||
|
|
||||||
this.requestName = ber.readString(0x80)
|
|
||||||
if (ber.peek() === 0x81) {
|
|
||||||
this.requestValueBuffer = ber.readString(0x81, true)
|
|
||||||
this.requestValue = this.requestValueBuffer.toString('utf8')
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
ExtendedRequest.prototype._toBer = function (ber) {
|
|
||||||
assert.ok(ber)
|
|
||||||
|
|
||||||
ber.writeString(this.requestName, 0x80)
|
|
||||||
if (Buffer.isBuffer(this.requestValue)) {
|
|
||||||
ber.writeBuffer(this.requestValue, 0x81)
|
|
||||||
} else if (typeof (this.requestValue) === 'string') {
|
|
||||||
ber.writeString(this.requestValue, 0x81)
|
|
||||||
}
|
|
||||||
|
|
||||||
return ber
|
|
||||||
}
|
|
||||||
|
|
||||||
ExtendedRequest.prototype._json = function (j) {
|
|
||||||
assert.ok(j)
|
|
||||||
|
|
||||||
j.requestName = this.requestName
|
|
||||||
j.requestValue = (Buffer.isBuffer(this.requestValue))
|
|
||||||
? this.requestValue.toString('hex')
|
|
||||||
: this.requestValue
|
|
||||||
|
|
||||||
return j
|
|
||||||
}
|
|
||||||
|
|
||||||
/// --- Exports
|
|
||||||
|
|
||||||
module.exports = ExtendedRequest
|
|
|
@ -1,86 +0,0 @@
|
||||||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
|
||||||
|
|
||||||
const assert = require('assert-plus')
|
|
||||||
const util = require('util')
|
|
||||||
|
|
||||||
const LDAPResult = require('./result')
|
|
||||||
const Protocol = require('@ldapjs/protocol')
|
|
||||||
|
|
||||||
/// --- API
|
|
||||||
|
|
||||||
function ExtendedResponse (options) {
|
|
||||||
options = options || {}
|
|
||||||
assert.object(options)
|
|
||||||
assert.optionalString(options.responseName)
|
|
||||||
assert.optionalString(options.responsevalue)
|
|
||||||
|
|
||||||
this.responseName = options.responseName || undefined
|
|
||||||
this.responseValue = options.responseValue || undefined
|
|
||||||
|
|
||||||
options.protocolOp = Protocol.operations.LDAP_RES_EXTENSION
|
|
||||||
LDAPResult.call(this, options)
|
|
||||||
}
|
|
||||||
util.inherits(ExtendedResponse, LDAPResult)
|
|
||||||
Object.defineProperties(ExtendedResponse.prototype, {
|
|
||||||
type: {
|
|
||||||
get: function getType () { return 'ExtendedResponse' },
|
|
||||||
configurable: false
|
|
||||||
},
|
|
||||||
_dn: {
|
|
||||||
get: function getDN () { return this.responseName },
|
|
||||||
configurable: false
|
|
||||||
},
|
|
||||||
name: {
|
|
||||||
get: function getName () { return this.responseName },
|
|
||||||
set: function setName (val) {
|
|
||||||
assert.string(val)
|
|
||||||
this.responseName = val
|
|
||||||
},
|
|
||||||
configurable: false
|
|
||||||
},
|
|
||||||
value: {
|
|
||||||
get: function getValue () { return this.responseValue },
|
|
||||||
set: function (val) {
|
|
||||||
assert.string(val)
|
|
||||||
this.responseValue = val
|
|
||||||
},
|
|
||||||
configurable: false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
ExtendedResponse.prototype._parse = function (ber) {
|
|
||||||
assert.ok(ber)
|
|
||||||
|
|
||||||
if (!LDAPResult.prototype._parse.call(this, ber)) { return false }
|
|
||||||
|
|
||||||
if (ber.peek() === 0x8a) { this.responseName = ber.readString(0x8a) }
|
|
||||||
if (ber.peek() === 0x8b) { this.responseValue = ber.readString(0x8b) }
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
ExtendedResponse.prototype._toBer = function (ber) {
|
|
||||||
assert.ok(ber)
|
|
||||||
|
|
||||||
if (!LDAPResult.prototype._toBer.call(this, ber)) { return false }
|
|
||||||
|
|
||||||
if (this.responseName) { ber.writeString(this.responseName, 0x8a) }
|
|
||||||
if (this.responseValue) { ber.writeString(this.responseValue, 0x8b) }
|
|
||||||
|
|
||||||
return ber
|
|
||||||
}
|
|
||||||
|
|
||||||
ExtendedResponse.prototype._json = function (j) {
|
|
||||||
assert.ok(j)
|
|
||||||
|
|
||||||
j = LDAPResult.prototype._json.call(this, j)
|
|
||||||
|
|
||||||
j.responseName = this.responseName
|
|
||||||
j.responseValue = this.responseValue
|
|
||||||
|
|
||||||
return j
|
|
||||||
}
|
|
||||||
|
|
||||||
/// --- Exports
|
|
||||||
|
|
||||||
module.exports = ExtendedResponse
|
|
|
@ -1,61 +1,39 @@
|
||||||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||||
|
|
||||||
const LDAPMessage = require('./message')
|
const messages = require('@ldapjs/messages')
|
||||||
const LDAPResult = require('./result')
|
|
||||||
const Parser = require('./parser')
|
const Parser = require('./parser')
|
||||||
|
|
||||||
const AbandonRequest = require('./abandon_request')
|
|
||||||
const AbandonResponse = require('./abandon_response')
|
|
||||||
const AddRequest = require('./add_request')
|
|
||||||
const AddResponse = require('./add_response')
|
|
||||||
const BindRequest = require('./bind_request')
|
|
||||||
const BindResponse = require('./bind_response')
|
|
||||||
const CompareRequest = require('./compare_request')
|
|
||||||
const CompareResponse = require('./compare_response')
|
|
||||||
const DeleteRequest = require('./del_request')
|
|
||||||
const DeleteResponse = require('./del_response')
|
|
||||||
const ExtendedRequest = require('./ext_request')
|
|
||||||
const ExtendedResponse = require('./ext_response')
|
|
||||||
const ModifyRequest = require('./modify_request')
|
|
||||||
const ModifyResponse = require('./modify_response')
|
|
||||||
const ModifyDNRequest = require('./moddn_request')
|
|
||||||
const ModifyDNResponse = require('./moddn_response')
|
|
||||||
const SearchRequest = require('./search_request')
|
|
||||||
const SearchEntry = require('./search_entry')
|
|
||||||
const SearchReference = require('./search_reference')
|
|
||||||
const SearchResponse = require('./search_response')
|
const SearchResponse = require('./search_response')
|
||||||
const UnbindRequest = require('./unbind_request')
|
|
||||||
const UnbindResponse = require('./unbind_response')
|
|
||||||
|
|
||||||
/// --- API
|
/// --- API
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|
||||||
LDAPMessage: LDAPMessage,
|
LDAPMessage: messages.LdapMessage,
|
||||||
LDAPResult: LDAPResult,
|
LDAPResult: messages.LdapResult,
|
||||||
Parser: Parser,
|
Parser: Parser,
|
||||||
|
|
||||||
AbandonRequest: AbandonRequest,
|
AbandonRequest: messages.AbandonRequest,
|
||||||
AbandonResponse: AbandonResponse,
|
AbandonResponse: messages.AbandonResponse,
|
||||||
AddRequest: AddRequest,
|
AddRequest: messages.AddRequest,
|
||||||
AddResponse: AddResponse,
|
AddResponse: messages.AddResponse,
|
||||||
BindRequest: BindRequest,
|
BindRequest: messages.BindRequest,
|
||||||
BindResponse: BindResponse,
|
BindResponse: messages.BindResponse,
|
||||||
CompareRequest: CompareRequest,
|
CompareRequest: messages.CompareRequest,
|
||||||
CompareResponse: CompareResponse,
|
CompareResponse: messages.CompareResponse,
|
||||||
DeleteRequest: DeleteRequest,
|
DeleteRequest: messages.DeleteRequest,
|
||||||
DeleteResponse: DeleteResponse,
|
DeleteResponse: messages.DeleteResponse,
|
||||||
ExtendedRequest: ExtendedRequest,
|
ExtendedRequest: messages.ExtensionRequest,
|
||||||
ExtendedResponse: ExtendedResponse,
|
ExtendedResponse: messages.ExtensionResponse,
|
||||||
ModifyRequest: ModifyRequest,
|
ModifyRequest: messages.ModifyRequest,
|
||||||
ModifyResponse: ModifyResponse,
|
ModifyResponse: messages.ModifyResponse,
|
||||||
ModifyDNRequest: ModifyDNRequest,
|
ModifyDNRequest: messages.ModifyDnRequest,
|
||||||
ModifyDNResponse: ModifyDNResponse,
|
ModifyDNResponse: messages.ModifyDnResponse,
|
||||||
SearchRequest: SearchRequest,
|
SearchRequest: messages.SearchRequest,
|
||||||
SearchEntry: SearchEntry,
|
SearchEntry: messages.SearchResultEntry,
|
||||||
SearchReference: SearchReference,
|
SearchReference: messages.SearchResultReference,
|
||||||
SearchResponse: SearchResponse,
|
SearchResponse: SearchResponse,
|
||||||
UnbindRequest: UnbindRequest,
|
UnbindRequest: messages.UnbindRequest
|
||||||
UnbindResponse: UnbindResponse
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,109 +0,0 @@
|
||||||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
|
||||||
|
|
||||||
const assert = require('assert-plus')
|
|
||||||
const util = require('util')
|
|
||||||
|
|
||||||
const asn1 = require('@ldapjs/asn1')
|
|
||||||
|
|
||||||
const logger = require('../logger')
|
|
||||||
// var Control = require('../controls').Control
|
|
||||||
|
|
||||||
/// --- Globals
|
|
||||||
|
|
||||||
// var Ber = asn1.Ber
|
|
||||||
// var BerReader = asn1.BerReader
|
|
||||||
const BerWriter = asn1.BerWriter
|
|
||||||
const getControl = require('../controls').getControl
|
|
||||||
|
|
||||||
/// --- API
|
|
||||||
|
|
||||||
/**
|
|
||||||
* LDAPMessage structure.
|
|
||||||
*
|
|
||||||
* @param {Object} options stuff.
|
|
||||||
*/
|
|
||||||
function LDAPMessage (options) {
|
|
||||||
assert.object(options)
|
|
||||||
|
|
||||||
this.messageID = options.messageID || 0
|
|
||||||
this.protocolOp = options.protocolOp || undefined
|
|
||||||
this.controls = options.controls ? options.controls.slice(0) : []
|
|
||||||
|
|
||||||
this.log = options.log || logger
|
|
||||||
}
|
|
||||||
Object.defineProperties(LDAPMessage.prototype, {
|
|
||||||
id: {
|
|
||||||
get: function getId () { return this.messageID },
|
|
||||||
configurable: false
|
|
||||||
},
|
|
||||||
dn: {
|
|
||||||
get: function getDN () { return this._dn || '' },
|
|
||||||
configurable: false
|
|
||||||
},
|
|
||||||
type: {
|
|
||||||
get: function getType () { return 'LDAPMessage' },
|
|
||||||
configurable: false
|
|
||||||
},
|
|
||||||
json: {
|
|
||||||
get: function () {
|
|
||||||
const out = this._json({
|
|
||||||
messageID: this.messageID,
|
|
||||||
protocolOp: this.type
|
|
||||||
})
|
|
||||||
out.controls = this.controls
|
|
||||||
return out
|
|
||||||
},
|
|
||||||
configurable: false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
LDAPMessage.prototype.toString = function () {
|
|
||||||
return JSON.stringify(this.json)
|
|
||||||
}
|
|
||||||
|
|
||||||
LDAPMessage.prototype.parse = function (ber) {
|
|
||||||
assert.ok(ber)
|
|
||||||
|
|
||||||
this.log.trace('parse: data=%s', util.inspect(ber.buffer))
|
|
||||||
|
|
||||||
// Delegate off to the specific type to parse
|
|
||||||
this._parse(ber, ber.length)
|
|
||||||
|
|
||||||
// Look for controls
|
|
||||||
if (ber.peek() === 0xa0) {
|
|
||||||
ber.readSequence()
|
|
||||||
const end = ber.offset + ber.length
|
|
||||||
while (ber.offset < end) {
|
|
||||||
const c = getControl(ber)
|
|
||||||
if (c) { this.controls.push(c) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.log.trace('Parsing done: %j', this.json)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
LDAPMessage.prototype.toBer = function () {
|
|
||||||
let writer = new BerWriter()
|
|
||||||
writer.startSequence()
|
|
||||||
writer.writeInt(this.messageID)
|
|
||||||
|
|
||||||
writer.startSequence(this.protocolOp)
|
|
||||||
if (this._toBer) { writer = this._toBer(writer) }
|
|
||||||
writer.endSequence()
|
|
||||||
|
|
||||||
if (this.controls && this.controls.length) {
|
|
||||||
writer.startSequence(0xa0)
|
|
||||||
this.controls.forEach(function (c) {
|
|
||||||
c.toBer(writer)
|
|
||||||
})
|
|
||||||
writer.endSequence()
|
|
||||||
}
|
|
||||||
|
|
||||||
writer.endSequence()
|
|
||||||
return writer.buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
/// --- Exports
|
|
||||||
|
|
||||||
module.exports = LDAPMessage
|
|
|
@ -1,85 +0,0 @@
|
||||||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
|
||||||
|
|
||||||
const assert = require('assert-plus')
|
|
||||||
const util = require('util')
|
|
||||||
|
|
||||||
const LDAPMessage = require('./message')
|
|
||||||
const Protocol = require('@ldapjs/protocol')
|
|
||||||
const dn = require('../dn')
|
|
||||||
const lassert = require('../assert')
|
|
||||||
|
|
||||||
/// --- API
|
|
||||||
|
|
||||||
function ModifyDNRequest (options) {
|
|
||||||
options = options || {}
|
|
||||||
assert.object(options)
|
|
||||||
assert.optionalBool(options.deleteOldRdn)
|
|
||||||
lassert.optionalStringDN(options.entry)
|
|
||||||
lassert.optionalDN(options.newRdn)
|
|
||||||
lassert.optionalDN(options.newSuperior)
|
|
||||||
|
|
||||||
options.protocolOp = Protocol.operations.LDAP_REQ_MODRDN
|
|
||||||
LDAPMessage.call(this, options)
|
|
||||||
|
|
||||||
this.entry = options.entry || null
|
|
||||||
this.newRdn = options.newRdn || null
|
|
||||||
this.deleteOldRdn = options.deleteOldRdn || true
|
|
||||||
this.newSuperior = options.newSuperior || null
|
|
||||||
}
|
|
||||||
util.inherits(ModifyDNRequest, LDAPMessage)
|
|
||||||
Object.defineProperties(ModifyDNRequest.prototype, {
|
|
||||||
type: {
|
|
||||||
get: function getType () { return 'ModifyDNRequest' },
|
|
||||||
configurable: false
|
|
||||||
},
|
|
||||||
_dn: {
|
|
||||||
get: function getDN () { return this.entry },
|
|
||||||
configurable: false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
ModifyDNRequest.prototype._parse = function (ber) {
|
|
||||||
assert.ok(ber)
|
|
||||||
|
|
||||||
this.entry = ber.readString()
|
|
||||||
this.newRdn = dn.parse(ber.readString())
|
|
||||||
this.deleteOldRdn = ber.readBoolean()
|
|
||||||
if (ber.peek() === 0x80) { this.newSuperior = dn.parse(ber.readString(0x80)) }
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
ModifyDNRequest.prototype._toBer = function (ber) {
|
|
||||||
// assert.ok(ber);
|
|
||||||
|
|
||||||
ber.writeString(this.entry.toString())
|
|
||||||
ber.writeString(this.newRdn.toString())
|
|
||||||
ber.writeBoolean(this.deleteOldRdn)
|
|
||||||
if (this.newSuperior) {
|
|
||||||
const s = this.newSuperior.toString()
|
|
||||||
const len = Buffer.byteLength(s)
|
|
||||||
|
|
||||||
ber.writeByte(0x80) // MODIFY_DN_REQUEST_NEW_SUPERIOR_TAG
|
|
||||||
ber.writeLength(len)
|
|
||||||
ber._ensure(len)
|
|
||||||
ber._buf.write(s, ber._offset)
|
|
||||||
ber._offset += len
|
|
||||||
}
|
|
||||||
|
|
||||||
return ber
|
|
||||||
}
|
|
||||||
|
|
||||||
ModifyDNRequest.prototype._json = function (j) {
|
|
||||||
assert.ok(j)
|
|
||||||
|
|
||||||
j.entry = this.entry.toString()
|
|
||||||
j.newRdn = this.newRdn.toString()
|
|
||||||
j.deleteOldRdn = this.deleteOldRdn
|
|
||||||
j.newSuperior = this.newSuperior ? this.newSuperior.toString() : ''
|
|
||||||
|
|
||||||
return j
|
|
||||||
}
|
|
||||||
|
|
||||||
/// --- Exports
|
|
||||||
|
|
||||||
module.exports = ModifyDNRequest
|
|
|
@ -1,22 +0,0 @@
|
||||||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
|
||||||
|
|
||||||
const assert = require('assert-plus')
|
|
||||||
const util = require('util')
|
|
||||||
|
|
||||||
const LDAPResult = require('./result')
|
|
||||||
const Protocol = require('@ldapjs/protocol')
|
|
||||||
|
|
||||||
/// --- API
|
|
||||||
|
|
||||||
function ModifyDNResponse (options) {
|
|
||||||
options = options || {}
|
|
||||||
assert.object(options)
|
|
||||||
|
|
||||||
options.protocolOp = Protocol.operations.LDAP_RES_MODRDN
|
|
||||||
LDAPResult.call(this, options)
|
|
||||||
}
|
|
||||||
util.inherits(ModifyDNResponse, LDAPResult)
|
|
||||||
|
|
||||||
/// --- Exports
|
|
||||||
|
|
||||||
module.exports = ModifyDNResponse
|
|
|
@ -1,83 +0,0 @@
|
||||||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
|
||||||
|
|
||||||
const assert = require('assert-plus')
|
|
||||||
const util = require('util')
|
|
||||||
|
|
||||||
const LDAPMessage = require('./message')
|
|
||||||
const Change = require('../change')
|
|
||||||
const Protocol = require('@ldapjs/protocol')
|
|
||||||
const lassert = require('../assert')
|
|
||||||
|
|
||||||
/// --- API
|
|
||||||
|
|
||||||
function ModifyRequest (options) {
|
|
||||||
options = options || {}
|
|
||||||
assert.object(options)
|
|
||||||
lassert.optionalStringDN(options.object)
|
|
||||||
lassert.optionalArrayOfAttribute(options.attributes)
|
|
||||||
|
|
||||||
options.protocolOp = Protocol.operations.LDAP_REQ_MODIFY
|
|
||||||
LDAPMessage.call(this, options)
|
|
||||||
|
|
||||||
this.object = options.object || null
|
|
||||||
this.changes = options.changes ? options.changes.slice(0) : []
|
|
||||||
}
|
|
||||||
util.inherits(ModifyRequest, LDAPMessage)
|
|
||||||
Object.defineProperties(ModifyRequest.prototype, {
|
|
||||||
type: {
|
|
||||||
get: function getType () { return 'ModifyRequest' },
|
|
||||||
configurable: false
|
|
||||||
},
|
|
||||||
_dn: {
|
|
||||||
get: function getDN () { return this.object },
|
|
||||||
configurable: false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
ModifyRequest.prototype._parse = function (ber) {
|
|
||||||
assert.ok(ber)
|
|
||||||
|
|
||||||
this.object = ber.readString()
|
|
||||||
|
|
||||||
ber.readSequence()
|
|
||||||
const end = ber.offset + ber.length
|
|
||||||
while (ber.offset < end) {
|
|
||||||
const c = new Change()
|
|
||||||
c.parse(ber)
|
|
||||||
c.modification.type = c.modification.type.toLowerCase()
|
|
||||||
this.changes.push(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.changes.sort(Change.compare)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
ModifyRequest.prototype._toBer = function (ber) {
|
|
||||||
assert.ok(ber)
|
|
||||||
|
|
||||||
ber.writeString(this.object.toString())
|
|
||||||
ber.startSequence()
|
|
||||||
this.changes.forEach(function (c) {
|
|
||||||
c.toBer(ber)
|
|
||||||
})
|
|
||||||
ber.endSequence()
|
|
||||||
|
|
||||||
return ber
|
|
||||||
}
|
|
||||||
|
|
||||||
ModifyRequest.prototype._json = function (j) {
|
|
||||||
assert.ok(j)
|
|
||||||
|
|
||||||
j.object = this.object
|
|
||||||
j.changes = []
|
|
||||||
|
|
||||||
this.changes.forEach(function (c) {
|
|
||||||
j.changes.push(c.json)
|
|
||||||
})
|
|
||||||
|
|
||||||
return j
|
|
||||||
}
|
|
||||||
|
|
||||||
/// --- Exports
|
|
||||||
|
|
||||||
module.exports = ModifyRequest
|
|
|
@ -1,22 +0,0 @@
|
||||||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
|
||||||
|
|
||||||
const assert = require('assert-plus')
|
|
||||||
const util = require('util')
|
|
||||||
|
|
||||||
const LDAPResult = require('./result')
|
|
||||||
const Protocol = require('@ldapjs/protocol')
|
|
||||||
|
|
||||||
/// --- API
|
|
||||||
|
|
||||||
function ModifyResponse (options) {
|
|
||||||
options = options || {}
|
|
||||||
assert.object(options)
|
|
||||||
|
|
||||||
options.protocolOp = Protocol.operations.LDAP_RES_MODIFY
|
|
||||||
LDAPResult.call(this, options)
|
|
||||||
}
|
|
||||||
util.inherits(ModifyResponse, LDAPResult)
|
|
||||||
|
|
||||||
/// --- Exports
|
|
||||||
|
|
||||||
module.exports = ModifyResponse
|
|
|
@ -5,39 +5,35 @@ const util = require('util')
|
||||||
|
|
||||||
const assert = require('assert-plus')
|
const assert = require('assert-plus')
|
||||||
const asn1 = require('@ldapjs/asn1')
|
const asn1 = require('@ldapjs/asn1')
|
||||||
// var VError = require('verror').VError
|
|
||||||
const logger = require('../logger')
|
const logger = require('../logger')
|
||||||
|
|
||||||
const AbandonRequest = require('./abandon_request')
|
const messages = require('@ldapjs/messages')
|
||||||
const AddRequest = require('./add_request')
|
const AbandonRequest = messages.AbandonRequest
|
||||||
const AddResponse = require('./add_response')
|
const AddRequest = messages.AddRequest
|
||||||
const BindRequest = require('./bind_request')
|
const AddResponse = messages.AddResponse
|
||||||
const BindResponse = require('./bind_response')
|
const BindRequest = messages.BindRequest
|
||||||
const CompareRequest = require('./compare_request')
|
const BindResponse = messages.BindResponse
|
||||||
const CompareResponse = require('./compare_response')
|
const CompareRequest = messages.CompareRequest
|
||||||
const DeleteRequest = require('./del_request')
|
const CompareResponse = messages.CompareResponse
|
||||||
const DeleteResponse = require('./del_response')
|
const DeleteRequest = messages.DeleteRequest
|
||||||
const ExtendedRequest = require('./ext_request')
|
const DeleteResponse = messages.DeleteResponse
|
||||||
const ExtendedResponse = require('./ext_response')
|
const ExtendedRequest = messages.ExtensionRequest
|
||||||
const ModifyRequest = require('./modify_request')
|
const ExtendedResponse = messages.ExtensionResponse
|
||||||
const ModifyResponse = require('./modify_response')
|
const ModifyRequest = messages.ModifyRequest
|
||||||
const ModifyDNRequest = require('./moddn_request')
|
const ModifyResponse = messages.ModifyResponse
|
||||||
const ModifyDNResponse = require('./moddn_response')
|
const ModifyDNRequest = messages.ModifyDnRequest
|
||||||
const SearchRequest = require('./search_request')
|
const ModifyDNResponse = messages.ModifyDnResponse
|
||||||
const SearchEntry = require('./search_entry')
|
const SearchRequest = messages.SearchRequest
|
||||||
const SearchReference = require('./search_reference')
|
const SearchEntry = messages.SearchResultEntry
|
||||||
|
const SearchReference = messages.SearchResultReference
|
||||||
const SearchResponse = require('./search_response')
|
const SearchResponse = require('./search_response')
|
||||||
const UnbindRequest = require('./unbind_request')
|
const UnbindRequest = messages.UnbindRequest
|
||||||
// var UnbindResponse = require('./unbind_response')
|
const LDAPResult = messages.LdapResult
|
||||||
|
|
||||||
const LDAPResult = require('./result')
|
|
||||||
// var Message = require('./message')
|
|
||||||
|
|
||||||
const Protocol = require('@ldapjs/protocol')
|
const Protocol = require('@ldapjs/protocol')
|
||||||
|
|
||||||
/// --- Globals
|
/// --- Globals
|
||||||
|
|
||||||
// var Ber = asn1.Ber
|
|
||||||
const BerReader = asn1.BerReader
|
const BerReader = asn1.BerReader
|
||||||
|
|
||||||
/// --- API
|
/// --- API
|
||||||
|
@ -52,6 +48,13 @@ function Parser (options = {}) {
|
||||||
}
|
}
|
||||||
util.inherits(Parser, EventEmitter)
|
util.inherits(Parser, EventEmitter)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The LDAP server/client implementations will receive data from a stream and feed
|
||||||
|
* it into this method. This method will collect that data into an internal
|
||||||
|
* growing buffer. As that buffer fills with enough data to constitute a valid
|
||||||
|
* LDAP message, the data will be parsed, emitted as a message object, and
|
||||||
|
* reset the buffer to account for any next message in the stream.
|
||||||
|
*/
|
||||||
Parser.prototype.write = function (data) {
|
Parser.prototype.write = function (data) {
|
||||||
if (!data || !Buffer.isBuffer(data)) { throw new TypeError('data (buffer) required') }
|
if (!data || !Buffer.isBuffer(data)) { throw new TypeError('data (buffer) required') }
|
||||||
|
|
||||||
|
@ -64,9 +67,9 @@ Parser.prototype.write = function (data) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
self.buffer = (self.buffer ? Buffer.concat([self.buffer, data]) : data)
|
self.buffer = self.buffer ? Buffer.concat([self.buffer, data]) : data
|
||||||
|
|
||||||
const ber = new BerReader(self.buffer)
|
let ber = new BerReader(self.buffer)
|
||||||
|
|
||||||
let foundSeq = false
|
let foundSeq = false
|
||||||
try {
|
try {
|
||||||
|
@ -80,9 +83,22 @@ Parser.prototype.write = function (data) {
|
||||||
return false
|
return false
|
||||||
} else if (ber.remain > ber.length) {
|
} else if (ber.remain > ber.length) {
|
||||||
// ETOOMUCH
|
// ETOOMUCH
|
||||||
// This is sort of ugly, but allows us to make miminal copies
|
|
||||||
|
// This is an odd branch. Basically, it is setting `nextMessage` to
|
||||||
|
// a buffer that represents data part of a message subsequent to the one
|
||||||
|
// being processed. It then re-creates `ber` as a representation of
|
||||||
|
// the message being processed and advances its offset to the value
|
||||||
|
// position of the TLV.
|
||||||
|
|
||||||
|
// Set `nextMessage` to the bytes subsequent to the current message's
|
||||||
|
// value bytes. That is, slice from the byte immediately following the
|
||||||
|
// current message's value bytes until the end of the buffer.
|
||||||
nextMessage = self.buffer.slice(ber.offset + ber.length)
|
nextMessage = self.buffer.slice(ber.offset + ber.length)
|
||||||
ber._size = ber.offset + ber.length
|
|
||||||
|
const currOffset = ber.offset
|
||||||
|
ber = new BerReader(ber.buffer.subarray(0, currOffset + ber.length))
|
||||||
|
ber.readSequence()
|
||||||
|
|
||||||
assert.equal(ber.remain, ber.length)
|
assert.equal(ber.remain, ber.length)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,13 +108,25 @@ Parser.prototype.write = function (data) {
|
||||||
|
|
||||||
let message
|
let message
|
||||||
try {
|
try {
|
||||||
// Bail here if peer isn't speaking protocol at all
|
if (Object.prototype.toString.call(ber) === '[object BerReader]') {
|
||||||
message = this.getMessage(ber)
|
// Parse the BER into a JavaScript object representation. The message
|
||||||
|
// objects require the full sequence in order to construct the object.
|
||||||
|
// At this point, we have already read the sequence tag and length, so
|
||||||
|
// we need to rewind the buffer a bit. The `.sequenceToReader` method
|
||||||
|
// does this for us.
|
||||||
|
message = messages.LdapMessage.parse(ber.sequenceToReader())
|
||||||
|
} else {
|
||||||
|
// Bail here if peer isn't speaking protocol at all
|
||||||
|
message = this.getMessage(ber)
|
||||||
|
}
|
||||||
|
|
||||||
if (!message) {
|
if (!message) {
|
||||||
return end()
|
return end()
|
||||||
}
|
}
|
||||||
message.parse(ber)
|
|
||||||
|
// TODO: find a better way to handle logging now that messages and the
|
||||||
|
// server are decoupled. ~ jsumners 2023-02-17
|
||||||
|
message.log = this.log
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.emit('error', e, message)
|
this.emit('error', e, message)
|
||||||
return false
|
return false
|
||||||
|
@ -113,7 +141,7 @@ Parser.prototype.getMessage = function (ber) {
|
||||||
|
|
||||||
const self = this
|
const self = this
|
||||||
|
|
||||||
const messageID = ber.readInt()
|
const messageId = ber.readInt()
|
||||||
const type = ber.readSequence()
|
const type = ber.readSequence()
|
||||||
|
|
||||||
let Message
|
let Message
|
||||||
|
@ -203,7 +231,7 @@ Parser.prototype.getMessage = function (ber) {
|
||||||
new Error('Op 0x' + (type ? type.toString(16) : '??') +
|
new Error('Op 0x' + (type ? type.toString(16) : '??') +
|
||||||
' not supported'),
|
' not supported'),
|
||||||
new LDAPResult({
|
new LDAPResult({
|
||||||
messageID: messageID,
|
messageId: messageId,
|
||||||
protocolOp: type || Protocol.operations.LDAP_RES_EXTENSION
|
protocolOp: type || Protocol.operations.LDAP_RES_EXTENSION
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
@ -211,7 +239,7 @@ Parser.prototype.getMessage = function (ber) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Message({
|
return new Message({
|
||||||
messageID: messageID,
|
messageId: messageId,
|
||||||
log: self.log
|
log: self.log
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,121 +0,0 @@
|
||||||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
|
||||||
|
|
||||||
const assert = require('assert-plus')
|
|
||||||
const util = require('util')
|
|
||||||
|
|
||||||
// var asn1 = require('@ldapjs/asn1')
|
|
||||||
|
|
||||||
const dtrace = require('../dtrace')
|
|
||||||
const LDAPMessage = require('./message')
|
|
||||||
const Protocol = require('@ldapjs/protocol')
|
|
||||||
|
|
||||||
/// --- Globals
|
|
||||||
|
|
||||||
// var Ber = asn1.Ber
|
|
||||||
// var BerWriter = asn1.BerWriter
|
|
||||||
|
|
||||||
/// --- API
|
|
||||||
|
|
||||||
function LDAPResult (options) {
|
|
||||||
options = options || {}
|
|
||||||
assert.object(options)
|
|
||||||
assert.optionalNumber(options.status)
|
|
||||||
assert.optionalString(options.matchedDN)
|
|
||||||
assert.optionalString(options.errorMessage)
|
|
||||||
assert.optionalArrayOfString(options.referrals)
|
|
||||||
|
|
||||||
LDAPMessage.call(this, options)
|
|
||||||
|
|
||||||
this.status = options.status || 0 // LDAP SUCCESS
|
|
||||||
this.matchedDN = options.matchedDN || ''
|
|
||||||
this.errorMessage = options.errorMessage || ''
|
|
||||||
this.referrals = options.referrals || []
|
|
||||||
|
|
||||||
this.connection = options.connection || null
|
|
||||||
}
|
|
||||||
util.inherits(LDAPResult, LDAPMessage)
|
|
||||||
Object.defineProperties(LDAPResult.prototype, {
|
|
||||||
type: {
|
|
||||||
get: function getType () { return 'LDAPResult' },
|
|
||||||
configurable: false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
LDAPResult.prototype.end = function (status) {
|
|
||||||
assert.ok(this.connection)
|
|
||||||
|
|
||||||
if (typeof (status) === 'number') { this.status = status }
|
|
||||||
|
|
||||||
const ber = this.toBer()
|
|
||||||
this.log.debug('%s: sending: %j', this.connection.ldap.id, this.json)
|
|
||||||
|
|
||||||
try {
|
|
||||||
const self = this
|
|
||||||
this.connection.write(ber)
|
|
||||||
|
|
||||||
if (self._dtraceOp && self._dtraceId) {
|
|
||||||
dtrace.fire('server-' + self._dtraceOp + '-done', function () {
|
|
||||||
const c = self.connection || { ldap: {} }
|
|
||||||
return [
|
|
||||||
self._dtraceId || 0,
|
|
||||||
(c.remoteAddress || ''),
|
|
||||||
c.ldap.bindDN ? c.ldap.bindDN.toString() : '',
|
|
||||||
(self.requestDN ? self.requestDN.toString() : ''),
|
|
||||||
status || self.status,
|
|
||||||
self.errorMessage
|
|
||||||
]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
this.log.warn(e, '%s failure to write message %j',
|
|
||||||
this.connection.ldap.id, this.json)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LDAPResult.prototype._parse = function (ber) {
|
|
||||||
assert.ok(ber)
|
|
||||||
|
|
||||||
this.status = ber.readEnumeration()
|
|
||||||
this.matchedDN = ber.readString()
|
|
||||||
this.errorMessage = ber.readString()
|
|
||||||
|
|
||||||
const t = ber.peek()
|
|
||||||
|
|
||||||
if (t === Protocol.operations.LDAP_RES_REFERRAL) {
|
|
||||||
const end = ber.offset + ber.length
|
|
||||||
while (ber.offset < end) { this.referrals.push(ber.readString()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
LDAPResult.prototype._toBer = function (ber) {
|
|
||||||
assert.ok(ber)
|
|
||||||
|
|
||||||
ber.writeEnumeration(this.status)
|
|
||||||
ber.writeString(this.matchedDN || '')
|
|
||||||
ber.writeString(this.errorMessage || '')
|
|
||||||
|
|
||||||
if (this.referrals.length) {
|
|
||||||
ber.startSequence(Protocol.operations.LDAP_RES_REFERRAL)
|
|
||||||
ber.writeStringArray(this.referrals)
|
|
||||||
ber.endSequence()
|
|
||||||
}
|
|
||||||
|
|
||||||
return ber
|
|
||||||
}
|
|
||||||
|
|
||||||
LDAPResult.prototype._json = function (j) {
|
|
||||||
assert.ok(j)
|
|
||||||
|
|
||||||
j.status = this.status
|
|
||||||
j.matchedDN = this.matchedDN
|
|
||||||
j.errorMessage = this.errorMessage
|
|
||||||
j.referrals = this.referrals
|
|
||||||
|
|
||||||
return j
|
|
||||||
}
|
|
||||||
|
|
||||||
/// --- Exports
|
|
||||||
|
|
||||||
module.exports = LDAPResult
|
|
|
@ -1,188 +0,0 @@
|
||||||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
|
||||||
|
|
||||||
const assert = require('assert-plus')
|
|
||||||
const util = require('util')
|
|
||||||
|
|
||||||
// var asn1 = require('@ldapjs/asn1')
|
|
||||||
|
|
||||||
const LDAPMessage = require('./message')
|
|
||||||
const Attribute = require('../attribute')
|
|
||||||
const Protocol = require('@ldapjs/protocol')
|
|
||||||
const lassert = require('../assert')
|
|
||||||
|
|
||||||
/// --- Globals
|
|
||||||
|
|
||||||
// var BerWriter = asn1.BerWriter
|
|
||||||
|
|
||||||
/// --- API
|
|
||||||
|
|
||||||
function SearchEntry (options) {
|
|
||||||
options = options || {}
|
|
||||||
assert.object(options)
|
|
||||||
lassert.optionalStringDN(options.objectName)
|
|
||||||
|
|
||||||
options.protocolOp = Protocol.operations.LDAP_RES_SEARCH_ENTRY
|
|
||||||
LDAPMessage.call(this, options)
|
|
||||||
|
|
||||||
this.objectName = options.objectName || null
|
|
||||||
this.setAttributes(options.attributes || [])
|
|
||||||
}
|
|
||||||
util.inherits(SearchEntry, LDAPMessage)
|
|
||||||
Object.defineProperties(SearchEntry.prototype, {
|
|
||||||
type: {
|
|
||||||
get: function getType () { return 'SearchEntry' },
|
|
||||||
configurable: false
|
|
||||||
},
|
|
||||||
_dn: {
|
|
||||||
get: function getDN () { return this.objectName },
|
|
||||||
configurable: false
|
|
||||||
},
|
|
||||||
object: {
|
|
||||||
get: function getObject () {
|
|
||||||
const obj = {
|
|
||||||
dn: this.dn.toString(),
|
|
||||||
controls: []
|
|
||||||
}
|
|
||||||
this.attributes.forEach(function (a) {
|
|
||||||
if (a.vals && a.vals.length) {
|
|
||||||
if (a.vals.length > 1) {
|
|
||||||
obj[a.type] = a.vals.slice()
|
|
||||||
} else {
|
|
||||||
obj[a.type] = a.vals[0]
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
obj[a.type] = []
|
|
||||||
}
|
|
||||||
})
|
|
||||||
this.controls.forEach(function (element) {
|
|
||||||
obj.controls.push(element.json)
|
|
||||||
})
|
|
||||||
return obj
|
|
||||||
},
|
|
||||||
configurable: false
|
|
||||||
},
|
|
||||||
raw: {
|
|
||||||
get: function getRaw () {
|
|
||||||
const obj = {
|
|
||||||
dn: this.dn.toString(),
|
|
||||||
controls: []
|
|
||||||
}
|
|
||||||
|
|
||||||
this.attributes.forEach(function (a) {
|
|
||||||
if (a.buffers && a.buffers.length) {
|
|
||||||
if (a.buffers.length > 1) {
|
|
||||||
obj[a.type] = a.buffers.slice()
|
|
||||||
} else {
|
|
||||||
obj[a.type] = a.buffers[0]
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
obj[a.type] = []
|
|
||||||
}
|
|
||||||
})
|
|
||||||
this.controls.forEach(function (element) {
|
|
||||||
obj.controls.push(element.json)
|
|
||||||
})
|
|
||||||
return obj
|
|
||||||
},
|
|
||||||
configurable: false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
SearchEntry.prototype.addAttribute = function (attr) {
|
|
||||||
if (!attr || typeof (attr) !== 'object') { throw new TypeError('attr (attribute) required') }
|
|
||||||
|
|
||||||
this.attributes.push(attr)
|
|
||||||
}
|
|
||||||
|
|
||||||
SearchEntry.prototype.toObject = function () {
|
|
||||||
return this.object
|
|
||||||
}
|
|
||||||
|
|
||||||
SearchEntry.prototype.fromObject = function (obj) {
|
|
||||||
if (typeof (obj) !== 'object') { throw new TypeError('object required') }
|
|
||||||
|
|
||||||
const self = this
|
|
||||||
if (obj.controls) { this.controls = obj.controls }
|
|
||||||
|
|
||||||
if (obj.attributes) { obj = obj.attributes }
|
|
||||||
this.attributes = []
|
|
||||||
|
|
||||||
Object.keys(obj).forEach(function (k) {
|
|
||||||
self.attributes.push(new Attribute({ type: k, vals: obj[k] }))
|
|
||||||
})
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
SearchEntry.prototype.setAttributes = function (obj) {
|
|
||||||
if (typeof (obj) !== 'object') { throw new TypeError('object required') }
|
|
||||||
|
|
||||||
if (Array.isArray(obj)) {
|
|
||||||
obj.forEach(function (a) {
|
|
||||||
if (!Attribute.isAttribute(a)) { throw new TypeError('entry must be an Array of Attributes') }
|
|
||||||
})
|
|
||||||
this.attributes = obj
|
|
||||||
} else {
|
|
||||||
const self = this
|
|
||||||
|
|
||||||
self.attributes = []
|
|
||||||
Object.keys(obj).forEach(function (k) {
|
|
||||||
const attr = new Attribute({ type: k })
|
|
||||||
if (Array.isArray(obj[k])) {
|
|
||||||
obj[k].forEach(function (v) {
|
|
||||||
attr.addValue(v.toString())
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
attr.addValue(obj[k].toString())
|
|
||||||
}
|
|
||||||
self.attributes.push(attr)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SearchEntry.prototype._json = function (j) {
|
|
||||||
assert.ok(j)
|
|
||||||
|
|
||||||
j.objectName = this.objectName.toString()
|
|
||||||
j.attributes = []
|
|
||||||
this.attributes.forEach(function (a) {
|
|
||||||
j.attributes.push(a.json || a)
|
|
||||||
})
|
|
||||||
|
|
||||||
return j
|
|
||||||
}
|
|
||||||
|
|
||||||
SearchEntry.prototype._parse = function (ber) {
|
|
||||||
assert.ok(ber)
|
|
||||||
|
|
||||||
this.objectName = ber.readString()
|
|
||||||
assert.ok(ber.readSequence())
|
|
||||||
|
|
||||||
const end = ber.offset + ber.length
|
|
||||||
while (ber.offset < end) {
|
|
||||||
const a = new Attribute()
|
|
||||||
a.parse(ber)
|
|
||||||
this.attributes.push(a)
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
SearchEntry.prototype._toBer = function (ber) {
|
|
||||||
assert.ok(ber)
|
|
||||||
|
|
||||||
const formattedObjectName = this.objectName.format({ skipSpace: true })
|
|
||||||
ber.writeString(formattedObjectName)
|
|
||||||
ber.startSequence()
|
|
||||||
this.attributes.forEach(function (a) {
|
|
||||||
// This may or may not be an attribute
|
|
||||||
ber = Attribute.toBer(a, ber)
|
|
||||||
})
|
|
||||||
ber.endSequence()
|
|
||||||
|
|
||||||
return ber
|
|
||||||
}
|
|
||||||
|
|
||||||
/// --- Exports
|
|
||||||
|
|
||||||
module.exports = SearchEntry
|
|
|
@ -1,101 +0,0 @@
|
||||||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
|
||||||
|
|
||||||
const assert = require('assert-plus')
|
|
||||||
const util = require('util')
|
|
||||||
|
|
||||||
// var asn1 = require('@ldapjs/asn1')
|
|
||||||
|
|
||||||
const LDAPMessage = require('./message')
|
|
||||||
const Protocol = require('@ldapjs/protocol')
|
|
||||||
const dn = require('../dn')
|
|
||||||
const url = require('../url')
|
|
||||||
|
|
||||||
/// --- Globals
|
|
||||||
|
|
||||||
// var BerWriter = asn1.BerWriter
|
|
||||||
const parseURL = url.parse
|
|
||||||
|
|
||||||
/// --- API
|
|
||||||
|
|
||||||
function SearchReference (options) {
|
|
||||||
options = options || {}
|
|
||||||
assert.object(options)
|
|
||||||
|
|
||||||
options.protocolOp = Protocol.operations.LDAP_RES_SEARCH_REF
|
|
||||||
LDAPMessage.call(this, options)
|
|
||||||
|
|
||||||
this.uris = options.uris || []
|
|
||||||
}
|
|
||||||
util.inherits(SearchReference, LDAPMessage)
|
|
||||||
Object.defineProperties(SearchReference.prototype, {
|
|
||||||
type: {
|
|
||||||
get: function getType () { return 'SearchReference' },
|
|
||||||
configurable: false
|
|
||||||
},
|
|
||||||
_dn: {
|
|
||||||
get: function getDN () { return new dn.DN('') },
|
|
||||||
configurable: false
|
|
||||||
},
|
|
||||||
object: {
|
|
||||||
get: function getObject () {
|
|
||||||
return {
|
|
||||||
dn: this.dn.toString(),
|
|
||||||
uris: this.uris.slice()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
configurable: false
|
|
||||||
},
|
|
||||||
urls: {
|
|
||||||
get: function getUrls () { return this.uris },
|
|
||||||
set: function setUrls (val) {
|
|
||||||
assert.ok(val)
|
|
||||||
assert.ok(Array.isArray(val))
|
|
||||||
this.uris = val.slice()
|
|
||||||
},
|
|
||||||
configurable: false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
SearchReference.prototype.toObject = function () {
|
|
||||||
return this.object
|
|
||||||
}
|
|
||||||
|
|
||||||
SearchReference.prototype.fromObject = function (obj) {
|
|
||||||
if (typeof (obj) !== 'object') { throw new TypeError('object required') }
|
|
||||||
|
|
||||||
this.uris = obj.uris ? obj.uris.slice() : []
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
SearchReference.prototype._json = function (j) {
|
|
||||||
assert.ok(j)
|
|
||||||
j.uris = this.uris.slice()
|
|
||||||
return j
|
|
||||||
}
|
|
||||||
|
|
||||||
SearchReference.prototype._parse = function (ber, length) {
|
|
||||||
assert.ok(ber)
|
|
||||||
|
|
||||||
while (ber.offset < length) {
|
|
||||||
const _url = ber.readString()
|
|
||||||
parseURL(_url)
|
|
||||||
this.uris.push(_url)
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
SearchReference.prototype._toBer = function (ber) {
|
|
||||||
assert.ok(ber)
|
|
||||||
|
|
||||||
this.uris.forEach(function (u) {
|
|
||||||
ber.writeString(u.href || u)
|
|
||||||
})
|
|
||||||
|
|
||||||
return ber
|
|
||||||
}
|
|
||||||
|
|
||||||
/// --- Exports
|
|
||||||
|
|
||||||
module.exports = SearchReference
|
|
|
@ -1,153 +0,0 @@
|
||||||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
|
||||||
|
|
||||||
const assert = require('assert-plus')
|
|
||||||
const util = require('util')
|
|
||||||
|
|
||||||
const asn1 = require('@ldapjs/asn1')
|
|
||||||
|
|
||||||
const LDAPMessage = require('./message')
|
|
||||||
// var LDAPResult = require('./result')
|
|
||||||
const dn = require('../dn')
|
|
||||||
const filters = require('@ldapjs/filter')
|
|
||||||
const Protocol = require('@ldapjs/protocol')
|
|
||||||
|
|
||||||
/// --- Globals
|
|
||||||
|
|
||||||
const Ber = asn1.Ber
|
|
||||||
|
|
||||||
/// --- API
|
|
||||||
|
|
||||||
function SearchRequest (options) {
|
|
||||||
options = options || {}
|
|
||||||
assert.object(options)
|
|
||||||
|
|
||||||
options.protocolOp = Protocol.operations.LDAP_REQ_SEARCH
|
|
||||||
LDAPMessage.call(this, options)
|
|
||||||
|
|
||||||
if (options.baseObject !== undefined) {
|
|
||||||
this.baseObject = options.baseObject
|
|
||||||
} else {
|
|
||||||
this.baseObject = dn.parse('')
|
|
||||||
}
|
|
||||||
this.scope = options.scope || 'base'
|
|
||||||
this.derefAliases = options.derefAliases || Protocol.search.NEVER_DEREF_ALIASES
|
|
||||||
this.sizeLimit = options.sizeLimit || 0
|
|
||||||
this.timeLimit = options.timeLimit || 0
|
|
||||||
this.typesOnly = options.typesOnly || false
|
|
||||||
this.filter = options.filter || null
|
|
||||||
this.attributes = options.attributes ? options.attributes.slice(0) : []
|
|
||||||
}
|
|
||||||
util.inherits(SearchRequest, LDAPMessage)
|
|
||||||
Object.defineProperties(SearchRequest.prototype, {
|
|
||||||
type: {
|
|
||||||
get: function getType () { return 'SearchRequest' },
|
|
||||||
configurable: false
|
|
||||||
},
|
|
||||||
_dn: {
|
|
||||||
get: function getDN () { return this.baseObject },
|
|
||||||
configurable: false
|
|
||||||
},
|
|
||||||
scope: {
|
|
||||||
get: function getScope () {
|
|
||||||
switch (this._scope) {
|
|
||||||
case Protocol.search.SCOPE_BASE_OBJECT: return 'base'
|
|
||||||
case Protocol.search.SCOPE_ONE_LEVEL: return 'one'
|
|
||||||
case Protocol.search.SCOPE_SUBTREE: return 'sub'
|
|
||||||
default:
|
|
||||||
throw new Error(this._scope + ' is an invalid search scope')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
set: function setScope (val) {
|
|
||||||
if (typeof (val) === 'string') {
|
|
||||||
switch (val) {
|
|
||||||
case 'base':
|
|
||||||
this._scope = Protocol.search.SCOPE_BASE_OBJECT
|
|
||||||
break
|
|
||||||
case 'one':
|
|
||||||
this._scope = Protocol.search.SCOPE_ONE_LEVEL
|
|
||||||
break
|
|
||||||
case 'sub':
|
|
||||||
this._scope = Protocol.search.SCOPE_SUBTREE
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
throw new Error(val + ' is an invalid search scope')
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this._scope = val
|
|
||||||
}
|
|
||||||
},
|
|
||||||
configurable: false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
SearchRequest.prototype._parse = function (ber) {
|
|
||||||
assert.ok(ber)
|
|
||||||
|
|
||||||
this.baseObject = ber.readString()
|
|
||||||
this.scope = ber.readEnumeration()
|
|
||||||
this.derefAliases = ber.readEnumeration()
|
|
||||||
this.sizeLimit = ber.readInt()
|
|
||||||
this.timeLimit = ber.readInt()
|
|
||||||
this.typesOnly = ber.readBoolean()
|
|
||||||
|
|
||||||
this.filter = filters.parseBer(ber)
|
|
||||||
|
|
||||||
// look for attributes
|
|
||||||
if (ber.peek() === 0x30) {
|
|
||||||
ber.readSequence()
|
|
||||||
const end = ber.offset + ber.length
|
|
||||||
while (ber.offset < end) { this.attributes.push(ber.readString().toLowerCase()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
SearchRequest.prototype._toBer = function (ber) {
|
|
||||||
assert.ok(ber)
|
|
||||||
|
|
||||||
// Format only with commas, since that is what RFC 4514 mandates.
|
|
||||||
// There's a gotcha here: even though it's called baseObject,
|
|
||||||
// it can be a string or a DN object.
|
|
||||||
const formattedDN = dn.DN.isDN(this.baseObject)
|
|
||||||
? this.baseObject.format({ skipSpace: true })
|
|
||||||
: this.baseObject.toString()
|
|
||||||
ber.writeString(formattedDN)
|
|
||||||
ber.writeEnumeration(this._scope)
|
|
||||||
ber.writeEnumeration(this.derefAliases)
|
|
||||||
ber.writeInt(this.sizeLimit)
|
|
||||||
ber.writeInt(this.timeLimit)
|
|
||||||
ber.writeBoolean(this.typesOnly)
|
|
||||||
|
|
||||||
const f = this.filter || new filters.PresenceFilter({ attribute: 'objectclass' })
|
|
||||||
const filterBer = f.toBer(ber)
|
|
||||||
ber.appendBuffer(filterBer.buffer)
|
|
||||||
|
|
||||||
ber.startSequence(Ber.Sequence | Ber.Constructor)
|
|
||||||
if (this.attributes && this.attributes.length) {
|
|
||||||
this.attributes.forEach(function (a) {
|
|
||||||
ber.writeString(a)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
ber.endSequence()
|
|
||||||
|
|
||||||
return ber
|
|
||||||
}
|
|
||||||
|
|
||||||
SearchRequest.prototype._json = function (j) {
|
|
||||||
assert.ok(j)
|
|
||||||
|
|
||||||
j.baseObject = this.baseObject
|
|
||||||
j.scope = this.scope
|
|
||||||
j.derefAliases = this.derefAliases
|
|
||||||
j.sizeLimit = this.sizeLimit
|
|
||||||
j.timeLimit = this.timeLimit
|
|
||||||
j.typesOnly = this.typesOnly
|
|
||||||
j.filter = this.filter.toString()
|
|
||||||
j.attributes = this.attributes
|
|
||||||
|
|
||||||
return j
|
|
||||||
}
|
|
||||||
|
|
||||||
/// --- Exports
|
|
||||||
|
|
||||||
module.exports = SearchRequest
|
|
|
@ -1,31 +1,31 @@
|
||||||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||||
|
|
||||||
const assert = require('assert-plus')
|
const assert = require('assert-plus')
|
||||||
const util = require('util')
|
|
||||||
|
|
||||||
const LDAPResult = require('./result')
|
const Attribute = require('@ldapjs/attribute')
|
||||||
const SearchEntry = require('./search_entry')
|
const {
|
||||||
const SearchReference = require('./search_reference')
|
SearchResultEntry: SearchEntry,
|
||||||
|
SearchResultReference: SearchReference,
|
||||||
|
SearchResultDone
|
||||||
|
} = require('@ldapjs/messages')
|
||||||
|
|
||||||
const dtrace = require('../dtrace')
|
const parseDN = require('@ldapjs/dn').DN.fromString
|
||||||
const parseDN = require('../dn').parse
|
|
||||||
const parseURL = require('../url').parse
|
|
||||||
const Protocol = require('@ldapjs/protocol')
|
|
||||||
|
|
||||||
/// --- API
|
/// --- API
|
||||||
|
|
||||||
function SearchResponse (options) {
|
class SearchResponse extends SearchResultDone {
|
||||||
options = options || {}
|
attributes
|
||||||
assert.object(options)
|
notAttributes
|
||||||
|
sentEntries
|
||||||
|
|
||||||
options.protocolOp = Protocol.operations.LDAP_RES_SEARCH
|
constructor (options = {}) {
|
||||||
LDAPResult.call(this, options)
|
super(options)
|
||||||
|
|
||||||
this.attributes = options.attributes ? options.attributes.slice() : []
|
this.attributes = options.attributes ? options.attributes.slice() : []
|
||||||
this.notAttributes = []
|
this.notAttributes = []
|
||||||
this.sentEntries = 0
|
this.sentEntries = 0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
util.inherits(SearchResponse, LDAPResult)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows you to send a SearchEntry back to the client.
|
* Allows you to send a SearchEntry back to the client.
|
||||||
|
@ -44,12 +44,16 @@ SearchResponse.prototype.send = function (entry, nofiltering) {
|
||||||
const savedAttrs = {}
|
const savedAttrs = {}
|
||||||
let save = null
|
let save = null
|
||||||
if (entry instanceof SearchEntry || entry instanceof SearchReference) {
|
if (entry instanceof SearchEntry || entry instanceof SearchReference) {
|
||||||
if (!entry.messageID) { entry.messageID = this.messageID }
|
if (!entry.messageId) { entry.messageId = this.messageId }
|
||||||
if (entry.messageID !== this.messageID) { throw new Error('SearchEntry messageID mismatch') }
|
if (entry.messageId !== this.messageId) {
|
||||||
|
throw new Error('SearchEntry messageId mismatch')
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!entry.attributes) { throw new Error('entry.attributes required') }
|
if (!entry.attributes) { throw new Error('entry.attributes required') }
|
||||||
|
|
||||||
const all = (self.attributes.indexOf('*') !== -1)
|
const all = (self.attributes.indexOf('*') !== -1)
|
||||||
|
// Filter attributes in a plain object according to the magic `_` prefix
|
||||||
|
// and presence in `notAttributes`.
|
||||||
Object.keys(entry.attributes).forEach(function (a) {
|
Object.keys(entry.attributes).forEach(function (a) {
|
||||||
const _a = a.toLowerCase()
|
const _a = a.toLowerCase()
|
||||||
if (!nofiltering && _a.length && _a[0] === '_') {
|
if (!nofiltering && _a.length && _a[0] === '_') {
|
||||||
|
@ -69,39 +73,24 @@ SearchResponse.prototype.send = function (entry, nofiltering) {
|
||||||
save = entry
|
save = entry
|
||||||
entry = new SearchEntry({
|
entry = new SearchEntry({
|
||||||
objectName: typeof (save.dn) === 'string' ? parseDN(save.dn) : save.dn,
|
objectName: typeof (save.dn) === 'string' ? parseDN(save.dn) : save.dn,
|
||||||
messageID: self.messageID,
|
messageId: self.messageId,
|
||||||
log: self.log
|
attributes: Attribute.fromObject(entry.attributes)
|
||||||
})
|
})
|
||||||
entry.fromObject(save)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.log.debug('%s: sending: %j', this.connection.ldap.id, entry.json)
|
this.log.debug('%s: sending: %j', this.connection.ldap.id, entry.pojo)
|
||||||
|
|
||||||
this.connection.write(entry.toBer())
|
this.connection.write(entry.toBer().buffer)
|
||||||
this.sentEntries++
|
this.sentEntries++
|
||||||
|
|
||||||
if (self._dtraceOp && self._dtraceId) {
|
|
||||||
dtrace.fire('server-search-entry', function () {
|
|
||||||
const c = self.connection || { ldap: {} }
|
|
||||||
return [
|
|
||||||
self._dtraceId || 0,
|
|
||||||
(c.remoteAddress || ''),
|
|
||||||
c.ldap.bindDN ? c.ldap.bindDN.toString() : '',
|
|
||||||
(self.requestDN ? self.requestDN.toString() : ''),
|
|
||||||
entry.objectName.toString(),
|
|
||||||
entry.attributes.length
|
|
||||||
]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restore attributes
|
// Restore attributes
|
||||||
Object.keys(savedAttrs).forEach(function (k) {
|
Object.keys(savedAttrs).forEach(function (k) {
|
||||||
save.attributes[k] = savedAttrs[k]
|
save.attributes[k] = savedAttrs[k]
|
||||||
})
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.log.warn(e, '%s failure to write message %j',
|
this.log.warn(e, '%s failure to write message %j',
|
||||||
this.connection.ldap.id, this.json)
|
this.connection.ldap.id, this.pojo)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,11 +98,10 @@ SearchResponse.prototype.createSearchEntry = function (object) {
|
||||||
assert.object(object)
|
assert.object(object)
|
||||||
|
|
||||||
const entry = new SearchEntry({
|
const entry = new SearchEntry({
|
||||||
messageID: this.messageID,
|
messageId: this.messageId,
|
||||||
log: this.log,
|
objectName: object.objectName || object.dn,
|
||||||
objectName: object.objectName || object.dn
|
attributes: object.attributes ?? []
|
||||||
})
|
})
|
||||||
entry.fromObject((object.attributes || object))
|
|
||||||
return entry
|
return entry
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,15 +110,10 @@ SearchResponse.prototype.createSearchReference = function (uris) {
|
||||||
|
|
||||||
if (!Array.isArray(uris)) { uris = [uris] }
|
if (!Array.isArray(uris)) { uris = [uris] }
|
||||||
|
|
||||||
for (let i = 0; i < uris.length; i++) {
|
|
||||||
if (typeof (uris[i]) === 'string') { uris[i] = parseURL(uris[i]) }
|
|
||||||
}
|
|
||||||
|
|
||||||
const self = this
|
const self = this
|
||||||
return new SearchReference({
|
return new SearchReference({
|
||||||
messageID: self.messageID,
|
messageId: self.messageId,
|
||||||
log: self.log,
|
uri: uris
|
||||||
uris: uris
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,62 +0,0 @@
|
||||||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
|
||||||
|
|
||||||
const assert = require('assert-plus')
|
|
||||||
const util = require('util')
|
|
||||||
|
|
||||||
const LDAPMessage = require('./message')
|
|
||||||
const dn = require('../dn')
|
|
||||||
const Protocol = require('@ldapjs/protocol')
|
|
||||||
|
|
||||||
/// --- Globals
|
|
||||||
|
|
||||||
const DN = dn.DN
|
|
||||||
const RDN = dn.RDN
|
|
||||||
|
|
||||||
/// --- API
|
|
||||||
|
|
||||||
function UnbindRequest (options) {
|
|
||||||
options = options || {}
|
|
||||||
assert.object(options)
|
|
||||||
|
|
||||||
options.protocolOp = Protocol.operations.LDAP_REQ_UNBIND
|
|
||||||
LDAPMessage.call(this, options)
|
|
||||||
}
|
|
||||||
util.inherits(UnbindRequest, LDAPMessage)
|
|
||||||
Object.defineProperties(UnbindRequest.prototype, {
|
|
||||||
type: {
|
|
||||||
get: function getType () { return 'UnbindRequest' },
|
|
||||||
configurable: false
|
|
||||||
},
|
|
||||||
_dn: {
|
|
||||||
get: function getDN () {
|
|
||||||
if (this.connection) {
|
|
||||||
return this.connection.ldap.bindDN
|
|
||||||
} else {
|
|
||||||
return new DN([new RDN({ cn: 'anonymous' })])
|
|
||||||
}
|
|
||||||
},
|
|
||||||
configurable: false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
UnbindRequest.prototype._parse = function (ber) {
|
|
||||||
assert.ok(ber)
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
UnbindRequest.prototype._toBer = function (ber) {
|
|
||||||
assert.ok(ber)
|
|
||||||
|
|
||||||
return ber
|
|
||||||
}
|
|
||||||
|
|
||||||
UnbindRequest.prototype._json = function (j) {
|
|
||||||
assert.ok(j)
|
|
||||||
|
|
||||||
return j
|
|
||||||
}
|
|
||||||
|
|
||||||
/// --- Exports
|
|
||||||
|
|
||||||
module.exports = UnbindRequest
|
|
|
@ -1,64 +0,0 @@
|
||||||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
|
||||||
|
|
||||||
const assert = require('assert-plus')
|
|
||||||
const util = require('util')
|
|
||||||
|
|
||||||
const dtrace = require('../dtrace')
|
|
||||||
|
|
||||||
const LDAPMessage = require('./result')
|
|
||||||
|
|
||||||
/// --- API
|
|
||||||
// Ok, so there's really no such thing as an unbind 'response', but to make
|
|
||||||
// the framework not suck, I just made this up, and have it stubbed so it's
|
|
||||||
// not such a one-off.
|
|
||||||
|
|
||||||
function UnbindResponse (options) {
|
|
||||||
options = options || {}
|
|
||||||
assert.object(options)
|
|
||||||
|
|
||||||
options.protocolOp = 0
|
|
||||||
LDAPMessage.call(this, options)
|
|
||||||
}
|
|
||||||
util.inherits(UnbindResponse, LDAPMessage)
|
|
||||||
Object.defineProperties(UnbindResponse.prototype, {
|
|
||||||
type: {
|
|
||||||
get: function getType () { return 'UnbindResponse' },
|
|
||||||
configurable: false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Special override that just ends the connection, if present.
|
|
||||||
*
|
|
||||||
* @param {Number} _status completely ignored.
|
|
||||||
*/
|
|
||||||
UnbindResponse.prototype.end = function (_status) {
|
|
||||||
assert.ok(this.connection)
|
|
||||||
|
|
||||||
this.log.trace('%s: unbinding!', this.connection.ldap.id)
|
|
||||||
|
|
||||||
this.connection.end()
|
|
||||||
|
|
||||||
const self = this
|
|
||||||
if (self._dtraceOp && self._dtraceId) {
|
|
||||||
dtrace.fire('server-' + self._dtraceOp + '-done', function () {
|
|
||||||
const c = self.connection || { ldap: {} }
|
|
||||||
return [
|
|
||||||
self._dtraceId || 0,
|
|
||||||
(c.remoteAddress || ''),
|
|
||||||
c.ldap.bindDN ? c.ldap.bindDN.toString() : '',
|
|
||||||
(self.requestDN ? self.requestDN.toString() : ''),
|
|
||||||
0,
|
|
||||||
''
|
|
||||||
]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
UnbindResponse.prototype._json = function (j) {
|
|
||||||
return j
|
|
||||||
}
|
|
||||||
|
|
||||||
/// --- Exports
|
|
||||||
|
|
||||||
module.exports = UnbindResponse
|
|
256
lib/server.js
256
lib/server.js
|
@ -9,30 +9,31 @@ const util = require('util')
|
||||||
// var asn1 = require('@ldapjs/asn1')
|
// var asn1 = require('@ldapjs/asn1')
|
||||||
const VError = require('verror').VError
|
const VError = require('verror').VError
|
||||||
|
|
||||||
const dn = require('./dn')
|
const { DN, RDN } = require('@ldapjs/dn')
|
||||||
const dtrace = require('./dtrace')
|
const dtrace = require('./dtrace')
|
||||||
const errors = require('./errors')
|
const errors = require('./errors')
|
||||||
const Protocol = require('@ldapjs/protocol')
|
const Protocol = require('@ldapjs/protocol')
|
||||||
|
|
||||||
|
const messages = require('@ldapjs/messages')
|
||||||
|
|
||||||
const Parser = require('./messages').Parser
|
const Parser = require('./messages').Parser
|
||||||
const AbandonResponse = require('./messages/abandon_response')
|
const LdapResult = messages.LdapResult
|
||||||
const AddResponse = require('./messages/add_response')
|
const AbandonResponse = messages.AbandonResponse
|
||||||
const BindResponse = require('./messages/bind_response')
|
const AddResponse = messages.AddResponse
|
||||||
const CompareResponse = require('./messages/compare_response')
|
const BindResponse = messages.BindResponse
|
||||||
const DeleteResponse = require('./messages/del_response')
|
const CompareResponse = messages.CompareResponse
|
||||||
const ExtendedResponse = require('./messages/ext_response')
|
const DeleteResponse = messages.DeleteResponse
|
||||||
// var LDAPResult = require('./messages/result')
|
const ExtendedResponse = messages.ExtensionResponse
|
||||||
const ModifyResponse = require('./messages/modify_response')
|
const ModifyResponse = messages.ModifyResponse
|
||||||
const ModifyDNResponse = require('./messages/moddn_response')
|
const ModifyDnResponse = messages.ModifyDnResponse
|
||||||
const SearchRequest = require('./messages/search_request')
|
const SearchRequest = messages.SearchRequest
|
||||||
const SearchResponse = require('./messages/search_response')
|
const SearchResponse = require('./messages/search_response')
|
||||||
const UnbindResponse = require('./messages/unbind_response')
|
|
||||||
|
|
||||||
/// --- Globals
|
/// --- Globals
|
||||||
|
|
||||||
// var Ber = asn1.Ber
|
// var Ber = asn1.Ber
|
||||||
// var BerReader = asn1.BerReader
|
// var BerReader = asn1.BerReader
|
||||||
const DN = dn.DN
|
// const DN = dn.DN
|
||||||
|
|
||||||
// var sprintf = util.format
|
// var sprintf = util.format
|
||||||
|
|
||||||
|
@ -93,13 +94,20 @@ function getResponse (req) {
|
||||||
Response = ModifyResponse
|
Response = ModifyResponse
|
||||||
break
|
break
|
||||||
case Protocol.operations.LDAP_REQ_MODRDN:
|
case Protocol.operations.LDAP_REQ_MODRDN:
|
||||||
Response = ModifyDNResponse
|
Response = ModifyDnResponse
|
||||||
break
|
break
|
||||||
case Protocol.operations.LDAP_REQ_SEARCH:
|
case Protocol.operations.LDAP_REQ_SEARCH:
|
||||||
Response = SearchResponse
|
Response = SearchResponse
|
||||||
break
|
break
|
||||||
case Protocol.operations.LDAP_REQ_UNBIND:
|
case Protocol.operations.LDAP_REQ_UNBIND:
|
||||||
Response = UnbindResponse
|
// TODO: when the server receives an unbind request this made up response object was returned.
|
||||||
|
// Instead, we need to just terminate the connection. ~ jsumners
|
||||||
|
Response = class extends LdapResult {
|
||||||
|
status = 0
|
||||||
|
end () {
|
||||||
|
req.connection.end()
|
||||||
|
}
|
||||||
|
}
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
return null
|
return null
|
||||||
|
@ -107,16 +115,83 @@ function getResponse (req) {
|
||||||
assert.ok(Response)
|
assert.ok(Response)
|
||||||
|
|
||||||
const res = new Response({
|
const res = new Response({
|
||||||
messageID: req.messageID,
|
messageId: req.messageId,
|
||||||
log: req.log,
|
|
||||||
attributes: ((req instanceof SearchRequest) ? req.attributes : undefined)
|
attributes: ((req instanceof SearchRequest) ? req.attributes : undefined)
|
||||||
})
|
})
|
||||||
|
res.log = req.log
|
||||||
res.connection = req.connection
|
res.connection = req.connection
|
||||||
res.logId = req.logId
|
res.logId = req.logId
|
||||||
|
|
||||||
|
if (typeof res.end !== 'function') {
|
||||||
|
// This is a hack to re-add the original tight coupling of the message
|
||||||
|
// objects and the server connection.
|
||||||
|
// TODO: remove this during server refactoring ~ jsumners 2023-02-16
|
||||||
|
switch (res.protocolOp) {
|
||||||
|
case 0: {
|
||||||
|
res.end = abandonResponseEnd
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
case Protocol.operations.LDAP_RES_COMPARE: {
|
||||||
|
res.end = compareResponseEnd
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
default: {
|
||||||
|
res.end = defaultResponseEnd
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response connection end handler for most responses.
|
||||||
|
*
|
||||||
|
* @param {number} status
|
||||||
|
*/
|
||||||
|
function defaultResponseEnd (status) {
|
||||||
|
if (typeof status === 'number') { this.status = status }
|
||||||
|
|
||||||
|
const ber = this.toBer()
|
||||||
|
this.log.debug('%s: sending: %j', this.connection.ldap.id, this.pojo)
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.connection.write(ber.buffer)
|
||||||
|
} catch (error) {
|
||||||
|
this.log.warn(
|
||||||
|
error,
|
||||||
|
'%s failure to write message %j',
|
||||||
|
this.connection.ldap.id,
|
||||||
|
this.pojo
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response connection end handler for ABANDON responses.
|
||||||
|
*/
|
||||||
|
function abandonResponseEnd () {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response connection end handler for COMPARE responses.
|
||||||
|
*
|
||||||
|
* @param {number | boolean} status
|
||||||
|
*/
|
||||||
|
function compareResponseEnd (status) {
|
||||||
|
let result = 0x06
|
||||||
|
if (typeof status === 'boolean') {
|
||||||
|
if (status === false) {
|
||||||
|
result = 0x05
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result = status
|
||||||
|
}
|
||||||
|
return defaultResponseEnd.call(this, result)
|
||||||
|
}
|
||||||
|
|
||||||
function defaultHandler (req, res, next) {
|
function defaultHandler (req, res, next) {
|
||||||
assert.ok(req)
|
assert.ok(req)
|
||||||
assert.ok(res)
|
assert.ok(res)
|
||||||
|
@ -157,69 +232,6 @@ function noExOpHandler (req, res, next) {
|
||||||
return next()
|
return next()
|
||||||
}
|
}
|
||||||
|
|
||||||
function fireDTraceProbe (req, res) {
|
|
||||||
assert.ok(req)
|
|
||||||
|
|
||||||
req._dtraceId = res._dtraceId = dtrace._nextId()
|
|
||||||
const probeArgs = [
|
|
||||||
req._dtraceId,
|
|
||||||
req.connection.remoteAddress || 'localhost',
|
|
||||||
req.connection.ldap.bindDN.toString(),
|
|
||||||
req.dn.toString()
|
|
||||||
]
|
|
||||||
|
|
||||||
let op
|
|
||||||
switch (req.protocolOp) {
|
|
||||||
case Protocol.operations.LDAP_REQ_ABANDON:
|
|
||||||
op = 'abandon'
|
|
||||||
break
|
|
||||||
case Protocol.operations.LDAP_REQ_ADD:
|
|
||||||
op = 'add'
|
|
||||||
probeArgs.push(req.attributes.length)
|
|
||||||
break
|
|
||||||
case Protocol.operations.LDAP_REQ_BIND:
|
|
||||||
op = 'bind'
|
|
||||||
break
|
|
||||||
case Protocol.operations.LDAP_REQ_COMPARE:
|
|
||||||
op = 'compare'
|
|
||||||
probeArgs.push(req.attribute)
|
|
||||||
probeArgs.push(req.value)
|
|
||||||
break
|
|
||||||
case Protocol.operations.LDAP_REQ_DELETE:
|
|
||||||
op = 'delete'
|
|
||||||
break
|
|
||||||
case Protocol.operations.LDAP_REQ_EXTENSION:
|
|
||||||
op = 'exop'
|
|
||||||
probeArgs.push(req.name)
|
|
||||||
probeArgs.push(req.value)
|
|
||||||
break
|
|
||||||
case Protocol.operations.LDAP_REQ_MODIFY:
|
|
||||||
op = 'modify'
|
|
||||||
probeArgs.push(req.changes.length)
|
|
||||||
break
|
|
||||||
case Protocol.operations.LDAP_REQ_MODRDN:
|
|
||||||
op = 'modifydn'
|
|
||||||
probeArgs.push(req.newRdn.toString())
|
|
||||||
probeArgs.push((req.newSuperior ? req.newSuperior.toString() : ''))
|
|
||||||
break
|
|
||||||
case Protocol.operations.LDAP_REQ_SEARCH:
|
|
||||||
op = 'search'
|
|
||||||
probeArgs.push(req.scope)
|
|
||||||
probeArgs.push(req.filter.toString())
|
|
||||||
break
|
|
||||||
case Protocol.operations.LDAP_REQ_UNBIND:
|
|
||||||
op = 'unbind'
|
|
||||||
break
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
res._dtraceOp = op
|
|
||||||
dtrace.fire('server-' + op + '-start', function () {
|
|
||||||
return probeArgs
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// --- API
|
/// --- API
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -261,8 +273,6 @@ function Server (options) {
|
||||||
|
|
||||||
this._chain = []
|
this._chain = []
|
||||||
this.log = options.log
|
this.log = options.log
|
||||||
this.strictDN = (options.strictDN !== undefined) ? options.strictDN : true
|
|
||||||
|
|
||||||
const log = this.log
|
const log = this.log
|
||||||
|
|
||||||
function setupConnection (c) {
|
function setupConnection (c) {
|
||||||
|
@ -277,12 +287,12 @@ function Server (options) {
|
||||||
c.remotePort = c.socket.remotePort
|
c.remotePort = c.socket.remotePort
|
||||||
}
|
}
|
||||||
|
|
||||||
const rdn = new dn.RDN({ cn: 'anonymous' })
|
const rdn = new RDN({ cn: 'anonymous' })
|
||||||
|
|
||||||
c.ldap = {
|
c.ldap = {
|
||||||
id: c.remoteAddress + ':' + c.remotePort,
|
id: c.remoteAddress + ':' + c.remotePort,
|
||||||
config: options,
|
config: options,
|
||||||
_bindDN: new DN([rdn])
|
_bindDN: new DN({ rdns: [rdn] })
|
||||||
}
|
}
|
||||||
c.addListener('timeout', function () {
|
c.addListener('timeout', function () {
|
||||||
log.trace('%s timed out', c.ldap.id)
|
log.trace('%s timed out', c.ldap.id)
|
||||||
|
@ -305,7 +315,9 @@ function Server (options) {
|
||||||
return c.ldap._bindDN
|
return c.ldap._bindDN
|
||||||
})
|
})
|
||||||
c.ldap.__defineSetter__('bindDN', function (val) {
|
c.ldap.__defineSetter__('bindDN', function (val) {
|
||||||
if (!(val instanceof DN)) { throw new TypeError('DN required') }
|
if (Object.prototype.toString.call(val) !== '[object LdapDn]') {
|
||||||
|
throw new TypeError('DN required')
|
||||||
|
}
|
||||||
|
|
||||||
c.ldap._bindDN = val
|
c.ldap._bindDN = val
|
||||||
return val
|
return val
|
||||||
|
@ -327,11 +339,13 @@ function Server (options) {
|
||||||
log: options.log
|
log: options.log
|
||||||
})
|
})
|
||||||
conn.parser.on('message', function (req) {
|
conn.parser.on('message', function (req) {
|
||||||
|
// TODO: this is mutating the `@ldapjs/message` objects.
|
||||||
|
// We should avoid doing that. ~ jsumners 2023-02-16
|
||||||
req.connection = conn
|
req.connection = conn
|
||||||
req.logId = conn.ldap.id + '::' + req.messageID
|
req.logId = conn.ldap.id + '::' + req.messageId
|
||||||
req.startTime = new Date().getTime()
|
req.startTime = new Date().getTime()
|
||||||
|
|
||||||
log.debug('%s: message received: req=%j', conn.ldap.id, req.json)
|
log.debug('%s: message received: req=%j', conn.ldap.id, req.pojo)
|
||||||
|
|
||||||
const res = getResponse(req)
|
const res = getResponse(req)
|
||||||
if (!res) {
|
if (!res) {
|
||||||
|
@ -343,31 +357,47 @@ function Server (options) {
|
||||||
// parse string DNs for routing/etc
|
// parse string DNs for routing/etc
|
||||||
try {
|
try {
|
||||||
switch (req.protocolOp) {
|
switch (req.protocolOp) {
|
||||||
case Protocol.operations.LDAP_REQ_BIND:
|
case Protocol.operations.LDAP_REQ_BIND: {
|
||||||
req.name = dn.parse(req.name)
|
req.name = DN.fromString(req.name)
|
||||||
break
|
break
|
||||||
|
}
|
||||||
|
|
||||||
case Protocol.operations.LDAP_REQ_ADD:
|
case Protocol.operations.LDAP_REQ_ADD:
|
||||||
case Protocol.operations.LDAP_REQ_COMPARE:
|
case Protocol.operations.LDAP_REQ_COMPARE:
|
||||||
case Protocol.operations.LDAP_REQ_DELETE:
|
case Protocol.operations.LDAP_REQ_DELETE: {
|
||||||
req.entry = dn.parse(req.entry)
|
if (typeof req.entry === 'string') {
|
||||||
|
req.entry = DN.fromString(req.entry)
|
||||||
|
} else if (Object.prototype.toString.call(req.entry) !== '[object LdapDn]') {
|
||||||
|
throw Error('invalid entry object for operation')
|
||||||
|
}
|
||||||
break
|
break
|
||||||
case Protocol.operations.LDAP_REQ_MODIFY:
|
}
|
||||||
req.object = dn.parse(req.object)
|
|
||||||
|
case Protocol.operations.LDAP_REQ_MODIFY: {
|
||||||
|
req.object = DN.fromString(req.object)
|
||||||
break
|
break
|
||||||
case Protocol.operations.LDAP_REQ_MODRDN:
|
}
|
||||||
req.entry = dn.parse(req.entry)
|
|
||||||
|
case Protocol.operations.LDAP_REQ_MODRDN: {
|
||||||
|
if (typeof req.entry === 'string') {
|
||||||
|
req.entry = DN.fromString(req.entry)
|
||||||
|
} else if (Object.prototype.toString.call(req.entry) !== '[object LdapDn]') {
|
||||||
|
throw Error('invalid entry object for operation')
|
||||||
|
}
|
||||||
// TODO: handle newRdn/Superior
|
// TODO: handle newRdn/Superior
|
||||||
break
|
break
|
||||||
case Protocol.operations.LDAP_REQ_SEARCH:
|
}
|
||||||
req.baseObject = dn.parse(req.baseObject)
|
|
||||||
|
case Protocol.operations.LDAP_REQ_SEARCH: {
|
||||||
break
|
break
|
||||||
default:
|
}
|
||||||
|
|
||||||
|
default: {
|
||||||
break
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (self.strictDN) {
|
return res.end(errors.LDAP_INVALID_DN_SYNTAX)
|
||||||
return res.end(errors.LDAP_INVALID_DN_SYNTAX)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
res.connection = conn
|
res.connection = conn
|
||||||
|
@ -409,16 +439,16 @@ function Server (options) {
|
||||||
if (req.protocolOp === Protocol.operations.LDAP_REQ_BIND && res.status === 0) {
|
if (req.protocolOp === Protocol.operations.LDAP_REQ_BIND && res.status === 0) {
|
||||||
// 0 length == anonymous bind
|
// 0 length == anonymous bind
|
||||||
if (req.dn.length === 0 && req.credentials === '') {
|
if (req.dn.length === 0 && req.credentials === '') {
|
||||||
conn.ldap.bindDN = new DN([new dn.RDN({ cn: 'anonymous' })])
|
conn.ldap.bindDN = new DN({ rdns: [new RDN({ cn: 'anonymous' })] })
|
||||||
} else {
|
} else {
|
||||||
conn.ldap.bindDN = req.dn
|
conn.ldap.bindDN = DN.fromString(req.dn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// unbind clear bindDN for safety
|
// unbind clear bindDN for safety
|
||||||
// conn should terminate on unbind (RFC4511 4.3)
|
// conn should terminate on unbind (RFC4511 4.3)
|
||||||
if (req.protocolOp === Protocol.operations.LDAP_REQ_UNBIND && res.status === 0) {
|
if (req.protocolOp === Protocol.operations.LDAP_REQ_UNBIND && res.status === 0) {
|
||||||
conn.ldap.bindDN = new DN([new dn.RDN({ cn: 'anonymous' })])
|
conn.ldap.bindDN = new DN({ rdns: [new RDN({ cn: 'anonymous' })] })
|
||||||
}
|
}
|
||||||
|
|
||||||
return after()
|
return after()
|
||||||
|
@ -725,12 +755,10 @@ Server.prototype.getConnections = function (callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Server.prototype._getRoute = function (_dn, backend) {
|
Server.prototype._getRoute = function (_dn, backend) {
|
||||||
assert.ok(dn)
|
|
||||||
|
|
||||||
if (!backend) { backend = this }
|
if (!backend) { backend = this }
|
||||||
|
|
||||||
let name
|
let name
|
||||||
if (_dn instanceof dn.DN) {
|
if (Object.prototype.toString.call(_dn) === '[object LdapDn]') {
|
||||||
name = _dn.toString()
|
name = _dn.toString()
|
||||||
} else {
|
} else {
|
||||||
name = _dn
|
name = _dn
|
||||||
|
@ -757,10 +785,10 @@ Server.prototype._sortedRouteKeys = function _sortedRouteKeys () {
|
||||||
Object.keys(this.routes).forEach(function (key) {
|
Object.keys(this.routes).forEach(function (key) {
|
||||||
const _dn = self.routes[key].dn
|
const _dn = self.routes[key].dn
|
||||||
// Ignore non-DN routes such as exop or unbind
|
// Ignore non-DN routes such as exop or unbind
|
||||||
if (_dn instanceof dn.DN) {
|
if (Object.prototype.toString.call(_dn) === '[object LdapDn]') {
|
||||||
const reversed = _dn.clone()
|
const reversed = _dn.clone()
|
||||||
reversed.rdns.reverse()
|
reversed.reverse()
|
||||||
reversedRDNsToKeys[reversed.format()] = key
|
reversedRDNsToKeys[reversed.toString()] = key
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
const output = []
|
const output = []
|
||||||
|
@ -777,11 +805,9 @@ Server.prototype._sortedRouteKeys = function _sortedRouteKeys () {
|
||||||
return this._routeKeyCache
|
return this._routeKeyCache
|
||||||
}
|
}
|
||||||
|
|
||||||
Server.prototype._getHandlerChain = function _getHandlerChain (req, res) {
|
Server.prototype._getHandlerChain = function _getHandlerChain (req) {
|
||||||
assert.ok(req)
|
assert.ok(req)
|
||||||
|
|
||||||
fireDTraceProbe(req, res)
|
|
||||||
|
|
||||||
const self = this
|
const self = this
|
||||||
const routes = this.routes
|
const routes = this.routes
|
||||||
let route
|
let route
|
||||||
|
@ -837,7 +863,7 @@ Server.prototype._getHandlerChain = function _getHandlerChain (req, res) {
|
||||||
const keys = this._sortedRouteKeys()
|
const keys = this._sortedRouteKeys()
|
||||||
let fallbackHandler = [noSuffixHandler]
|
let fallbackHandler = [noSuffixHandler]
|
||||||
// invalid DNs in non-strict mode are routed to the default handler
|
// invalid DNs in non-strict mode are routed to the default handler
|
||||||
const testDN = (typeof (req.dn) === 'string') ? '' : req.dn
|
const testDN = (typeof (req.dn) === 'string') ? DN.fromString(req.dn) : req.dn
|
||||||
|
|
||||||
for (let i = 0; i < keys.length; i++) {
|
for (let i = 0; i < keys.length; i++) {
|
||||||
const suffix = keys[i]
|
const suffix = keys[i]
|
||||||
|
@ -884,7 +910,7 @@ Server.prototype._mount = function (op, name, argv, notDN) {
|
||||||
backend = argv[0]
|
backend = argv[0]
|
||||||
index = 1
|
index = 1
|
||||||
}
|
}
|
||||||
const route = this._getRoute(notDN ? name : dn.parse(name), backend)
|
const route = this._getRoute(notDN ? name : DN.fromString(name), backend)
|
||||||
|
|
||||||
const chain = this._chain.slice()
|
const chain = this._chain.slice()
|
||||||
argv.slice(index).forEach(function (a) {
|
argv.slice(index).forEach(function (a) {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
const querystring = require('querystring')
|
const querystring = require('querystring')
|
||||||
const url = require('url')
|
const url = require('url')
|
||||||
const dn = require('./dn')
|
const { DN } = require('@ldapjs/dn')
|
||||||
const filter = require('@ldapjs/filter')
|
const filter = require('@ldapjs/filter')
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
@ -38,7 +38,7 @@ module.exports = {
|
||||||
|
|
||||||
if (u.pathname) {
|
if (u.pathname) {
|
||||||
u.pathname = querystring.unescape(u.pathname.substr(1))
|
u.pathname = querystring.unescape(u.pathname.substr(1))
|
||||||
u.DN = parseDN ? dn.parse(u.pathname) : u.pathname
|
u.DN = parseDN ? DN.fromString(u.pathname) : u.pathname
|
||||||
}
|
}
|
||||||
|
|
||||||
if (u.search) {
|
if (u.search) {
|
||||||
|
|
16
package.json
16
package.json
|
@ -10,16 +10,14 @@
|
||||||
"url": "git://github.com/ldapjs/node-ldapjs.git"
|
"url": "git://github.com/ldapjs/node-ldapjs.git"
|
||||||
},
|
},
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"directories": {
|
|
||||||
"lib": "./lib"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10.13.0"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ldapjs/asn1": "1.2.0",
|
"@ldapjs/asn1": "2.0.0-rc.4",
|
||||||
"@ldapjs/controls": "^1.0.0",
|
"@ldapjs/attribute": "1.0.0-rc.5",
|
||||||
"@ldapjs/filter": "1.0.0",
|
"@ldapjs/change": "1.0.0-rc.3",
|
||||||
|
"@ldapjs/controls": "2.0.0-rc.1",
|
||||||
|
"@ldapjs/dn": "1.0.0-rc.1",
|
||||||
|
"@ldapjs/filter": "2.0.0-rc.5",
|
||||||
|
"@ldapjs/messages": "1.0.0-rc.2",
|
||||||
"@ldapjs/protocol": "^1.0.0",
|
"@ldapjs/protocol": "^1.0.0",
|
||||||
"abstract-logging": "^2.0.0",
|
"abstract-logging": "^2.0.0",
|
||||||
"assert-plus": "^1.0.0",
|
"assert-plus": "^1.0.0",
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
parserOptions: {
|
||||||
|
ecmaVersion: 'latest'
|
||||||
|
},
|
||||||
|
|
||||||
rules: {
|
rules: {
|
||||||
'no-shadow': 'off'
|
'no-shadow': 'off'
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,163 +0,0 @@
|
||||||
'use strict'
|
|
||||||
|
|
||||||
const { test } = require('tap')
|
|
||||||
const { BerReader, BerWriter } = require('@ldapjs/asn1')
|
|
||||||
const { Attribute } = require('../lib')
|
|
||||||
|
|
||||||
test('new no args', function (t) {
|
|
||||||
t.ok(new Attribute())
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('new with args', function (t) {
|
|
||||||
let attr = new Attribute({
|
|
||||||
type: 'cn',
|
|
||||||
vals: ['foo', 'bar']
|
|
||||||
})
|
|
||||||
t.ok(attr)
|
|
||||||
attr.addValue('baz')
|
|
||||||
t.equal(attr.type, 'cn')
|
|
||||||
t.equal(attr.vals.length, 3)
|
|
||||||
t.equal(attr.vals[0], 'foo')
|
|
||||||
t.equal(attr.vals[1], 'bar')
|
|
||||||
t.equal(attr.vals[2], 'baz')
|
|
||||||
t.throws(function () {
|
|
||||||
attr = new Attribute('not an object')
|
|
||||||
})
|
|
||||||
t.throws(function () {
|
|
||||||
const typeThatIsNotAString = 1
|
|
||||||
attr = new Attribute({
|
|
||||||
type: typeThatIsNotAString
|
|
||||||
})
|
|
||||||
})
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('toBer', function (t) {
|
|
||||||
const attr = new Attribute({
|
|
||||||
type: 'cn',
|
|
||||||
vals: ['foo', 'bar']
|
|
||||||
})
|
|
||||||
t.ok(attr)
|
|
||||||
const ber = new BerWriter()
|
|
||||||
attr.toBer(ber)
|
|
||||||
const reader = new BerReader(ber.buffer)
|
|
||||||
t.ok(reader.readSequence())
|
|
||||||
t.equal(reader.readString(), 'cn')
|
|
||||||
t.equal(reader.readSequence(), 0x31) // lber set
|
|
||||||
t.equal(reader.readString(), 'foo')
|
|
||||||
t.equal(reader.readString(), 'bar')
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('parse', function (t) {
|
|
||||||
const ber = new BerWriter()
|
|
||||||
ber.startSequence()
|
|
||||||
ber.writeString('cn')
|
|
||||||
ber.startSequence(0x31)
|
|
||||||
ber.writeStringArray(['foo', 'bar'])
|
|
||||||
ber.endSequence()
|
|
||||||
ber.endSequence()
|
|
||||||
|
|
||||||
const attr = new Attribute()
|
|
||||||
t.ok(attr)
|
|
||||||
t.ok(attr.parse(new BerReader(ber.buffer)))
|
|
||||||
|
|
||||||
t.equal(attr.type, 'cn')
|
|
||||||
t.equal(attr.vals.length, 2)
|
|
||||||
t.equal(attr.vals[0], 'foo')
|
|
||||||
t.equal(attr.vals[1], 'bar')
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('parse - without 0x31', function (t) {
|
|
||||||
const ber = new BerWriter()
|
|
||||||
ber.startSequence()
|
|
||||||
ber.writeString('sn')
|
|
||||||
ber.endSequence()
|
|
||||||
|
|
||||||
const attr = new Attribute()
|
|
||||||
t.ok(attr)
|
|
||||||
t.ok(attr.parse(new BerReader(ber.buffer)))
|
|
||||||
|
|
||||||
t.equal(attr.type, 'sn')
|
|
||||||
t.equal(attr.vals.length, 0)
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('toString', function (t) {
|
|
||||||
const attr = new Attribute({
|
|
||||||
type: 'foobar',
|
|
||||||
vals: ['asdf']
|
|
||||||
})
|
|
||||||
const expected = attr.toString()
|
|
||||||
const actual = JSON.stringify(attr.json)
|
|
||||||
t.equal(actual, expected)
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('isAttribute', function (t) {
|
|
||||||
const isA = Attribute.isAttribute
|
|
||||||
t.notOk(isA(null))
|
|
||||||
t.notOk(isA('asdf'))
|
|
||||||
t.ok(isA(new Attribute({
|
|
||||||
type: 'foobar',
|
|
||||||
vals: ['asdf']
|
|
||||||
})))
|
|
||||||
|
|
||||||
t.ok(isA({
|
|
||||||
type: 'foo',
|
|
||||||
vals: ['item', Buffer.alloc(5)],
|
|
||||||
toBer: function () { /* placeholder */ }
|
|
||||||
}))
|
|
||||||
|
|
||||||
// bad type in vals
|
|
||||||
t.notOk(isA({
|
|
||||||
type: 'foo',
|
|
||||||
vals: ['item', null],
|
|
||||||
toBer: function () { /* placeholder */ }
|
|
||||||
}))
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('compare', function (t) {
|
|
||||||
const comp = Attribute.compare
|
|
||||||
const a = new Attribute({
|
|
||||||
type: 'foo',
|
|
||||||
vals: ['bar']
|
|
||||||
})
|
|
||||||
const b = new Attribute({
|
|
||||||
type: 'foo',
|
|
||||||
vals: ['bar']
|
|
||||||
})
|
|
||||||
const notAnAttribute = 'this is not an attribute'
|
|
||||||
|
|
||||||
t.throws(function () {
|
|
||||||
comp(a, notAnAttribute)
|
|
||||||
})
|
|
||||||
t.throws(function () {
|
|
||||||
comp(notAnAttribute, b)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.equal(comp(a, b), 0)
|
|
||||||
|
|
||||||
// Different types
|
|
||||||
a.type = 'boo'
|
|
||||||
t.equal(comp(a, b), -1)
|
|
||||||
t.equal(comp(b, a), 1)
|
|
||||||
a.type = 'foo'
|
|
||||||
|
|
||||||
// Different value counts
|
|
||||||
a.vals = ['bar', 'baz']
|
|
||||||
t.equal(comp(a, b), 1)
|
|
||||||
t.equal(comp(b, a), -1)
|
|
||||||
|
|
||||||
// Different value contents (same count)
|
|
||||||
a.vals = ['baz']
|
|
||||||
t.equal(comp(a, b), 1)
|
|
||||||
t.equal(comp(b, a), -1)
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
|
@ -1,253 +0,0 @@
|
||||||
'use strict'
|
|
||||||
|
|
||||||
const fs = require('fs')
|
|
||||||
const path = require('path')
|
|
||||||
const { test } = require('tap')
|
|
||||||
const { BerReader, BerWriter } = require('@ldapjs/asn1')
|
|
||||||
const { Attribute, Change } = require('../lib')
|
|
||||||
|
|
||||||
test('new no args', function (t) {
|
|
||||||
t.ok(new Change())
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('new with args', function (t) {
|
|
||||||
const change = new Change({
|
|
||||||
operation: 'add',
|
|
||||||
modification: new Attribute({
|
|
||||||
type: 'cn',
|
|
||||||
vals: ['foo', 'bar']
|
|
||||||
})
|
|
||||||
})
|
|
||||||
t.ok(change)
|
|
||||||
|
|
||||||
t.equal(change.operation, 'add')
|
|
||||||
t.equal(change.modification.type, 'cn')
|
|
||||||
t.equal(change.modification.vals.length, 2)
|
|
||||||
t.equal(change.modification.vals[0], 'foo')
|
|
||||||
t.equal(change.modification.vals[1], 'bar')
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('new with args and buffer', function (t) {
|
|
||||||
const img = fs.readFileSync(path.join(__dirname, '/imgs/test.jpg'))
|
|
||||||
|
|
||||||
const change = new Change({
|
|
||||||
operation: 'add',
|
|
||||||
modification: {
|
|
||||||
thumbnailPhoto: img
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.ok(change)
|
|
||||||
|
|
||||||
t.equal(change.operation, 'add')
|
|
||||||
t.equal(change.modification.type, 'thumbnailPhoto')
|
|
||||||
t.equal(change.modification.vals.length, 1)
|
|
||||||
t.equal(change.modification.buffers[0].compare(img), 0)
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('validate fields', function (t) {
|
|
||||||
const c = new Change()
|
|
||||||
t.ok(c)
|
|
||||||
t.throws(function () {
|
|
||||||
c.operation = 'bogus'
|
|
||||||
})
|
|
||||||
t.throws(function () {
|
|
||||||
c.modification = { too: 'many', fields: 'here' }
|
|
||||||
})
|
|
||||||
c.modification = {
|
|
||||||
foo: ['bar', 'baz']
|
|
||||||
}
|
|
||||||
t.ok(c.modification)
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('GH-31 (multiple attributes per Change)', function (t) {
|
|
||||||
t.throws(function () {
|
|
||||||
const c = new Change({
|
|
||||||
operation: 'replace',
|
|
||||||
modification: {
|
|
||||||
cn: 'foo',
|
|
||||||
sn: 'bar'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
t.notOk(c)
|
|
||||||
})
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('toBer', function (t) {
|
|
||||||
const change = new Change({
|
|
||||||
operation: 'Add',
|
|
||||||
modification: new Attribute({
|
|
||||||
type: 'cn',
|
|
||||||
vals: ['foo', 'bar']
|
|
||||||
})
|
|
||||||
})
|
|
||||||
t.ok(change)
|
|
||||||
|
|
||||||
const ber = new BerWriter()
|
|
||||||
change.toBer(ber)
|
|
||||||
const reader = new BerReader(ber.buffer)
|
|
||||||
t.ok(reader.readSequence())
|
|
||||||
t.equal(reader.readEnumeration(), 0x00)
|
|
||||||
t.ok(reader.readSequence())
|
|
||||||
t.equal(reader.readString(), 'cn')
|
|
||||||
t.equal(reader.readSequence(), 0x31) // lber set
|
|
||||||
t.equal(reader.readString(), 'foo')
|
|
||||||
t.equal(reader.readString(), 'bar')
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('parse', function (t) {
|
|
||||||
const ber = new BerWriter()
|
|
||||||
ber.startSequence()
|
|
||||||
ber.writeEnumeration(0x00)
|
|
||||||
ber.startSequence()
|
|
||||||
ber.writeString('cn')
|
|
||||||
ber.startSequence(0x31)
|
|
||||||
ber.writeStringArray(['foo', 'bar'])
|
|
||||||
ber.endSequence()
|
|
||||||
ber.endSequence()
|
|
||||||
ber.endSequence()
|
|
||||||
|
|
||||||
const change = new Change()
|
|
||||||
t.ok(change)
|
|
||||||
t.ok(change.parse(new BerReader(ber.buffer)))
|
|
||||||
|
|
||||||
t.equal(change.operation, 'add')
|
|
||||||
t.equal(change.modification.type, 'cn')
|
|
||||||
t.equal(change.modification.vals.length, 2)
|
|
||||||
t.equal(change.modification.vals[0], 'foo')
|
|
||||||
t.equal(change.modification.vals[1], 'bar')
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('apply - replace', function (t) {
|
|
||||||
let res
|
|
||||||
const single = new Change({
|
|
||||||
operation: 'replace',
|
|
||||||
modification: {
|
|
||||||
type: 'cn',
|
|
||||||
vals: ['new']
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const twin = new Change({
|
|
||||||
operation: 'replace',
|
|
||||||
modification: {
|
|
||||||
type: 'cn',
|
|
||||||
vals: ['new', 'two']
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const empty = new Change({
|
|
||||||
operation: 'replace',
|
|
||||||
modification: {
|
|
||||||
type: 'cn',
|
|
||||||
vals: []
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// plain
|
|
||||||
res = Change.apply(single, { cn: ['old'] })
|
|
||||||
t.same(res.cn, ['new'])
|
|
||||||
|
|
||||||
// multiple
|
|
||||||
res = Change.apply(single, { cn: ['old', 'also'] })
|
|
||||||
t.same(res.cn, ['new'])
|
|
||||||
|
|
||||||
// empty
|
|
||||||
res = Change.apply(empty, { cn: ['existing'] })
|
|
||||||
t.equal(res.cn, undefined)
|
|
||||||
t.ok(Object.keys(res).indexOf('cn') === -1)
|
|
||||||
|
|
||||||
// absent
|
|
||||||
res = Change.apply(single, { dn: ['otherjunk'] })
|
|
||||||
t.same(res.cn, ['new'])
|
|
||||||
|
|
||||||
// scalar formatting "success"
|
|
||||||
res = Change.apply(single, { cn: 'old' }, true)
|
|
||||||
t.equal(res.cn, 'new')
|
|
||||||
|
|
||||||
// scalar formatting "failure"
|
|
||||||
res = Change.apply(twin, { cn: 'old' }, true)
|
|
||||||
t.same(res.cn, ['new', 'two'])
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('apply - add', function (t) {
|
|
||||||
let res
|
|
||||||
const single = new Change({
|
|
||||||
operation: 'add',
|
|
||||||
modification: {
|
|
||||||
type: 'cn',
|
|
||||||
vals: ['new']
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// plain
|
|
||||||
res = Change.apply(single, { cn: ['old'] })
|
|
||||||
t.same(res.cn, ['old', 'new'])
|
|
||||||
|
|
||||||
// multiple
|
|
||||||
res = Change.apply(single, { cn: ['old', 'also'] })
|
|
||||||
t.same(res.cn, ['old', 'also', 'new'])
|
|
||||||
|
|
||||||
// absent
|
|
||||||
res = Change.apply(single, { dn: ['otherjunk'] })
|
|
||||||
t.same(res.cn, ['new'])
|
|
||||||
|
|
||||||
// scalar formatting "success"
|
|
||||||
res = Change.apply(single, { }, true)
|
|
||||||
t.equal(res.cn, 'new')
|
|
||||||
|
|
||||||
// scalar formatting "failure"
|
|
||||||
res = Change.apply(single, { cn: 'old' }, true)
|
|
||||||
t.same(res.cn, ['old', 'new'])
|
|
||||||
|
|
||||||
// duplicate add
|
|
||||||
res = Change.apply(single, { cn: 'new' })
|
|
||||||
t.same(res.cn, ['new'])
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('apply - delete', function (t) {
|
|
||||||
let res
|
|
||||||
const single = new Change({
|
|
||||||
operation: 'delete',
|
|
||||||
modification: {
|
|
||||||
type: 'cn',
|
|
||||||
vals: ['old']
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// plain
|
|
||||||
res = Change.apply(single, { cn: ['old', 'new'] })
|
|
||||||
t.same(res.cn, ['new'])
|
|
||||||
|
|
||||||
// empty
|
|
||||||
res = Change.apply(single, { cn: ['old'] })
|
|
||||||
t.equal(res.cn, undefined)
|
|
||||||
t.ok(Object.keys(res).indexOf('cn') === -1)
|
|
||||||
|
|
||||||
// scalar formatting "success"
|
|
||||||
res = Change.apply(single, { cn: ['old', 'one'] }, true)
|
|
||||||
t.equal(res.cn, 'one')
|
|
||||||
|
|
||||||
// scalar formatting "failure"
|
|
||||||
res = Change.apply(single, { cn: ['old', 'several', 'items'] }, true)
|
|
||||||
t.same(res.cn, ['several', 'items'])
|
|
||||||
|
|
||||||
// absent
|
|
||||||
res = Change.apply(single, { dn: ['otherjunk'] })
|
|
||||||
t.ok(res)
|
|
||||||
t.equal(res.cn, undefined)
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
|
@ -6,8 +6,18 @@ const tap = require('tap')
|
||||||
const vasync = require('vasync')
|
const vasync = require('vasync')
|
||||||
const getPort = require('get-port')
|
const getPort = require('get-port')
|
||||||
const { getSock, uuid } = require('./utils')
|
const { getSock, uuid } = require('./utils')
|
||||||
|
const Attribute = require('@ldapjs/attribute')
|
||||||
|
const Change = require('@ldapjs/change')
|
||||||
|
const messages = require('@ldapjs/messages')
|
||||||
|
const controls = require('@ldapjs/controls')
|
||||||
const ldap = require('../lib')
|
const ldap = require('../lib')
|
||||||
const { Attribute, Change } = ldap
|
|
||||||
|
const {
|
||||||
|
SearchRequest,
|
||||||
|
SearchResultEntry,
|
||||||
|
SearchResultReference,
|
||||||
|
SearchResultDone
|
||||||
|
} = messages
|
||||||
|
|
||||||
const SUFFIX = 'dc=test'
|
const SUFFIX = 'dc=test'
|
||||||
const LDAP_CONNECT_TIMEOUT = process.env.LDAP_CONNECT_TIMEOUT || 0
|
const LDAP_CONNECT_TIMEOUT = process.env.LDAP_CONNECT_TIMEOUT || 0
|
||||||
|
@ -44,7 +54,7 @@ tap.beforeEach((t) => {
|
||||||
|
|
||||||
// LDAP whoami
|
// LDAP whoami
|
||||||
server.exop('1.3.6.1.4.1.4203.1.11.3', function (req, res, next) {
|
server.exop('1.3.6.1.4.1.4203.1.11.3', function (req, res, next) {
|
||||||
res.value = 'u:xxyyz@EXAMPLE.NET'
|
res.responseValue = 'u:xxyyz@EXAMPLE.NET'
|
||||||
res.end()
|
res.end()
|
||||||
return next()
|
return next()
|
||||||
})
|
})
|
||||||
|
@ -88,21 +98,21 @@ tap.beforeEach((t) => {
|
||||||
if (req.dn.equals('cn=ref,' + SUFFIX)) {
|
if (req.dn.equals('cn=ref,' + SUFFIX)) {
|
||||||
res.send(res.createSearchReference('ldap://localhost'))
|
res.send(res.createSearchReference('ldap://localhost'))
|
||||||
} else if (req.dn.equals('cn=bin,' + SUFFIX)) {
|
} else if (req.dn.equals('cn=bin,' + SUFFIX)) {
|
||||||
|
const attributes = []
|
||||||
|
attributes.push(new Attribute({ type: 'foo;binary', values: ['wr0gKyDCvCA9IMK+'] }))
|
||||||
|
attributes.push(new Attribute({ type: 'gb18030', values: [Buffer.from([0xB5, 0xE7, 0xCA, 0xD3, 0xBB, 0xFA])] }))
|
||||||
|
attributes.push(new Attribute({ type: 'objectclass', values: ['binary'] }))
|
||||||
res.send(res.createSearchEntry({
|
res.send(res.createSearchEntry({
|
||||||
objectName: req.dn,
|
objectName: req.dn,
|
||||||
attributes: {
|
attributes
|
||||||
'foo;binary': 'wr0gKyDCvCA9IMK+',
|
|
||||||
gb18030: Buffer.from([0xB5, 0xE7, 0xCA, 0xD3, 0xBB, 0xFA]),
|
|
||||||
objectclass: 'binary'
|
|
||||||
}
|
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
|
const attributes = []
|
||||||
|
attributes.push(new Attribute({ type: 'cn', values: ['unit', 'test'] }))
|
||||||
|
attributes.push(new Attribute({ type: 'SN', values: ['testy'] }))
|
||||||
const e = res.createSearchEntry({
|
const e = res.createSearchEntry({
|
||||||
objectName: req.dn,
|
objectName: req.dn,
|
||||||
attributes: {
|
attributes
|
||||||
cn: ['unit', 'test'],
|
|
||||||
SN: 'testy'
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
res.send(e)
|
res.send(e)
|
||||||
res.send(e)
|
res.send(e)
|
||||||
|
@ -142,13 +152,14 @@ tap.beforeEach((t) => {
|
||||||
end = (end > max || end < min) ? max : end
|
end = (end > max || end < min) ? max : end
|
||||||
let i
|
let i
|
||||||
for (i = start; i < end; i++) {
|
for (i = start; i < end; i++) {
|
||||||
res.send({
|
res.send(new SearchResultEntry({
|
||||||
dn: util.format('o=%d, cn=paged', i),
|
messageId: res.id,
|
||||||
attributes: {
|
entry: `o=${i},cn=paged`,
|
||||||
|
attributes: Attribute.fromObject({
|
||||||
o: [i],
|
o: [i],
|
||||||
objectclass: ['pagedResult']
|
objectclass: ['pagedResult']
|
||||||
}
|
})
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
|
@ -156,39 +167,40 @@ tap.beforeEach((t) => {
|
||||||
let cookie = null
|
let cookie = null
|
||||||
let pageSize = 0
|
let pageSize = 0
|
||||||
req.controls.forEach(function (control) {
|
req.controls.forEach(function (control) {
|
||||||
if (control.type === ldap.PagedResultsControl.OID) {
|
if (control.type === controls.PagedResultsControl.OID) {
|
||||||
pageSize = control.value.size
|
pageSize = control.value.size
|
||||||
cookie = control.value.cookie
|
cookie = control.value.cookie
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
if (cookie && Buffer.isBuffer(cookie)) {
|
if (!cookie || Buffer.isBuffer(cookie) === false) {
|
||||||
// Do simple paging
|
|
||||||
let first = min
|
|
||||||
if (cookie.length !== 0) {
|
|
||||||
first = parseInt(cookie.toString(), 10)
|
|
||||||
}
|
|
||||||
const last = sendResults(first, first + pageSize)
|
|
||||||
|
|
||||||
let resultCookie
|
|
||||||
if (last < max) {
|
|
||||||
resultCookie = Buffer.from(last.toString())
|
|
||||||
} else {
|
|
||||||
resultCookie = Buffer.from('')
|
|
||||||
}
|
|
||||||
res.controls.push(new ldap.PagedResultsControl({
|
|
||||||
value: {
|
|
||||||
size: pageSize, // correctness not required here
|
|
||||||
cookie: resultCookie
|
|
||||||
}
|
|
||||||
}))
|
|
||||||
res.end()
|
|
||||||
next()
|
|
||||||
} else {
|
|
||||||
// don't allow non-paged searches for this test endpoint
|
// don't allow non-paged searches for this test endpoint
|
||||||
next(new ldap.UnwillingToPerformError())
|
next(Error('unwilling to perform'))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Do simple paging
|
||||||
|
let first = min
|
||||||
|
if (cookie.length !== 0) {
|
||||||
|
first = parseInt(cookie.toString(), 10)
|
||||||
|
}
|
||||||
|
const last = sendResults(first, first + pageSize)
|
||||||
|
|
||||||
|
let resultCookie
|
||||||
|
if (last < max) {
|
||||||
|
resultCookie = Buffer.from(last.toString())
|
||||||
|
} else {
|
||||||
|
resultCookie = Buffer.from('')
|
||||||
|
}
|
||||||
|
res.addControl(new controls.PagedResultsControl({
|
||||||
|
value: {
|
||||||
|
size: pageSize, // correctness not required here
|
||||||
|
cookie: resultCookie
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
res.end()
|
||||||
|
next()
|
||||||
})
|
})
|
||||||
|
|
||||||
server.search('cn=sssvlv', function (req, res, next) {
|
server.search('cn=sssvlv', function (req, res, next) {
|
||||||
const min = 0
|
const min = 0
|
||||||
const max = 100
|
const max = 100
|
||||||
|
@ -484,7 +496,7 @@ tap.test('add success', function (t) {
|
||||||
const attrs = [
|
const attrs = [
|
||||||
new Attribute({
|
new Attribute({
|
||||||
type: 'cn',
|
type: 'cn',
|
||||||
vals: ['test']
|
values: ['test']
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
t.context.client.add('cn=add, ' + SUFFIX, attrs, function (err, res) {
|
t.context.client.add('cn=add, ' + SUFFIX, attrs, function (err, res) {
|
||||||
|
@ -510,7 +522,7 @@ tap.test('add success with object', function (t) {
|
||||||
|
|
||||||
tap.test('add buffer', function (t) {
|
tap.test('add buffer', function (t) {
|
||||||
const { BerReader } = require('@ldapjs/asn1')
|
const { BerReader } = require('@ldapjs/asn1')
|
||||||
const dn = `cn=add, ${SUFFIX}`
|
const dn = `cn=add,${SUFFIX}`
|
||||||
const attribute = 'thumbnailPhoto'
|
const attribute = 'thumbnailPhoto'
|
||||||
const binary = 0xa5
|
const binary = 0xa5
|
||||||
const entry = {
|
const entry = {
|
||||||
|
@ -519,7 +531,7 @@ tap.test('add buffer', function (t) {
|
||||||
const write = t.context.client._socket.write
|
const write = t.context.client._socket.write
|
||||||
t.context.client._socket.write = (data, encoding, cb) => {
|
t.context.client._socket.write = (data, encoding, cb) => {
|
||||||
const reader = new BerReader(data)
|
const reader = new BerReader(data)
|
||||||
t.equal(data.byteLength, 49)
|
t.equal(data.byteLength, 48)
|
||||||
t.ok(reader.readSequence())
|
t.ok(reader.readSequence())
|
||||||
t.equal(reader.readInt(), 0x1)
|
t.equal(reader.readInt(), 0x1)
|
||||||
t.equal(reader.readSequence(), 0x68)
|
t.equal(reader.readSequence(), 0x68)
|
||||||
|
@ -621,7 +633,7 @@ tap.test('modify success', function (t) {
|
||||||
type: 'Replace',
|
type: 'Replace',
|
||||||
modification: new Attribute({
|
modification: new Attribute({
|
||||||
type: 'cn',
|
type: 'cn',
|
||||||
vals: ['test']
|
values: ['test']
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
t.context.client.modify('cn=modify, ' + SUFFIX, change, function (err, res) {
|
t.context.client.modify('cn=modify, ' + SUFFIX, change, function (err, res) {
|
||||||
|
@ -632,26 +644,11 @@ tap.test('modify success', function (t) {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
tap.test('modify change plain object success', function (t) {
|
|
||||||
const change = new Change({
|
|
||||||
type: 'Replace',
|
|
||||||
modification: {
|
|
||||||
cn: 'test'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
t.context.client.modify('cn=modify, ' + SUFFIX, change, function (err, res) {
|
|
||||||
t.error(err)
|
|
||||||
t.ok(res)
|
|
||||||
t.equal(res.status, 0)
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
// https://github.com/ldapjs/node-ldapjs/pull/435
|
// https://github.com/ldapjs/node-ldapjs/pull/435
|
||||||
tap.test('can delete attributes', function (t) {
|
tap.test('can delete attributes', function (t) {
|
||||||
const change = new Change({
|
const change = new Change({
|
||||||
type: 'Delete',
|
type: 'Delete',
|
||||||
modification: { cn: null }
|
modification: new Attribute({ type: 'cn', values: [null] })
|
||||||
})
|
})
|
||||||
t.context.client.modify('cn=modify,' + SUFFIX, change, function (err, res) {
|
t.context.client.modify('cn=modify,' + SUFFIX, change, function (err, res) {
|
||||||
t.error(err)
|
t.error(err)
|
||||||
|
@ -667,7 +664,7 @@ tap.test('modify array success', function (t) {
|
||||||
operation: 'Replace',
|
operation: 'Replace',
|
||||||
modification: new Attribute({
|
modification: new Attribute({
|
||||||
type: 'cn',
|
type: 'cn',
|
||||||
vals: ['test']
|
values: ['test']
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
new Change({
|
new Change({
|
||||||
|
@ -685,22 +682,6 @@ tap.test('modify array success', function (t) {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
tap.test('modify change plain object success (GH-31)', function (t) {
|
|
||||||
const change = {
|
|
||||||
type: 'replace',
|
|
||||||
modification: {
|
|
||||||
cn: 'test',
|
|
||||||
sn: 'bar'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t.context.client.modify('cn=modify, ' + SUFFIX, change, function (err, res) {
|
|
||||||
t.error(err)
|
|
||||||
t.ok(res)
|
|
||||||
t.equal(res.status, 0)
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
tap.test('modify DN new RDN only', function (t) {
|
tap.test('modify DN new RDN only', function (t) {
|
||||||
t.context.client.modifyDN('cn=old, ' + SUFFIX, 'cn=new', function (err, res) {
|
t.context.client.modifyDN('cn=old, ' + SUFFIX, 'cn=new', function (err, res) {
|
||||||
t.error(err)
|
t.error(err)
|
||||||
|
@ -729,47 +710,42 @@ tap.test('modify DN excessive length (GH-480)', function (t) {
|
||||||
})
|
})
|
||||||
|
|
||||||
tap.test('modify DN excessive superior length', function (t) {
|
tap.test('modify DN excessive superior length', function (t) {
|
||||||
const { BerReader, BerWriter } = require('@ldapjs/asn1')
|
const { ModifyDnRequest } = messages
|
||||||
const ModifyDNRequest = require('../lib/messages/moddn_request')
|
|
||||||
const ber = new BerWriter()
|
|
||||||
const entry = 'cn=Test User,ou=A Long OU ,ou=Another Long OU ,ou=Another Long OU ,dc=acompany,DC=io'
|
const entry = 'cn=Test User,ou=A Long OU ,ou=Another Long OU ,ou=Another Long OU ,dc=acompany,DC=io'
|
||||||
const newSuperior = 'ou=A New Long OU , ou=Another New Long OU , ou=An OU , dc=acompany, dc=io'
|
const newSuperior = 'ou=A New Long OU , ou=Another New Long OU , ou=An OU , dc=acompany, dc=io'
|
||||||
const newRdn = entry.replace(/(.*?),.*/, '$1')
|
const newRdn = entry.replace(/(.*?),.*/, '$1')
|
||||||
const deleteOldRdn = true
|
const deleteOldRdn = true
|
||||||
const req = new ModifyDNRequest({
|
|
||||||
entry: entry,
|
const req = new ModifyDnRequest({
|
||||||
deleteOldRdn: deleteOldRdn,
|
entry,
|
||||||
controls: []
|
deleteOldRdn,
|
||||||
|
newRdn,
|
||||||
|
newSuperior
|
||||||
})
|
})
|
||||||
req.newRdn = newRdn
|
|
||||||
req.newSuperior = newSuperior
|
t.equal(req.entry.toString(), 'cn=Test User,ou=A Long OU,ou=Another Long OU,ou=Another Long OU,dc=acompany,DC=io')
|
||||||
req._toBer(ber)
|
t.equal(req.newRdn.toString(), 'cn=Test User')
|
||||||
const reader = new BerReader(ber.buffer)
|
t.equal(req.deleteOldRdn, true)
|
||||||
t.equal(reader.readString(), entry)
|
t.equal(req.newSuperior.toString(), 'ou=A New Long OU,ou=Another New Long OU,ou=An OU,dc=acompany,dc=io')
|
||||||
t.equal(reader.readString(), newRdn)
|
|
||||||
t.equal(reader.readBoolean(), deleteOldRdn)
|
|
||||||
t.equal(reader.readByte(), 0x80)
|
|
||||||
reader.readLength()
|
|
||||||
t.equal(reader._len, newSuperior.length)
|
|
||||||
reader._buf[--reader._offset] = 0x4
|
|
||||||
t.equal(reader.readString(), newSuperior)
|
|
||||||
t.end()
|
t.end()
|
||||||
})
|
})
|
||||||
|
|
||||||
tap.test('search basic', function (t) {
|
tap.test('search basic', function (t) {
|
||||||
|
const { SearchResultEntry, SearchResultDone } = messages
|
||||||
|
|
||||||
t.context.client.search('cn=test, ' + SUFFIX, '(objectclass=*)', function (err, res) {
|
t.context.client.search('cn=test, ' + SUFFIX, '(objectclass=*)', function (err, res) {
|
||||||
t.error(err)
|
t.error(err)
|
||||||
t.ok(res)
|
t.ok(res)
|
||||||
let gotEntry = 0
|
let gotEntry = 0
|
||||||
res.on('searchEntry', function (entry) {
|
res.on('searchEntry', function (entry) {
|
||||||
t.ok(entry)
|
t.ok(entry)
|
||||||
t.ok(entry instanceof ldap.SearchEntry)
|
t.ok(entry instanceof SearchResultEntry)
|
||||||
t.equal(entry.dn.toString(), 'cn=test,' + SUFFIX)
|
t.equal(entry.dn.toString(), 'cn=test,' + SUFFIX)
|
||||||
t.ok(entry.attributes)
|
t.ok(entry.attributes)
|
||||||
t.ok(entry.attributes.length)
|
t.ok(entry.attributes.length)
|
||||||
t.equal(entry.attributes[0].type, 'cn')
|
t.equal(entry.attributes[0].type, 'cn')
|
||||||
t.equal(entry.attributes[1].type, 'SN')
|
t.equal(entry.attributes[1].type, 'SN')
|
||||||
t.ok(entry.object)
|
|
||||||
gotEntry++
|
gotEntry++
|
||||||
})
|
})
|
||||||
res.on('error', function (err) {
|
res.on('error', function (err) {
|
||||||
|
@ -777,7 +753,7 @@ tap.test('search basic', function (t) {
|
||||||
})
|
})
|
||||||
res.on('end', function (res) {
|
res.on('end', function (res) {
|
||||||
t.ok(res)
|
t.ok(res)
|
||||||
t.ok(res instanceof ldap.SearchResponse)
|
t.ok(res instanceof SearchResultDone)
|
||||||
t.equal(res.status, 0)
|
t.equal(res.status, 0)
|
||||||
t.equal(gotEntry, 2)
|
t.equal(gotEntry, 2)
|
||||||
t.end()
|
t.end()
|
||||||
|
@ -844,7 +820,7 @@ tap.test('search paged', { timeout: 10000 }, function (t) {
|
||||||
t2.error(err)
|
t2.error(err)
|
||||||
res.on('searchEntry', entryListener)
|
res.on('searchEntry', entryListener)
|
||||||
res.on('searchRequest', (searchRequest) => {
|
res.on('searchRequest', (searchRequest) => {
|
||||||
t2.ok(searchRequest instanceof ldap.SearchRequest)
|
t2.ok(searchRequest instanceof SearchRequest)
|
||||||
if (currentSearchRequest === null) {
|
if (currentSearchRequest === null) {
|
||||||
t2.equal(countPages, 0)
|
t2.equal(countPages, 0)
|
||||||
}
|
}
|
||||||
|
@ -855,7 +831,7 @@ tap.test('search paged', { timeout: 10000 }, function (t) {
|
||||||
res.on('end', function (result) {
|
res.on('end', function (result) {
|
||||||
t2.equal(countEntries, 1000)
|
t2.equal(countEntries, 1000)
|
||||||
t2.equal(countPages, 10)
|
t2.equal(countPages, 10)
|
||||||
t2.equal(result.messageID, currentSearchRequest.messageID)
|
t2.equal(result.messageId, currentSearchRequest.messageId)
|
||||||
t2.end()
|
t2.end()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -871,7 +847,7 @@ tap.test('search paged', { timeout: 10000 }, function (t) {
|
||||||
function pageListener (result) {
|
function pageListener (result) {
|
||||||
countPages += 1
|
countPages += 1
|
||||||
if (countPages < 10) {
|
if (countPages < 10) {
|
||||||
t2.equal(result.messageID, currentSearchRequest.messageID)
|
t2.equal(result.messageId, currentSearchRequest.messageId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -897,7 +873,7 @@ tap.test('search paged', { timeout: 10000 }, function (t) {
|
||||||
countPages++
|
countPages++
|
||||||
// cancel after 9 to verify callback usage
|
// cancel after 9 to verify callback usage
|
||||||
if (countPages === 9) {
|
if (countPages === 9) {
|
||||||
// another page should never be encountered
|
// another page should never be encountered
|
||||||
res.removeListener('page', pageListener)
|
res.removeListener('page', pageListener)
|
||||||
.on('page', t2.fail.bind(null, 'unexpected page'))
|
.on('page', t2.fail.bind(null, 'unexpected page'))
|
||||||
return cb(new Error())
|
return cb(new Error())
|
||||||
|
@ -995,7 +971,12 @@ tap.test('search paged', { timeout: 10000 }, function (t) {
|
||||||
t.end()
|
t.end()
|
||||||
})
|
})
|
||||||
|
|
||||||
tap.test('search - sssvlv', { timeout: 10000 }, function (t) {
|
// We are skipping the ServerSideSorting test because we have skipped
|
||||||
|
// properly implementing the controls in order to get v3 shipped. These
|
||||||
|
// tests should be re-enabled once we have addressed this issue.
|
||||||
|
// ~ jsumners 2023-02-19
|
||||||
|
// TODO: re-enable after adding back SSSR support
|
||||||
|
tap.test('search - sssvlv', { timeout: 10000, skip: true }, function (t) {
|
||||||
t.test('ssv - asc', function (t2) {
|
t.test('ssv - asc', function (t2) {
|
||||||
let preventry = null
|
let preventry = null
|
||||||
const sssrcontrol = new ldap.ServerSideSortingRequestControl(
|
const sssrcontrol = new ldap.ServerSideSortingRequestControl(
|
||||||
|
@ -1025,6 +1006,7 @@ tap.test('search - sssvlv', { timeout: 10000 }, function (t) {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
t.test('ssv - desc', function (t2) {
|
t.test('ssv - desc', function (t2) {
|
||||||
let preventry = null
|
let preventry = null
|
||||||
const sssrcontrol = new ldap.ServerSideSortingRequestControl(
|
const sssrcontrol = new ldap.ServerSideSortingRequestControl(
|
||||||
|
@ -1099,6 +1081,7 @@ tap.test('search - sssvlv', { timeout: 10000 }, function (t) {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
t.test('vlv - last page', { skip: true }, function (t2) {
|
t.test('vlv - last page', { skip: true }, function (t2) {
|
||||||
// This test is disabled.
|
// This test is disabled.
|
||||||
// See https://github.com/ldapjs/node-ldapjs/pull/797#issuecomment-1094132289
|
// See https://github.com/ldapjs/node-ldapjs/pull/797#issuecomment-1094132289
|
||||||
|
@ -1143,6 +1126,7 @@ tap.test('search - sssvlv', { timeout: 10000 }, function (t) {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
t.end()
|
t.end()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -1158,7 +1142,7 @@ tap.test('search referral', function (t) {
|
||||||
res.on('searchReference', function (referral) {
|
res.on('searchReference', function (referral) {
|
||||||
gotReferral = true
|
gotReferral = true
|
||||||
t.ok(referral)
|
t.ok(referral)
|
||||||
t.ok(referral instanceof ldap.SearchReference)
|
t.ok(referral instanceof SearchResultReference)
|
||||||
t.ok(referral.uris)
|
t.ok(referral.uris)
|
||||||
t.ok(referral.uris.length)
|
t.ok(referral.uris.length)
|
||||||
})
|
})
|
||||||
|
@ -1167,7 +1151,7 @@ tap.test('search referral', function (t) {
|
||||||
})
|
})
|
||||||
res.on('end', function (res) {
|
res.on('end', function (res) {
|
||||||
t.ok(res)
|
t.ok(res)
|
||||||
t.ok(res instanceof ldap.SearchResponse)
|
t.ok(res instanceof SearchResultDone)
|
||||||
t.equal(res.status, 0)
|
t.equal(res.status, 0)
|
||||||
t.equal(gotEntry, 0)
|
t.equal(gotEntry, 0)
|
||||||
t.ok(gotReferral)
|
t.ok(gotReferral)
|
||||||
|
@ -1184,14 +1168,13 @@ tap.test('search rootDSE', function (t) {
|
||||||
t.ok(entry)
|
t.ok(entry)
|
||||||
t.equal(entry.dn.toString(), '')
|
t.equal(entry.dn.toString(), '')
|
||||||
t.ok(entry.attributes)
|
t.ok(entry.attributes)
|
||||||
t.ok(entry.object)
|
|
||||||
})
|
})
|
||||||
res.on('error', function (err) {
|
res.on('error', function (err) {
|
||||||
t.fail(err)
|
t.fail(err)
|
||||||
})
|
})
|
||||||
res.on('end', function (res) {
|
res.on('end', function (res) {
|
||||||
t.ok(res)
|
t.ok(res)
|
||||||
t.ok(res instanceof ldap.SearchResponse)
|
t.ok(res instanceof SearchResultDone)
|
||||||
t.equal(res.status, 0)
|
t.equal(res.status, 0)
|
||||||
t.end()
|
t.end()
|
||||||
})
|
})
|
||||||
|
@ -1204,12 +1187,16 @@ tap.test('search empty attribute', function (t) {
|
||||||
t.ok(res)
|
t.ok(res)
|
||||||
let gotEntry = 0
|
let gotEntry = 0
|
||||||
res.on('searchEntry', function (entry) {
|
res.on('searchEntry', function (entry) {
|
||||||
const obj = entry.toObject()
|
const obj = entry.pojo
|
||||||
t.equal('dc=empty', obj.dn)
|
t.equal('dc=empty', obj.objectName)
|
||||||
t.ok(obj.member)
|
|
||||||
t.equal(obj.member.length, 0)
|
const member = entry.attributes[0]
|
||||||
t.ok(obj['member;range=0-1'])
|
t.ok(member)
|
||||||
t.ok(obj['member;range=0-1'].length)
|
t.equal(member.values.length, 0)
|
||||||
|
|
||||||
|
const rangedMember = entry.attributes[1]
|
||||||
|
t.equal(rangedMember.type, 'member;range=0-1')
|
||||||
|
t.equal(rangedMember.values.length, 2)
|
||||||
gotEntry++
|
gotEntry++
|
||||||
})
|
})
|
||||||
res.on('error', function (err) {
|
res.on('error', function (err) {
|
||||||
|
@ -1217,7 +1204,7 @@ tap.test('search empty attribute', function (t) {
|
||||||
})
|
})
|
||||||
res.on('end', function (res) {
|
res.on('end', function (res) {
|
||||||
t.ok(res)
|
t.ok(res)
|
||||||
t.ok(res instanceof ldap.SearchResponse)
|
t.ok(res instanceof SearchResultDone)
|
||||||
t.equal(res.status, 0)
|
t.equal(res.status, 0)
|
||||||
t.equal(gotEntry, 1)
|
t.equal(gotEntry, 1)
|
||||||
t.end()
|
t.end()
|
||||||
|
@ -1234,12 +1221,12 @@ tap.test('GH-21 binary attributes', function (t) {
|
||||||
const expect2 = Buffer.from([0xB5, 0xE7, 0xCA, 0xD3, 0xBB, 0xFA])
|
const expect2 = Buffer.from([0xB5, 0xE7, 0xCA, 0xD3, 0xBB, 0xFA])
|
||||||
res.on('searchEntry', function (entry) {
|
res.on('searchEntry', function (entry) {
|
||||||
t.ok(entry)
|
t.ok(entry)
|
||||||
t.ok(entry instanceof ldap.SearchEntry)
|
t.ok(entry instanceof SearchResultEntry)
|
||||||
t.equal(entry.dn.toString(), 'cn=bin,' + SUFFIX)
|
t.equal(entry.dn.toString(), 'cn=bin,' + SUFFIX)
|
||||||
t.ok(entry.attributes)
|
t.ok(entry.attributes)
|
||||||
t.ok(entry.attributes.length)
|
t.ok(entry.attributes.length)
|
||||||
t.equal(entry.attributes[0].type, 'foo;binary')
|
t.equal(entry.attributes[0].type, 'foo;binary')
|
||||||
t.equal(entry.attributes[0].vals[0], expect.toString('base64'))
|
t.equal(entry.attributes[0].values[0], expect.toString('base64'))
|
||||||
t.equal(entry.attributes[0].buffers[0].toString('base64'),
|
t.equal(entry.attributes[0].buffers[0].toString('base64'),
|
||||||
expect.toString('base64'))
|
expect.toString('base64'))
|
||||||
|
|
||||||
|
@ -1248,7 +1235,6 @@ tap.test('GH-21 binary attributes', function (t) {
|
||||||
t.equal(expect2.length, entry.attributes[1].buffers[0].length)
|
t.equal(expect2.length, entry.attributes[1].buffers[0].length)
|
||||||
for (let i = 0; i < expect2.length; i++) { t.equal(expect2[i], entry.attributes[1].buffers[0][i]) }
|
for (let i = 0; i < expect2.length; i++) { t.equal(expect2[i], entry.attributes[1].buffers[0][i]) }
|
||||||
|
|
||||||
t.ok(entry.object)
|
|
||||||
gotEntry++
|
gotEntry++
|
||||||
})
|
})
|
||||||
res.on('error', function (err) {
|
res.on('error', function (err) {
|
||||||
|
@ -1256,7 +1242,7 @@ tap.test('GH-21 binary attributes', function (t) {
|
||||||
})
|
})
|
||||||
res.on('end', function (res) {
|
res.on('end', function (res) {
|
||||||
t.ok(res)
|
t.ok(res)
|
||||||
t.ok(res instanceof ldap.SearchResponse)
|
t.ok(res instanceof SearchResultDone)
|
||||||
t.equal(res.status, 0)
|
t.equal(res.status, 0)
|
||||||
t.equal(gotEntry, 1)
|
t.equal(gotEntry, 1)
|
||||||
t.end()
|
t.end()
|
||||||
|
@ -1267,7 +1253,7 @@ tap.test('GH-21 binary attributes', function (t) {
|
||||||
tap.test('GH-23 case insensitive attribute filtering', function (t) {
|
tap.test('GH-23 case insensitive attribute filtering', function (t) {
|
||||||
const opts = {
|
const opts = {
|
||||||
filter: '(objectclass=*)',
|
filter: '(objectclass=*)',
|
||||||
attributes: ['Cn']
|
attributes: ['@Cn']
|
||||||
}
|
}
|
||||||
t.context.client.search('cn=test, ' + SUFFIX, opts, function (err, res) {
|
t.context.client.search('cn=test, ' + SUFFIX, opts, function (err, res) {
|
||||||
t.error(err)
|
t.error(err)
|
||||||
|
@ -1275,12 +1261,11 @@ tap.test('GH-23 case insensitive attribute filtering', function (t) {
|
||||||
let gotEntry = 0
|
let gotEntry = 0
|
||||||
res.on('searchEntry', function (entry) {
|
res.on('searchEntry', function (entry) {
|
||||||
t.ok(entry)
|
t.ok(entry)
|
||||||
t.ok(entry instanceof ldap.SearchEntry)
|
t.ok(entry instanceof SearchResultEntry)
|
||||||
t.equal(entry.dn.toString(), 'cn=test,' + SUFFIX)
|
t.equal(entry.dn.toString(), 'cn=test,' + SUFFIX)
|
||||||
t.ok(entry.attributes)
|
t.ok(entry.attributes)
|
||||||
t.ok(entry.attributes.length)
|
t.ok(entry.attributes.length)
|
||||||
t.equal(entry.attributes[0].type, 'cn')
|
t.equal(entry.attributes[0].type, 'cn')
|
||||||
t.ok(entry.object)
|
|
||||||
gotEntry++
|
gotEntry++
|
||||||
})
|
})
|
||||||
res.on('error', function (err) {
|
res.on('error', function (err) {
|
||||||
|
@ -1288,7 +1273,7 @@ tap.test('GH-23 case insensitive attribute filtering', function (t) {
|
||||||
})
|
})
|
||||||
res.on('end', function (res) {
|
res.on('end', function (res) {
|
||||||
t.ok(res)
|
t.ok(res)
|
||||||
t.ok(res instanceof ldap.SearchResponse)
|
t.ok(res instanceof SearchResultDone)
|
||||||
t.equal(res.status, 0)
|
t.equal(res.status, 0)
|
||||||
t.equal(gotEntry, 2)
|
t.equal(gotEntry, 2)
|
||||||
t.end()
|
t.end()
|
||||||
|
@ -1307,13 +1292,12 @@ tap.test('GH-24 attribute selection of *', function (t) {
|
||||||
let gotEntry = 0
|
let gotEntry = 0
|
||||||
res.on('searchEntry', function (entry) {
|
res.on('searchEntry', function (entry) {
|
||||||
t.ok(entry)
|
t.ok(entry)
|
||||||
t.ok(entry instanceof ldap.SearchEntry)
|
t.ok(entry instanceof SearchResultEntry)
|
||||||
t.equal(entry.dn.toString(), 'cn=test,' + SUFFIX)
|
t.equal(entry.dn.toString(), 'cn=test,' + SUFFIX)
|
||||||
t.ok(entry.attributes)
|
t.ok(entry.attributes)
|
||||||
t.ok(entry.attributes.length)
|
t.ok(entry.attributes.length)
|
||||||
t.equal(entry.attributes[0].type, 'cn')
|
t.equal(entry.attributes[0].type, 'cn')
|
||||||
t.equal(entry.attributes[1].type, 'SN')
|
t.equal(entry.attributes[1].type, 'SN')
|
||||||
t.ok(entry.object)
|
|
||||||
gotEntry++
|
gotEntry++
|
||||||
})
|
})
|
||||||
res.on('error', function (err) {
|
res.on('error', function (err) {
|
||||||
|
@ -1321,7 +1305,7 @@ tap.test('GH-24 attribute selection of *', function (t) {
|
||||||
})
|
})
|
||||||
res.on('end', function (res) {
|
res.on('end', function (res) {
|
||||||
t.ok(res)
|
t.ok(res)
|
||||||
t.ok(res instanceof ldap.SearchResponse)
|
t.ok(res instanceof SearchResultDone)
|
||||||
t.equal(res.status, 0)
|
t.equal(res.status, 0)
|
||||||
t.equal(gotEntry, 2)
|
t.equal(gotEntry, 2)
|
||||||
t.end()
|
t.end()
|
||||||
|
@ -1658,7 +1642,7 @@ tap.test('connection timeout', function (t) {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
tap.only('emitError', function (t) {
|
tap.test('emitError', function (t) {
|
||||||
t.test('connectTimeout', function (t) {
|
t.test('connectTimeout', function (t) {
|
||||||
getPort().then(function (unusedPortNumber) {
|
getPort().then(function (unusedPortNumber) {
|
||||||
const client = ldap.createClient({
|
const client = ldap.createClient({
|
||||||
|
|
234
test/dn.test.js
234
test/dn.test.js
|
@ -1,234 +0,0 @@
|
||||||
'use strict'
|
|
||||||
|
|
||||||
const { test } = require('tap')
|
|
||||||
const { dn } = require('../lib')
|
|
||||||
|
|
||||||
test('parse basic', function (t) {
|
|
||||||
const DN_STR = 'cn=mark, ou=people, o=joyent'
|
|
||||||
const name = dn.parse(DN_STR)
|
|
||||||
t.ok(name)
|
|
||||||
t.ok(name.rdns)
|
|
||||||
t.ok(Array.isArray(name.rdns))
|
|
||||||
t.equal(3, name.rdns.length)
|
|
||||||
name.rdns.forEach(function (rdn) {
|
|
||||||
t.equal('object', typeof (rdn))
|
|
||||||
})
|
|
||||||
t.equal(name.toString(), DN_STR)
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('parse escaped', function (t) {
|
|
||||||
const DN_STR = 'cn=m\\,ark, ou=people, o=joyent'
|
|
||||||
const name = dn.parse(DN_STR)
|
|
||||||
t.ok(name)
|
|
||||||
t.ok(name.rdns)
|
|
||||||
t.ok(Array.isArray(name.rdns))
|
|
||||||
t.equal(3, name.rdns.length)
|
|
||||||
name.rdns.forEach(function (rdn) {
|
|
||||||
t.equal('object', typeof (rdn))
|
|
||||||
})
|
|
||||||
t.equal(name.toString(), DN_STR)
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('parse compound', function (t) {
|
|
||||||
const DN_STR = 'cn=mark+sn=cavage, ou=people, o=joyent'
|
|
||||||
const name = dn.parse(DN_STR)
|
|
||||||
t.ok(name)
|
|
||||||
t.ok(name.rdns)
|
|
||||||
t.ok(Array.isArray(name.rdns))
|
|
||||||
t.equal(3, name.rdns.length)
|
|
||||||
name.rdns.forEach(function (rdn) {
|
|
||||||
t.equal('object', typeof (rdn))
|
|
||||||
})
|
|
||||||
t.equal(name.toString(), DN_STR)
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('parse quoted', function (t) {
|
|
||||||
const DN_STR = 'cn="mark+sn=cavage", ou=people, o=joyent'
|
|
||||||
const ESCAPE_STR = 'cn=mark\\+sn\\=cavage, ou=people, o=joyent'
|
|
||||||
const name = dn.parse(DN_STR)
|
|
||||||
t.ok(name)
|
|
||||||
t.ok(name.rdns)
|
|
||||||
t.ok(Array.isArray(name.rdns))
|
|
||||||
t.equal(3, name.rdns.length)
|
|
||||||
name.rdns.forEach(function (rdn) {
|
|
||||||
t.equal('object', typeof (rdn))
|
|
||||||
})
|
|
||||||
t.equal(name.toString(), ESCAPE_STR)
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('equals', function (t) {
|
|
||||||
const dn1 = dn.parse('cn=foo,dc=bar')
|
|
||||||
t.ok(dn1.equals('cn=foo,dc=bar'))
|
|
||||||
t.ok(!dn1.equals('cn=foo1,dc=bar'))
|
|
||||||
t.ok(dn1.equals(dn.parse('cn=foo,dc=bar')))
|
|
||||||
t.ok(!dn1.equals(dn.parse('cn=foo2,dc=bar')))
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('child of', function (t) {
|
|
||||||
const dn1 = dn.parse('cn=foo,dc=bar')
|
|
||||||
t.ok(dn1.childOf('dc=bar'))
|
|
||||||
t.ok(!dn1.childOf('dc=moo'))
|
|
||||||
t.ok(!dn1.childOf('dc=foo'))
|
|
||||||
t.ok(!dn1.childOf('cn=foo,dc=bar'))
|
|
||||||
|
|
||||||
t.ok(dn1.childOf(dn.parse('dc=bar')))
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('parent of', function (t) {
|
|
||||||
const dn1 = dn.parse('cn=foo,dc=bar')
|
|
||||||
t.ok(dn1.parentOf('cn=moo,cn=foo,dc=bar'))
|
|
||||||
t.ok(!dn1.parentOf('cn=moo,cn=bar,dc=foo'))
|
|
||||||
t.ok(!dn1.parentOf('cn=foo,dc=bar'))
|
|
||||||
|
|
||||||
t.ok(dn1.parentOf(dn.parse('cn=moo,cn=foo,dc=bar')))
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('DN parent', function (t) {
|
|
||||||
const _dn = dn.parse('cn=foo,ou=bar')
|
|
||||||
const parent1 = _dn.parent()
|
|
||||||
const parent2 = parent1.parent()
|
|
||||||
t.ok(parent1.equals('ou=bar'))
|
|
||||||
t.ok(parent2.equals(''))
|
|
||||||
t.equal(parent2.parent(), null)
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('empty DNs', function (t) {
|
|
||||||
const _dn = dn.parse('')
|
|
||||||
const _dn2 = dn.parse('cn=foo')
|
|
||||||
t.ok(_dn.isEmpty())
|
|
||||||
t.notOk(_dn2.isEmpty())
|
|
||||||
t.notOk(_dn.equals('cn=foo'))
|
|
||||||
t.notOk(_dn2.equals(''))
|
|
||||||
t.ok(_dn.parentOf('cn=foo'))
|
|
||||||
t.notOk(_dn.childOf('cn=foo'))
|
|
||||||
t.notOk(_dn2.parentOf(''))
|
|
||||||
t.ok(_dn2.childOf(''))
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('case insensitive attribute names', function (t) {
|
|
||||||
const dn1 = dn.parse('CN=foo,dc=bar')
|
|
||||||
t.ok(dn1.equals('cn=foo,dc=bar'))
|
|
||||||
t.ok(dn1.equals(dn.parse('cn=foo,DC=bar')))
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('format', function (t) {
|
|
||||||
const DN_ORDER = dn.parse('sn=bar+cn=foo,ou=test')
|
|
||||||
const DN_QUOTE = dn.parse('cn="foo",ou=test')
|
|
||||||
const DN_QUOTE2 = dn.parse('cn=" foo",ou=test')
|
|
||||||
const DN_SPACE = dn.parse('cn=foo,ou=test')
|
|
||||||
const DN_SPACE2 = dn.parse('cn=foo ,ou=test')
|
|
||||||
const DN_CASE = dn.parse('CN=foo,Ou=test')
|
|
||||||
|
|
||||||
t.equal(DN_ORDER.format({ keepOrder: false }), 'cn=foo+sn=bar, ou=test')
|
|
||||||
t.equal(DN_ORDER.format({ keepOrder: true }), 'sn=bar+cn=foo, ou=test')
|
|
||||||
|
|
||||||
t.equal(DN_QUOTE.format({ keepQuote: false }), 'cn=foo, ou=test')
|
|
||||||
t.equal(DN_QUOTE.format({ keepQuote: true }), 'cn="foo", ou=test')
|
|
||||||
t.equal(DN_QUOTE2.format({ keepQuote: false }), 'cn=" foo", ou=test')
|
|
||||||
t.equal(DN_QUOTE2.format({ keepQuote: true }), 'cn=" foo", ou=test')
|
|
||||||
|
|
||||||
t.equal(DN_SPACE.format({ keepSpace: false }), 'cn=foo, ou=test')
|
|
||||||
t.equal(DN_SPACE.format({ keepSpace: true }), 'cn=foo,ou=test')
|
|
||||||
t.equal(DN_SPACE.format({ skipSpace: true }), 'cn=foo,ou=test')
|
|
||||||
t.equal(DN_SPACE2.format({ keepSpace: false }), 'cn=foo, ou=test')
|
|
||||||
t.equal(DN_SPACE2.format({ keepSpace: true }), 'cn=foo ,ou=test')
|
|
||||||
t.equal(DN_SPACE2.format({ skipSpace: true }), 'cn=foo,ou=test')
|
|
||||||
|
|
||||||
t.equal(DN_CASE.format({ keepCase: false }), 'cn=foo, ou=test')
|
|
||||||
t.equal(DN_CASE.format({ keepCase: true }), 'CN=foo, Ou=test')
|
|
||||||
t.equal(DN_CASE.format({ upperName: true }), 'CN=foo, OU=test')
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('set format', function (t) {
|
|
||||||
const _dn = dn.parse('uid="user", sn=bar+cn=foo, dc=test , DC=com')
|
|
||||||
t.equal(_dn.toString(), 'uid=user, cn=foo+sn=bar, dc=test, dc=com')
|
|
||||||
_dn.setFormat({ keepOrder: true })
|
|
||||||
t.equal(_dn.toString(), 'uid=user, sn=bar+cn=foo, dc=test, dc=com')
|
|
||||||
_dn.setFormat({ keepQuote: true })
|
|
||||||
t.equal(_dn.toString(), 'uid="user", cn=foo+sn=bar, dc=test, dc=com')
|
|
||||||
_dn.setFormat({ keepSpace: true })
|
|
||||||
t.equal(_dn.toString(), 'uid=user, cn=foo+sn=bar, dc=test , dc=com')
|
|
||||||
_dn.setFormat({ keepCase: true })
|
|
||||||
t.equal(_dn.toString(), 'uid=user, cn=foo+sn=bar, dc=test, DC=com')
|
|
||||||
_dn.setFormat({ upperName: true })
|
|
||||||
t.equal(_dn.toString(), 'UID=user, CN=foo+SN=bar, DC=test, DC=com')
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('format persists across clone', function (t) {
|
|
||||||
const _dn = dn.parse('uid="user", sn=bar+cn=foo, dc=test , DC=com')
|
|
||||||
const OUT = 'UID="user", CN=foo+SN=bar, DC=test, DC=com'
|
|
||||||
_dn.setFormat({ keepQuote: true, upperName: true })
|
|
||||||
const clone = _dn.clone()
|
|
||||||
t.equal(_dn.toString(), OUT)
|
|
||||||
t.equal(clone.toString(), OUT)
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('initialization', function (t) {
|
|
||||||
const dn1 = new dn.DN()
|
|
||||||
t.ok(dn1)
|
|
||||||
t.equal(dn1.toString(), '')
|
|
||||||
t.ok(dn1.isEmpty(), 'DN with no initializer defaults to null DN')
|
|
||||||
|
|
||||||
const data = [
|
|
||||||
new dn.RDN({ foo: 'bar' }),
|
|
||||||
new dn.RDN({ o: 'base' })
|
|
||||||
]
|
|
||||||
const dn2 = new dn.DN(data)
|
|
||||||
t.ok(dn2)
|
|
||||||
t.equal(dn2.toString(), 'foo=bar, o=base')
|
|
||||||
t.ok(!dn2.isEmpty())
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('array functions', function (t) {
|
|
||||||
const dn1 = dn.parse('a=foo, b=bar, c=baz')
|
|
||||||
t.ok(dn1)
|
|
||||||
t.equal(dn1.toString(), 'a=foo, b=bar, c=baz')
|
|
||||||
|
|
||||||
t.ok(dn1.reverse())
|
|
||||||
t.equal(dn1.toString(), 'c=baz, b=bar, a=foo')
|
|
||||||
|
|
||||||
let rdn = dn1.pop()
|
|
||||||
t.ok(rdn)
|
|
||||||
t.equal(dn1.toString(), 'c=baz, b=bar')
|
|
||||||
|
|
||||||
t.ok(dn1.push(rdn))
|
|
||||||
t.equal(dn1.toString(), 'c=baz, b=bar, a=foo')
|
|
||||||
|
|
||||||
rdn = dn1.shift()
|
|
||||||
t.ok(rdn)
|
|
||||||
t.equal(dn1.toString(), 'b=bar, a=foo')
|
|
||||||
|
|
||||||
t.ok(dn1.unshift(rdn))
|
|
||||||
t.equal(dn1.toString(), 'c=baz, b=bar, a=foo')
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('isDN duck-testing', function (t) {
|
|
||||||
const valid = dn.parse('cn=foo')
|
|
||||||
const isDN = dn.DN.isDN
|
|
||||||
t.notOk(isDN(null))
|
|
||||||
t.notOk(isDN('cn=foo'))
|
|
||||||
t.ok(isDN(valid))
|
|
||||||
const duck = {
|
|
||||||
rdns: [{ look: 'ma' }, { a: 'dn' }],
|
|
||||||
toString: function () { return 'look=ma, a=dn' }
|
|
||||||
}
|
|
||||||
t.ok(isDN(duck))
|
|
||||||
t.end()
|
|
||||||
})
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
const tap = require('tap')
|
const tap = require('tap')
|
||||||
const { getSock, uuid } = require('./utils')
|
const { getSock, uuid } = require('./utils')
|
||||||
|
const { SearchResultEntry } = require('@ldapjs/messages')
|
||||||
|
const Attribute = require('@ldapjs/attribute')
|
||||||
const ldap = require('../lib')
|
const ldap = require('../lib')
|
||||||
|
|
||||||
function search (t, options, callback) {
|
function search (t, options, callback) {
|
||||||
|
@ -40,18 +42,20 @@ tap.beforeEach((t) => {
|
||||||
})
|
})
|
||||||
|
|
||||||
server.search(suffix, function (req, res) {
|
server.search(suffix, function (req, res) {
|
||||||
const entry = {
|
const entry = new SearchResultEntry({
|
||||||
dn: 'cn=foo, ' + suffix,
|
entry: 'cn=foo,' + suffix,
|
||||||
attributes: {
|
attributes: Attribute.fromObject({
|
||||||
objectclass: ['person', 'top'],
|
objectclass: ['person', 'top'],
|
||||||
cn: 'Pogo Stick',
|
cn: 'Pogo Stick',
|
||||||
sn: 'Stick',
|
sn: 'Stick',
|
||||||
givenname: 'ogo',
|
givenname: 'ogo',
|
||||||
mail: uuid() + '@pogostick.org'
|
mail: uuid() + '@pogostick.org'
|
||||||
}
|
})
|
||||||
}
|
})
|
||||||
|
|
||||||
if (req.filter.matches(entry.attributes)) { res.send(entry) }
|
if (req.filter.matches(entry.attributes)) {
|
||||||
|
res.send(entry)
|
||||||
|
}
|
||||||
|
|
||||||
res.end()
|
res.end()
|
||||||
})
|
})
|
||||||
|
@ -87,7 +91,7 @@ tap.afterEach((t) => {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
tap.test('Evolution search filter (GH-3)', { only: true }, function (t) {
|
tap.test('Evolution search filter (GH-3)', function (t) {
|
||||||
// This is what Evolution sends, when searching for a contact 'ogo'. Wow.
|
// This is what Evolution sends, when searching for a contact 'ogo'. Wow.
|
||||||
const filter =
|
const filter =
|
||||||
'(|(cn=ogo*)(givenname=ogo*)(sn=ogo*)(mail=ogo*)(member=ogo*)' +
|
'(|(cn=ogo*)(givenname=ogo*)(sn=ogo*)(mail=ogo*)(member=ogo*)' +
|
||||||
|
@ -114,7 +118,7 @@ tap.test('GH-49 Client errors on bad attributes', function (t) {
|
||||||
const searchOpts = {
|
const searchOpts = {
|
||||||
filter: 'cn=*ogo*',
|
filter: 'cn=*ogo*',
|
||||||
scope: 'one',
|
scope: 'one',
|
||||||
attributes: 'dn'
|
attributes: '@dn'
|
||||||
}
|
}
|
||||||
return search(t, searchOpts)
|
return search(t, searchOpts)
|
||||||
})
|
})
|
||||||
|
|
|
@ -109,7 +109,7 @@ tap.test('#fetch', t => {
|
||||||
t.test('returns handler for fetched message', async t => {
|
t.test('returns handler for fetched message', async t => {
|
||||||
const tracker = messageTrackerFactory({ id: 'foo', parser: {} })
|
const tracker = messageTrackerFactory({ id: 'foo', parser: {} })
|
||||||
tracker.track({}, handler)
|
tracker.track({}, handler)
|
||||||
const fetched = tracker.fetch(1)
|
const { callback: fetched } = tracker.fetch(1)
|
||||||
t.equal(fetched, handler)
|
t.equal(fetched, handler)
|
||||||
|
|
||||||
function handler () {}
|
function handler () {}
|
||||||
|
@ -120,7 +120,7 @@ tap.test('#fetch', t => {
|
||||||
tracker.track({}, handler)
|
tracker.track({}, handler)
|
||||||
tracker.track({ abandon: 'message' }, () => {})
|
tracker.track({ abandon: 'message' }, () => {})
|
||||||
tracker.abandon(1)
|
tracker.abandon(1)
|
||||||
const fetched = tracker.fetch(1)
|
const { callback: fetched } = tracker.fetch(1)
|
||||||
t.equal(fetched, handler)
|
t.equal(fetched, handler)
|
||||||
|
|
||||||
function handler () {}
|
function handler () {}
|
||||||
|
@ -185,13 +185,13 @@ tap.test('#remove', t => {
|
||||||
})
|
})
|
||||||
|
|
||||||
tap.test('#track', t => {
|
tap.test('#track', t => {
|
||||||
t.test('add messageID and tracks message', async t => {
|
t.test('add messageId and tracks message', async t => {
|
||||||
const tracker = messageTrackerFactory({ id: 'foo', parser: {} })
|
const tracker = messageTrackerFactory({ id: 'foo', parser: {} })
|
||||||
const msg = {}
|
const msg = {}
|
||||||
tracker.track(msg, handler)
|
tracker.track(msg, handler)
|
||||||
|
|
||||||
t.same(msg, { messageID: 1 })
|
t.same(msg, { messageId: 1 })
|
||||||
const cb = tracker.fetch(1)
|
const { callback: cb } = tracker.fetch(1)
|
||||||
t.equal(cb, handler)
|
t.equal(cb, handler)
|
||||||
|
|
||||||
function handler () {}
|
function handler () {}
|
||||||
|
|
|
@ -1,118 +0,0 @@
|
||||||
'use strict'
|
|
||||||
|
|
||||||
const { test } = require('tap')
|
|
||||||
const { BerReader, BerWriter } = require('@ldapjs/asn1')
|
|
||||||
const { AddRequest, Attribute, dn } = require('../../lib')
|
|
||||||
|
|
||||||
test('new no args', t => {
|
|
||||||
t.ok(new AddRequest())
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('new with args', t => {
|
|
||||||
const req = new AddRequest({
|
|
||||||
entry: dn.parse('cn=foo, o=test'),
|
|
||||||
attributes: [new Attribute({ type: 'cn', vals: ['foo'] }),
|
|
||||||
new Attribute({ type: 'objectclass', vals: ['person'] })]
|
|
||||||
})
|
|
||||||
t.ok(req)
|
|
||||||
t.equal(req.dn.toString(), 'cn=foo, o=test')
|
|
||||||
t.equal(req.attributes.length, 2)
|
|
||||||
t.equal(req.attributes[0].type, 'cn')
|
|
||||||
t.equal(req.attributes[0].vals[0], 'foo')
|
|
||||||
t.equal(req.attributes[1].type, 'objectclass')
|
|
||||||
t.equal(req.attributes[1].vals[0], 'person')
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('parse', t => {
|
|
||||||
const ber = new BerWriter()
|
|
||||||
ber.writeString('cn=foo, o=test')
|
|
||||||
|
|
||||||
ber.startSequence()
|
|
||||||
|
|
||||||
ber.startSequence()
|
|
||||||
ber.writeString('cn')
|
|
||||||
ber.startSequence(0x31)
|
|
||||||
ber.writeString('foo')
|
|
||||||
ber.endSequence()
|
|
||||||
ber.endSequence()
|
|
||||||
|
|
||||||
ber.startSequence()
|
|
||||||
ber.writeString('objectclass')
|
|
||||||
ber.startSequence(0x31)
|
|
||||||
ber.writeString('person')
|
|
||||||
ber.endSequence()
|
|
||||||
ber.endSequence()
|
|
||||||
|
|
||||||
ber.endSequence()
|
|
||||||
|
|
||||||
const req = new AddRequest()
|
|
||||||
t.ok(req._parse(new BerReader(ber.buffer)))
|
|
||||||
t.equal(req.dn.toString(), 'cn=foo, o=test')
|
|
||||||
t.equal(req.attributes.length, 2)
|
|
||||||
t.equal(req.attributes[0].type, 'cn')
|
|
||||||
t.equal(req.attributes[0].vals[0], 'foo')
|
|
||||||
t.equal(req.attributes[1].type, 'objectclass')
|
|
||||||
t.equal(req.attributes[1].vals[0], 'person')
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('toBer', t => {
|
|
||||||
const req = new AddRequest({
|
|
||||||
messageID: 123,
|
|
||||||
entry: dn.parse('cn=foo, o=test'),
|
|
||||||
attributes: [new Attribute({ type: 'cn', vals: ['foo'] }),
|
|
||||||
new Attribute({ type: 'objectclass', vals: ['person'] })]
|
|
||||||
})
|
|
||||||
|
|
||||||
t.ok(req)
|
|
||||||
|
|
||||||
const ber = new BerReader(req.toBer())
|
|
||||||
t.ok(ber)
|
|
||||||
t.equal(ber.readSequence(), 0x30)
|
|
||||||
t.equal(ber.readInt(), 123)
|
|
||||||
t.equal(ber.readSequence(), 0x68)
|
|
||||||
t.equal(ber.readString(), 'cn=foo, o=test')
|
|
||||||
t.ok(ber.readSequence())
|
|
||||||
|
|
||||||
t.ok(ber.readSequence())
|
|
||||||
t.equal(ber.readString(), 'cn')
|
|
||||||
t.equal(ber.readSequence(), 0x31)
|
|
||||||
t.equal(ber.readString(), 'foo')
|
|
||||||
|
|
||||||
t.ok(ber.readSequence())
|
|
||||||
t.equal(ber.readString(), 'objectclass')
|
|
||||||
t.equal(ber.readSequence(), 0x31)
|
|
||||||
t.equal(ber.readString(), 'person')
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('toObject', t => {
|
|
||||||
const req = new AddRequest({
|
|
||||||
entry: dn.parse('cn=foo, o=test'),
|
|
||||||
attributes: [new Attribute({ type: 'cn', vals: ['foo', 'bar'] }),
|
|
||||||
new Attribute({ type: 'objectclass', vals: ['person'] })]
|
|
||||||
})
|
|
||||||
|
|
||||||
t.ok(req)
|
|
||||||
|
|
||||||
const obj = req.toObject()
|
|
||||||
t.ok(obj)
|
|
||||||
|
|
||||||
t.ok(obj.dn)
|
|
||||||
t.equal(obj.dn, 'cn=foo, o=test')
|
|
||||||
t.ok(obj.attributes)
|
|
||||||
t.ok(obj.attributes.cn)
|
|
||||||
t.ok(Array.isArray(obj.attributes.cn))
|
|
||||||
t.equal(obj.attributes.cn.length, 2)
|
|
||||||
t.equal(obj.attributes.cn[0], 'foo')
|
|
||||||
t.equal(obj.attributes.cn[1], 'bar')
|
|
||||||
t.ok(obj.attributes.objectclass)
|
|
||||||
t.ok(Array.isArray(obj.attributes.objectclass))
|
|
||||||
t.equal(obj.attributes.objectclass.length, 1)
|
|
||||||
t.equal(obj.attributes.objectclass[0], 'person')
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
|
@ -1,56 +0,0 @@
|
||||||
'use strict'
|
|
||||||
|
|
||||||
const { test } = require('tap')
|
|
||||||
const { BerReader, BerWriter } = require('@ldapjs/asn1')
|
|
||||||
const { AddResponse } = require('../../lib')
|
|
||||||
|
|
||||||
test('new no args', function (t) {
|
|
||||||
t.ok(new AddResponse())
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('new with args', function (t) {
|
|
||||||
const res = new AddResponse({
|
|
||||||
messageID: 123,
|
|
||||||
status: 0
|
|
||||||
})
|
|
||||||
t.ok(res)
|
|
||||||
t.equal(res.messageID, 123)
|
|
||||||
t.equal(res.status, 0)
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('parse', function (t) {
|
|
||||||
const ber = new BerWriter()
|
|
||||||
ber.writeEnumeration(0)
|
|
||||||
ber.writeString('cn=root')
|
|
||||||
ber.writeString('foo')
|
|
||||||
|
|
||||||
const res = new AddResponse()
|
|
||||||
t.ok(res._parse(new BerReader(ber.buffer)))
|
|
||||||
t.equal(res.status, 0)
|
|
||||||
t.equal(res.matchedDN, 'cn=root')
|
|
||||||
t.equal(res.errorMessage, 'foo')
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('toBer', function (t) {
|
|
||||||
const res = new AddResponse({
|
|
||||||
messageID: 123,
|
|
||||||
status: 3,
|
|
||||||
matchedDN: 'cn=root',
|
|
||||||
errorMessage: 'foo'
|
|
||||||
})
|
|
||||||
t.ok(res)
|
|
||||||
|
|
||||||
const ber = new BerReader(res.toBer())
|
|
||||||
t.ok(ber)
|
|
||||||
t.equal(ber.readSequence(), 0x30)
|
|
||||||
t.equal(ber.readInt(), 123)
|
|
||||||
t.equal(ber.readSequence(), 0x69)
|
|
||||||
t.equal(ber.readEnumeration(), 3)
|
|
||||||
t.equal(ber.readString(), 'cn=root')
|
|
||||||
t.equal(ber.readString(), 'foo')
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
|
@ -1,58 +0,0 @@
|
||||||
'use strict'
|
|
||||||
|
|
||||||
const { test } = require('tap')
|
|
||||||
const { BerReader, BerWriter } = require('@ldapjs/asn1')
|
|
||||||
const { BindRequest, dn } = require('../../lib')
|
|
||||||
|
|
||||||
test('new no args', function (t) {
|
|
||||||
t.ok(new BindRequest())
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('new with args', function (t) {
|
|
||||||
const req = new BindRequest({
|
|
||||||
version: 3,
|
|
||||||
name: dn.parse('cn=root'),
|
|
||||||
credentials: 'secret'
|
|
||||||
})
|
|
||||||
t.ok(req)
|
|
||||||
t.equal(req.version, 3)
|
|
||||||
t.equal(req.name.toString(), 'cn=root')
|
|
||||||
t.equal(req.credentials, 'secret')
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('parse', function (t) {
|
|
||||||
const ber = new BerWriter()
|
|
||||||
ber.writeInt(3)
|
|
||||||
ber.writeString('cn=root')
|
|
||||||
ber.writeString('secret', 0x80)
|
|
||||||
|
|
||||||
const req = new BindRequest()
|
|
||||||
t.ok(req._parse(new BerReader(ber.buffer)))
|
|
||||||
t.equal(req.version, 3)
|
|
||||||
t.equal(req.dn.toString(), 'cn=root')
|
|
||||||
t.equal(req.credentials, 'secret')
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('toBer', function (t) {
|
|
||||||
const req = new BindRequest({
|
|
||||||
messageID: 123,
|
|
||||||
version: 3,
|
|
||||||
name: dn.parse('cn=root'),
|
|
||||||
credentials: 'secret'
|
|
||||||
})
|
|
||||||
t.ok(req)
|
|
||||||
|
|
||||||
const ber = new BerReader(req.toBer())
|
|
||||||
t.ok(ber)
|
|
||||||
t.equal(ber.readSequence(), 0x30)
|
|
||||||
t.equal(ber.readInt(), 123)
|
|
||||||
t.equal(ber.readSequence(), 0x60)
|
|
||||||
t.equal(ber.readInt(), 0x03)
|
|
||||||
t.equal(ber.readString(), 'cn=root')
|
|
||||||
t.equal(ber.readString(0x80), 'secret')
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
|
@ -1,56 +0,0 @@
|
||||||
'use strict'
|
|
||||||
|
|
||||||
const { test } = require('tap')
|
|
||||||
const { BerReader, BerWriter } = require('@ldapjs/asn1')
|
|
||||||
const { BindResponse } = require('../../lib')
|
|
||||||
|
|
||||||
test('new no args', function (t) {
|
|
||||||
t.ok(new BindResponse())
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('new with args', function (t) {
|
|
||||||
const res = new BindResponse({
|
|
||||||
messageID: 123,
|
|
||||||
status: 0
|
|
||||||
})
|
|
||||||
t.ok(res)
|
|
||||||
t.equal(res.messageID, 123)
|
|
||||||
t.equal(res.status, 0)
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('parse', function (t) {
|
|
||||||
const ber = new BerWriter()
|
|
||||||
ber.writeEnumeration(0)
|
|
||||||
ber.writeString('cn=root')
|
|
||||||
ber.writeString('foo')
|
|
||||||
|
|
||||||
const res = new BindResponse()
|
|
||||||
t.ok(res._parse(new BerReader(ber.buffer)))
|
|
||||||
t.equal(res.status, 0)
|
|
||||||
t.equal(res.matchedDN, 'cn=root')
|
|
||||||
t.equal(res.errorMessage, 'foo')
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('toBer', function (t) {
|
|
||||||
const res = new BindResponse({
|
|
||||||
messageID: 123,
|
|
||||||
status: 3,
|
|
||||||
matchedDN: 'cn=root',
|
|
||||||
errorMessage: 'foo'
|
|
||||||
})
|
|
||||||
t.ok(res)
|
|
||||||
|
|
||||||
const ber = new BerReader(res.toBer())
|
|
||||||
t.ok(ber)
|
|
||||||
t.equal(ber.readSequence(), 0x30)
|
|
||||||
t.equal(ber.readInt(), 123)
|
|
||||||
t.equal(ber.readSequence(), 0x61)
|
|
||||||
t.equal(ber.readEnumeration(), 3)
|
|
||||||
t.equal(ber.readString(), 'cn=root')
|
|
||||||
t.equal(ber.readString(), 'foo')
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
|
@ -1,64 +0,0 @@
|
||||||
'use strict'
|
|
||||||
|
|
||||||
const { test } = require('tap')
|
|
||||||
const { BerReader, BerWriter } = require('@ldapjs/asn1')
|
|
||||||
const { CompareRequest, dn } = require('../../lib')
|
|
||||||
|
|
||||||
test('new no args', function (t) {
|
|
||||||
t.ok(new CompareRequest())
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('new with args', function (t) {
|
|
||||||
const req = new CompareRequest({
|
|
||||||
entry: dn.parse('cn=foo, o=test'),
|
|
||||||
attribute: 'sn',
|
|
||||||
value: 'testy'
|
|
||||||
})
|
|
||||||
t.ok(req)
|
|
||||||
t.equal(req.dn.toString(), 'cn=foo, o=test')
|
|
||||||
t.equal(req.attribute, 'sn')
|
|
||||||
t.equal(req.value, 'testy')
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('parse', function (t) {
|
|
||||||
const ber = new BerWriter()
|
|
||||||
ber.writeString('cn=foo, o=test')
|
|
||||||
|
|
||||||
ber.startSequence()
|
|
||||||
ber.writeString('sn')
|
|
||||||
ber.writeString('testy')
|
|
||||||
ber.endSequence()
|
|
||||||
|
|
||||||
const req = new CompareRequest()
|
|
||||||
t.ok(req._parse(new BerReader(ber.buffer)))
|
|
||||||
t.equal(req.dn, 'cn=foo, o=test')
|
|
||||||
t.equal(req.attribute, 'sn')
|
|
||||||
t.equal(req.value, 'testy')
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('toBer', function (t) {
|
|
||||||
const req = new CompareRequest({
|
|
||||||
messageID: 123,
|
|
||||||
entry: dn.parse('cn=foo, o=test'),
|
|
||||||
attribute: 'sn',
|
|
||||||
value: 'testy'
|
|
||||||
})
|
|
||||||
|
|
||||||
t.ok(req)
|
|
||||||
|
|
||||||
const ber = new BerReader(req.toBer())
|
|
||||||
t.ok(ber)
|
|
||||||
t.equal(ber.readSequence(), 0x30)
|
|
||||||
t.equal(ber.readInt(), 123)
|
|
||||||
t.equal(ber.readSequence(), 0x6e)
|
|
||||||
t.equal(ber.readString(), 'cn=foo, o=test')
|
|
||||||
t.ok(ber.readSequence())
|
|
||||||
|
|
||||||
t.equal(ber.readString(), 'sn')
|
|
||||||
t.equal(ber.readString(), 'testy')
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
|
@ -1,56 +0,0 @@
|
||||||
'use strict'
|
|
||||||
|
|
||||||
const { test } = require('tap')
|
|
||||||
const { BerReader, BerWriter } = require('@ldapjs/asn1')
|
|
||||||
const { CompareResponse } = require('../../lib')
|
|
||||||
|
|
||||||
test('new no args', function (t) {
|
|
||||||
t.ok(new CompareResponse())
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('new with args', function (t) {
|
|
||||||
const res = new CompareResponse({
|
|
||||||
messageID: 123,
|
|
||||||
status: 0
|
|
||||||
})
|
|
||||||
t.ok(res)
|
|
||||||
t.equal(res.messageID, 123)
|
|
||||||
t.equal(res.status, 0)
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('parse', function (t) {
|
|
||||||
const ber = new BerWriter()
|
|
||||||
ber.writeEnumeration(0)
|
|
||||||
ber.writeString('cn=root')
|
|
||||||
ber.writeString('foo')
|
|
||||||
|
|
||||||
const res = new CompareResponse()
|
|
||||||
t.ok(res._parse(new BerReader(ber.buffer)))
|
|
||||||
t.equal(res.status, 0)
|
|
||||||
t.equal(res.matchedDN, 'cn=root')
|
|
||||||
t.equal(res.errorMessage, 'foo')
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('toBer', function (t) {
|
|
||||||
const res = new CompareResponse({
|
|
||||||
messageID: 123,
|
|
||||||
status: 3,
|
|
||||||
matchedDN: 'cn=root',
|
|
||||||
errorMessage: 'foo'
|
|
||||||
})
|
|
||||||
t.ok(res)
|
|
||||||
|
|
||||||
const ber = new BerReader(res.toBer())
|
|
||||||
t.ok(ber)
|
|
||||||
t.equal(ber.readSequence(), 0x30)
|
|
||||||
t.equal(ber.readInt(), 123)
|
|
||||||
t.equal(ber.readSequence(), 0x6f)
|
|
||||||
t.equal(ber.readEnumeration(), 3)
|
|
||||||
t.equal(ber.readString(), 'cn=root')
|
|
||||||
t.equal(ber.readString(), 'foo')
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
|
@ -1,47 +0,0 @@
|
||||||
'use strict'
|
|
||||||
|
|
||||||
const { test } = require('tap')
|
|
||||||
const { BerReader, BerWriter } = require('@ldapjs/asn1')
|
|
||||||
const { DeleteRequest, dn } = require('../../lib')
|
|
||||||
|
|
||||||
test('new no args', function (t) {
|
|
||||||
t.ok(new DeleteRequest())
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('new with args', function (t) {
|
|
||||||
const req = new DeleteRequest({
|
|
||||||
entry: dn.parse('cn=test')
|
|
||||||
})
|
|
||||||
t.ok(req)
|
|
||||||
t.equal(req.dn.toString(), 'cn=test')
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('parse', function (t) {
|
|
||||||
const ber = new BerWriter()
|
|
||||||
ber.writeString('cn=test', 0x4a)
|
|
||||||
|
|
||||||
const req = new DeleteRequest()
|
|
||||||
const reader = new BerReader(ber.buffer)
|
|
||||||
reader.readSequence(0x4a)
|
|
||||||
t.ok(req.parse(reader, reader.length))
|
|
||||||
t.equal(req.dn.toString(), 'cn=test')
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('toBer', function (t) {
|
|
||||||
const req = new DeleteRequest({
|
|
||||||
messageID: 123,
|
|
||||||
entry: dn.parse('cn=test')
|
|
||||||
})
|
|
||||||
t.ok(req)
|
|
||||||
|
|
||||||
const ber = new BerReader(req.toBer())
|
|
||||||
t.ok(ber)
|
|
||||||
t.equal(ber.readSequence(), 0x30)
|
|
||||||
t.equal(ber.readInt(), 123)
|
|
||||||
t.equal(ber.readString(0x4a), 'cn=test')
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
|
@ -1,56 +0,0 @@
|
||||||
'use strict'
|
|
||||||
|
|
||||||
const { test } = require('tap')
|
|
||||||
const { BerReader, BerWriter } = require('@ldapjs/asn1')
|
|
||||||
const { DeleteResponse } = require('../../lib')
|
|
||||||
|
|
||||||
test('new no args', function (t) {
|
|
||||||
t.ok(new DeleteResponse())
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('new with args', function (t) {
|
|
||||||
const res = new DeleteResponse({
|
|
||||||
messageID: 123,
|
|
||||||
status: 0
|
|
||||||
})
|
|
||||||
t.ok(res)
|
|
||||||
t.equal(res.messageID, 123)
|
|
||||||
t.equal(res.status, 0)
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('parse', function (t) {
|
|
||||||
const ber = new BerWriter()
|
|
||||||
ber.writeEnumeration(0)
|
|
||||||
ber.writeString('cn=root')
|
|
||||||
ber.writeString('foo')
|
|
||||||
|
|
||||||
const res = new DeleteResponse()
|
|
||||||
t.ok(res._parse(new BerReader(ber.buffer)))
|
|
||||||
t.equal(res.status, 0)
|
|
||||||
t.equal(res.matchedDN, 'cn=root')
|
|
||||||
t.equal(res.errorMessage, 'foo')
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('toBer', function (t) {
|
|
||||||
const res = new DeleteResponse({
|
|
||||||
messageID: 123,
|
|
||||||
status: 3,
|
|
||||||
matchedDN: 'cn=root',
|
|
||||||
errorMessage: 'foo'
|
|
||||||
})
|
|
||||||
t.ok(res)
|
|
||||||
|
|
||||||
const ber = new BerReader(res.toBer())
|
|
||||||
t.ok(ber)
|
|
||||||
t.equal(ber.readSequence(), 0x30)
|
|
||||||
t.equal(ber.readInt(), 123)
|
|
||||||
t.equal(ber.readSequence(), 0x6b)
|
|
||||||
t.equal(ber.readEnumeration(), 3)
|
|
||||||
t.equal(ber.readString(), 'cn=root')
|
|
||||||
t.equal(ber.readString(), 'foo')
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
|
@ -1,125 +0,0 @@
|
||||||
'use strict'
|
|
||||||
|
|
||||||
const { test } = require('tap')
|
|
||||||
const { BerReader, BerWriter } = require('@ldapjs/asn1')
|
|
||||||
const { ExtendedRequest } = require('../../lib')
|
|
||||||
|
|
||||||
test('new no args', function (t) {
|
|
||||||
t.ok(new ExtendedRequest())
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('new with args', function (t) {
|
|
||||||
const req = new ExtendedRequest({
|
|
||||||
requestName: '1.2.3.4',
|
|
||||||
requestValue: 'test'
|
|
||||||
})
|
|
||||||
t.ok(req)
|
|
||||||
t.equal(req.requestName, '1.2.3.4')
|
|
||||||
t.equal(req.requestValue, 'test')
|
|
||||||
t.equal(Buffer.compare(req.requestValueBuffer, Buffer.from('test', 'utf8')), 0)
|
|
||||||
t.equal(req.value, 'test')
|
|
||||||
t.equal(Buffer.compare(req.valueBuffer, Buffer.from('test', 'utf8')), 0)
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('new with buffer args', function (t) {
|
|
||||||
const req = new ExtendedRequest({
|
|
||||||
requestName: '1.2.3.4',
|
|
||||||
requestValue: Buffer.from('test', 'utf8')
|
|
||||||
})
|
|
||||||
t.ok(req)
|
|
||||||
t.equal(req.requestName, '1.2.3.4')
|
|
||||||
t.equal(req.requestValue, req.requestValueBuffer)
|
|
||||||
t.equal(Buffer.compare(req.requestValueBuffer, Buffer.from('test', 'utf8')), 0)
|
|
||||||
t.equal(req.value, req.valueBuffer)
|
|
||||||
t.equal(Buffer.compare(req.valueBuffer, Buffer.from('test', 'utf8')), 0)
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('new no args set args', function (t) {
|
|
||||||
const req = new ExtendedRequest()
|
|
||||||
t.ok(req)
|
|
||||||
|
|
||||||
req.name = '1.2.3.4'
|
|
||||||
t.equal(req.requestName, '1.2.3.4')
|
|
||||||
|
|
||||||
req.value = 'test'
|
|
||||||
t.equal(req.requestValue, 'test')
|
|
||||||
t.equal(Buffer.compare(req.requestValueBuffer, Buffer.from('test', 'utf8')), 0)
|
|
||||||
t.equal(req.value, 'test')
|
|
||||||
t.equal(Buffer.compare(req.valueBuffer, Buffer.from('test', 'utf8')), 0)
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('new no args set args buffer', function (t) {
|
|
||||||
const req = new ExtendedRequest()
|
|
||||||
t.ok(req)
|
|
||||||
|
|
||||||
req.name = '1.2.3.4'
|
|
||||||
t.equal(req.requestName, '1.2.3.4')
|
|
||||||
|
|
||||||
req.value = Buffer.from('test', 'utf8')
|
|
||||||
t.equal(req.requestValue, req.requestValueBuffer)
|
|
||||||
t.equal(Buffer.compare(req.requestValueBuffer, Buffer.from('test', 'utf8')), 0)
|
|
||||||
t.equal(req.value, req.valueBuffer)
|
|
||||||
t.equal(Buffer.compare(req.valueBuffer, Buffer.from('test', 'utf8')), 0)
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('parse', function (t) {
|
|
||||||
const ber = new BerWriter()
|
|
||||||
ber.writeString('1.2.3.4', 0x80)
|
|
||||||
ber.writeString('test', 0x81)
|
|
||||||
|
|
||||||
const req = new ExtendedRequest()
|
|
||||||
t.ok(req._parse(new BerReader(ber.buffer)))
|
|
||||||
t.equal(req.requestName, '1.2.3.4')
|
|
||||||
t.equal(req.requestValue, 'test')
|
|
||||||
t.equal(Buffer.compare(req.requestValueBuffer, Buffer.from('test', 'utf8')), 0)
|
|
||||||
t.equal(req.value, 'test')
|
|
||||||
t.equal(Buffer.compare(req.valueBuffer, Buffer.from('test', 'utf8')), 0)
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('toBer', function (t) {
|
|
||||||
const req = new ExtendedRequest({
|
|
||||||
messageID: 123,
|
|
||||||
requestName: '1.2.3.4',
|
|
||||||
requestValue: 'test'
|
|
||||||
})
|
|
||||||
|
|
||||||
t.ok(req)
|
|
||||||
|
|
||||||
const ber = new BerReader(req.toBer())
|
|
||||||
t.ok(ber)
|
|
||||||
t.equal(ber.readSequence(), 0x30)
|
|
||||||
t.equal(ber.readInt(), 123)
|
|
||||||
t.equal(ber.readSequence(), 0x77)
|
|
||||||
t.equal(ber.readString(0x80), '1.2.3.4')
|
|
||||||
t.equal(ber.readString(0x81), 'test')
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('toBer from buffer', function (t) {
|
|
||||||
const req = new ExtendedRequest({
|
|
||||||
messageID: 123,
|
|
||||||
requestName: '1.2.3.4',
|
|
||||||
requestValue: Buffer.from('test', 'utf8')
|
|
||||||
})
|
|
||||||
|
|
||||||
t.ok(req)
|
|
||||||
|
|
||||||
const ber = new BerReader(req.toBer())
|
|
||||||
t.ok(ber)
|
|
||||||
t.equal(ber.readSequence(), 0x30)
|
|
||||||
t.equal(ber.readInt(), 123)
|
|
||||||
t.equal(ber.readSequence(), 0x77)
|
|
||||||
t.equal(ber.readString(0x80), '1.2.3.4')
|
|
||||||
t.equal(ber.readString(0x81), 'test')
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
|
@ -1,68 +0,0 @@
|
||||||
'use strict'
|
|
||||||
|
|
||||||
const { test } = require('tap')
|
|
||||||
const { BerReader, BerWriter } = require('@ldapjs/asn1')
|
|
||||||
const { ExtendedResponse } = require('../../lib')
|
|
||||||
|
|
||||||
test('new no args', function (t) {
|
|
||||||
t.ok(new ExtendedResponse())
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('new with args', function (t) {
|
|
||||||
const res = new ExtendedResponse({
|
|
||||||
messageID: 123,
|
|
||||||
status: 0,
|
|
||||||
responseName: '1.2.3.4',
|
|
||||||
responseValue: 'test'
|
|
||||||
})
|
|
||||||
t.ok(res)
|
|
||||||
t.equal(res.messageID, 123)
|
|
||||||
t.equal(res.status, 0)
|
|
||||||
t.equal(res.responseName, '1.2.3.4')
|
|
||||||
t.equal(res.responseValue, 'test')
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('parse', function (t) {
|
|
||||||
const ber = new BerWriter()
|
|
||||||
ber.writeEnumeration(0)
|
|
||||||
ber.writeString('cn=root')
|
|
||||||
ber.writeString('foo')
|
|
||||||
ber.writeString('1.2.3.4', 0x8a)
|
|
||||||
ber.writeString('test', 0x8b)
|
|
||||||
|
|
||||||
const res = new ExtendedResponse()
|
|
||||||
t.ok(res._parse(new BerReader(ber.buffer)))
|
|
||||||
t.equal(res.status, 0)
|
|
||||||
t.equal(res.matchedDN, 'cn=root')
|
|
||||||
t.equal(res.errorMessage, 'foo')
|
|
||||||
t.equal(res.responseName, '1.2.3.4')
|
|
||||||
t.equal(res.responseValue, 'test')
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('toBer', function (t) {
|
|
||||||
const res = new ExtendedResponse({
|
|
||||||
messageID: 123,
|
|
||||||
status: 3,
|
|
||||||
matchedDN: 'cn=root',
|
|
||||||
errorMessage: 'foo',
|
|
||||||
responseName: '1.2.3.4',
|
|
||||||
responseValue: 'test'
|
|
||||||
})
|
|
||||||
t.ok(res)
|
|
||||||
|
|
||||||
const ber = new BerReader(res.toBer())
|
|
||||||
t.ok(ber)
|
|
||||||
t.equal(ber.readSequence(), 0x30)
|
|
||||||
t.equal(ber.readInt(), 123)
|
|
||||||
t.equal(ber.readSequence(), 0x78)
|
|
||||||
t.equal(ber.readEnumeration(), 3)
|
|
||||||
t.equal(ber.readString(), 'cn=root')
|
|
||||||
t.equal(ber.readString(), 'foo')
|
|
||||||
t.equal(ber.readString(0x8a), '1.2.3.4')
|
|
||||||
t.equal(ber.readString(0x8b), 'test')
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
|
@ -1,60 +0,0 @@
|
||||||
'use strict'
|
|
||||||
|
|
||||||
const { test } = require('tap')
|
|
||||||
const { BerReader, BerWriter } = require('@ldapjs/asn1')
|
|
||||||
const { ModifyDNRequest, dn } = require('../../lib')
|
|
||||||
|
|
||||||
test('new no args', function (t) {
|
|
||||||
t.ok(new ModifyDNRequest())
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('new with args', function (t) {
|
|
||||||
const req = new ModifyDNRequest({
|
|
||||||
entry: dn.parse('cn=foo, o=test'),
|
|
||||||
newRdn: dn.parse('cn=foo2'),
|
|
||||||
deleteOldRdn: true
|
|
||||||
})
|
|
||||||
t.ok(req)
|
|
||||||
t.equal(req.dn.toString(), 'cn=foo, o=test')
|
|
||||||
t.equal(req.newRdn.toString(), 'cn=foo2')
|
|
||||||
t.equal(req.deleteOldRdn, true)
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('parse', function (t) {
|
|
||||||
const ber = new BerWriter()
|
|
||||||
ber.writeString('cn=foo, o=test')
|
|
||||||
ber.writeString('cn=foo2')
|
|
||||||
ber.writeBoolean(true)
|
|
||||||
|
|
||||||
const req = new ModifyDNRequest()
|
|
||||||
t.ok(req._parse(new BerReader(ber.buffer)))
|
|
||||||
t.equal(req.dn.toString(), 'cn=foo, o=test')
|
|
||||||
t.equal(req.newRdn.toString(), 'cn=foo2')
|
|
||||||
t.equal(req.deleteOldRdn, true)
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('toBer', function (t) {
|
|
||||||
const req = new ModifyDNRequest({
|
|
||||||
messageID: 123,
|
|
||||||
entry: dn.parse('cn=foo, o=test'),
|
|
||||||
newRdn: dn.parse('cn=foo2'),
|
|
||||||
deleteOldRdn: true
|
|
||||||
})
|
|
||||||
|
|
||||||
t.ok(req)
|
|
||||||
|
|
||||||
const ber = new BerReader(req.toBer())
|
|
||||||
t.ok(ber)
|
|
||||||
t.equal(ber.readSequence(), 0x30)
|
|
||||||
t.equal(ber.readInt(), 123)
|
|
||||||
t.equal(ber.readSequence(), 0x6c)
|
|
||||||
t.equal(ber.readString(), 'cn=foo, o=test')
|
|
||||||
t.equal(ber.readString(), 'cn=foo2')
|
|
||||||
t.equal(ber.readBoolean(), true)
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
|
@ -1,56 +0,0 @@
|
||||||
'use strict'
|
|
||||||
|
|
||||||
const { test } = require('tap')
|
|
||||||
const { BerReader, BerWriter } = require('@ldapjs/asn1')
|
|
||||||
const { ModifyDNResponse } = require('../../lib')
|
|
||||||
|
|
||||||
test('new no args', function (t) {
|
|
||||||
t.ok(new ModifyDNResponse())
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('new with args', function (t) {
|
|
||||||
const res = new ModifyDNResponse({
|
|
||||||
messageID: 123,
|
|
||||||
status: 0
|
|
||||||
})
|
|
||||||
t.ok(res)
|
|
||||||
t.equal(res.messageID, 123)
|
|
||||||
t.equal(res.status, 0)
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('parse', function (t) {
|
|
||||||
const ber = new BerWriter()
|
|
||||||
ber.writeEnumeration(0)
|
|
||||||
ber.writeString('cn=root')
|
|
||||||
ber.writeString('foo')
|
|
||||||
|
|
||||||
const res = new ModifyDNResponse()
|
|
||||||
t.ok(res._parse(new BerReader(ber.buffer)))
|
|
||||||
t.equal(res.status, 0)
|
|
||||||
t.equal(res.matchedDN, 'cn=root')
|
|
||||||
t.equal(res.errorMessage, 'foo')
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('toBer', function (t) {
|
|
||||||
const res = new ModifyDNResponse({
|
|
||||||
messageID: 123,
|
|
||||||
status: 3,
|
|
||||||
matchedDN: 'cn=root',
|
|
||||||
errorMessage: 'foo'
|
|
||||||
})
|
|
||||||
t.ok(res)
|
|
||||||
|
|
||||||
const ber = new BerReader(res.toBer())
|
|
||||||
t.ok(ber)
|
|
||||||
t.equal(ber.readSequence(), 0x30)
|
|
||||||
t.equal(ber.readInt(), 123)
|
|
||||||
t.equal(ber.readSequence(), 0x6d)
|
|
||||||
t.equal(ber.readEnumeration(), 3)
|
|
||||||
t.equal(ber.readString(), 'cn=root')
|
|
||||||
t.equal(ber.readString(), 'foo')
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
|
@ -1,86 +0,0 @@
|
||||||
'use strict'
|
|
||||||
|
|
||||||
const { test } = require('tap')
|
|
||||||
const { BerReader, BerWriter } = require('@ldapjs/asn1')
|
|
||||||
const { ModifyRequest, Attribute, Change, dn } = require('../../lib')
|
|
||||||
|
|
||||||
test('new no args', function (t) {
|
|
||||||
t.ok(new ModifyRequest())
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('new with args', function (t) {
|
|
||||||
const req = new ModifyRequest({
|
|
||||||
object: dn.parse('cn=foo, o=test'),
|
|
||||||
changes: [new Change({
|
|
||||||
operation: 'Replace',
|
|
||||||
modification: new Attribute({ type: 'objectclass', vals: ['person'] })
|
|
||||||
})]
|
|
||||||
})
|
|
||||||
t.ok(req)
|
|
||||||
t.equal(req.dn.toString(), 'cn=foo, o=test')
|
|
||||||
t.equal(req.changes.length, 1)
|
|
||||||
t.equal(req.changes[0].operation, 'replace')
|
|
||||||
t.equal(req.changes[0].modification.type, 'objectclass')
|
|
||||||
t.equal(req.changes[0].modification.vals[0], 'person')
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('parse', function (t) {
|
|
||||||
const ber = new BerWriter()
|
|
||||||
ber.writeString('cn=foo, o=test')
|
|
||||||
ber.startSequence()
|
|
||||||
|
|
||||||
ber.startSequence()
|
|
||||||
ber.writeEnumeration(0x02)
|
|
||||||
|
|
||||||
ber.startSequence()
|
|
||||||
ber.writeString('objectclass')
|
|
||||||
ber.startSequence(0x31)
|
|
||||||
ber.writeString('person')
|
|
||||||
ber.endSequence()
|
|
||||||
ber.endSequence()
|
|
||||||
|
|
||||||
ber.endSequence()
|
|
||||||
|
|
||||||
ber.endSequence()
|
|
||||||
|
|
||||||
const req = new ModifyRequest()
|
|
||||||
t.ok(req._parse(new BerReader(ber.buffer)))
|
|
||||||
t.equal(req.dn.toString(), 'cn=foo, o=test')
|
|
||||||
t.equal(req.changes.length, 1)
|
|
||||||
t.equal(req.changes[0].operation, 'replace')
|
|
||||||
t.equal(req.changes[0].modification.type, 'objectclass')
|
|
||||||
t.equal(req.changes[0].modification.vals[0], 'person')
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('toBer', function (t) {
|
|
||||||
const req = new ModifyRequest({
|
|
||||||
messageID: 123,
|
|
||||||
object: dn.parse('cn=foo, o=test'),
|
|
||||||
changes: [new Change({
|
|
||||||
operation: 'Replace',
|
|
||||||
modification: new Attribute({ type: 'objectclass', vals: ['person'] })
|
|
||||||
})]
|
|
||||||
})
|
|
||||||
|
|
||||||
t.ok(req)
|
|
||||||
|
|
||||||
const ber = new BerReader(req.toBer())
|
|
||||||
t.ok(ber)
|
|
||||||
t.equal(ber.readSequence(), 0x30)
|
|
||||||
t.equal(ber.readInt(), 123)
|
|
||||||
t.equal(ber.readSequence(), 0x66)
|
|
||||||
t.equal(ber.readString(), 'cn=foo, o=test')
|
|
||||||
t.ok(ber.readSequence())
|
|
||||||
t.ok(ber.readSequence())
|
|
||||||
t.equal(ber.readEnumeration(), 0x02)
|
|
||||||
|
|
||||||
t.ok(ber.readSequence())
|
|
||||||
t.equal(ber.readString(), 'objectclass')
|
|
||||||
t.equal(ber.readSequence(), 0x31)
|
|
||||||
t.equal(ber.readString(), 'person')
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
|
@ -1,56 +0,0 @@
|
||||||
'use strict'
|
|
||||||
|
|
||||||
const { test } = require('tap')
|
|
||||||
const { BerReader, BerWriter } = require('@ldapjs/asn1')
|
|
||||||
const { ModifyResponse } = require('../../lib')
|
|
||||||
|
|
||||||
test('new no args', function (t) {
|
|
||||||
t.ok(new ModifyResponse())
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('new with args', function (t) {
|
|
||||||
const res = new ModifyResponse({
|
|
||||||
messageID: 123,
|
|
||||||
status: 0
|
|
||||||
})
|
|
||||||
t.ok(res)
|
|
||||||
t.equal(res.messageID, 123)
|
|
||||||
t.equal(res.status, 0)
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('parse', function (t) {
|
|
||||||
const ber = new BerWriter()
|
|
||||||
ber.writeEnumeration(0)
|
|
||||||
ber.writeString('cn=root')
|
|
||||||
ber.writeString('foo')
|
|
||||||
|
|
||||||
const res = new ModifyResponse()
|
|
||||||
t.ok(res._parse(new BerReader(ber.buffer)))
|
|
||||||
t.equal(res.status, 0)
|
|
||||||
t.equal(res.matchedDN, 'cn=root')
|
|
||||||
t.equal(res.errorMessage, 'foo')
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('toBer', function (t) {
|
|
||||||
const res = new ModifyResponse({
|
|
||||||
messageID: 123,
|
|
||||||
status: 3,
|
|
||||||
matchedDN: 'cn=root',
|
|
||||||
errorMessage: 'foo'
|
|
||||||
})
|
|
||||||
t.ok(res)
|
|
||||||
|
|
||||||
const ber = new BerReader(res.toBer())
|
|
||||||
t.ok(ber)
|
|
||||||
t.equal(ber.readSequence(), 0x30)
|
|
||||||
t.equal(ber.readInt(), 123)
|
|
||||||
t.equal(ber.readSequence(), 0x67)
|
|
||||||
t.equal(ber.readEnumeration(), 3)
|
|
||||||
t.equal(ber.readString(), 'cn=root')
|
|
||||||
t.equal(ber.readString(), 'foo')
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
|
@ -1,7 +1,7 @@
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
const { test } = require('tap')
|
const { test } = require('tap')
|
||||||
const { Parser, LDAPMessage, LDAP_REQ_EXTENSION } = require('../../lib')
|
const { Parser } = require('../../lib')
|
||||||
|
|
||||||
test('wrong protocol error', function (t) {
|
test('wrong protocol error', function (t) {
|
||||||
const p = new Parser()
|
const p = new Parser()
|
||||||
|
@ -14,36 +14,3 @@ test('wrong protocol error', function (t) {
|
||||||
// Send some bogus data to incur an error
|
// Send some bogus data to incur an error
|
||||||
p.write(Buffer.from([16, 1, 4]))
|
p.write(Buffer.from([16, 1, 4]))
|
||||||
})
|
})
|
||||||
|
|
||||||
test('bad protocol op', function (t) {
|
|
||||||
const p = new Parser()
|
|
||||||
const message = new LDAPMessage({
|
|
||||||
protocolOp: 254 // bogus (at least today)
|
|
||||||
})
|
|
||||||
p.once('error', function (err) {
|
|
||||||
t.ok(err)
|
|
||||||
t.ok(/not supported$/.test(err.message))
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
p.write(message.toBer())
|
|
||||||
})
|
|
||||||
|
|
||||||
test('bad message structure', function (t) {
|
|
||||||
const p = new Parser()
|
|
||||||
|
|
||||||
// message with bogus structure
|
|
||||||
const message = new LDAPMessage({
|
|
||||||
protocolOp: LDAP_REQ_EXTENSION
|
|
||||||
})
|
|
||||||
message._toBer = function (writer) {
|
|
||||||
writer.writeBuffer(Buffer.from([16, 1, 4]), 80)
|
|
||||||
return writer
|
|
||||||
}
|
|
||||||
|
|
||||||
p.once('error', function (err) {
|
|
||||||
t.ok(err)
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
p.write(message.toBer())
|
|
||||||
})
|
|
||||||
|
|
|
@ -1,91 +0,0 @@
|
||||||
'use strict'
|
|
||||||
|
|
||||||
const { test } = require('tap')
|
|
||||||
const { BerReader, BerWriter } = require('@ldapjs/asn1')
|
|
||||||
const { SearchEntry, Attribute, dn } = require('../../lib')
|
|
||||||
|
|
||||||
test('new no args', function (t) {
|
|
||||||
t.ok(new SearchEntry())
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('new with args', function (t) {
|
|
||||||
const res = new SearchEntry({
|
|
||||||
messageID: 123,
|
|
||||||
objectName: dn.parse('cn=foo, o=test'),
|
|
||||||
attributes: [new Attribute({ type: 'cn', vals: ['foo'] }),
|
|
||||||
new Attribute({ type: 'objectclass', vals: ['person'] })]
|
|
||||||
})
|
|
||||||
t.ok(res)
|
|
||||||
t.equal(res.messageID, 123)
|
|
||||||
t.equal(res.dn.toString(), 'cn=foo, o=test')
|
|
||||||
t.equal(res.attributes.length, 2)
|
|
||||||
t.equal(res.attributes[0].type, 'cn')
|
|
||||||
t.equal(res.attributes[0].vals[0], 'foo')
|
|
||||||
t.equal(res.attributes[1].type, 'objectclass')
|
|
||||||
t.equal(res.attributes[1].vals[0], 'person')
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('parse', function (t) {
|
|
||||||
const ber = new BerWriter()
|
|
||||||
ber.writeString('cn=foo, o=test')
|
|
||||||
|
|
||||||
ber.startSequence()
|
|
||||||
|
|
||||||
ber.startSequence()
|
|
||||||
ber.writeString('cn')
|
|
||||||
ber.startSequence(0x31)
|
|
||||||
ber.writeString('foo')
|
|
||||||
ber.endSequence()
|
|
||||||
ber.endSequence()
|
|
||||||
|
|
||||||
ber.startSequence()
|
|
||||||
ber.writeString('objectclass')
|
|
||||||
ber.startSequence(0x31)
|
|
||||||
ber.writeString('person')
|
|
||||||
ber.endSequence()
|
|
||||||
ber.endSequence()
|
|
||||||
|
|
||||||
ber.endSequence()
|
|
||||||
|
|
||||||
const res = new SearchEntry()
|
|
||||||
t.ok(res._parse(new BerReader(ber.buffer)))
|
|
||||||
t.equal(res.dn, 'cn=foo, o=test')
|
|
||||||
t.equal(res.attributes.length, 2)
|
|
||||||
t.equal(res.attributes[0].type, 'cn')
|
|
||||||
t.equal(res.attributes[0].vals[0], 'foo')
|
|
||||||
t.equal(res.attributes[1].type, 'objectclass')
|
|
||||||
t.equal(res.attributes[1].vals[0], 'person')
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('toBer', function (t) {
|
|
||||||
const res = new SearchEntry({
|
|
||||||
messageID: 123,
|
|
||||||
objectName: dn.parse('cn=foo, o=test'),
|
|
||||||
attributes: [new Attribute({ type: 'cn', vals: ['foo'] }),
|
|
||||||
new Attribute({ type: 'objectclass', vals: ['person'] })]
|
|
||||||
})
|
|
||||||
t.ok(res)
|
|
||||||
|
|
||||||
const ber = new BerReader(res.toBer())
|
|
||||||
t.ok(ber)
|
|
||||||
t.equal(ber.readSequence(), 0x30)
|
|
||||||
t.equal(ber.readInt(), 123)
|
|
||||||
t.equal(ber.readSequence(), 0x64)
|
|
||||||
t.equal(ber.readString(), 'cn=foo,o=test')
|
|
||||||
t.ok(ber.readSequence())
|
|
||||||
|
|
||||||
t.ok(ber.readSequence())
|
|
||||||
t.equal(ber.readString(), 'cn')
|
|
||||||
t.equal(ber.readSequence(), 0x31)
|
|
||||||
t.equal(ber.readString(), 'foo')
|
|
||||||
|
|
||||||
t.ok(ber.readSequence())
|
|
||||||
t.equal(ber.readString(), 'objectclass')
|
|
||||||
t.equal(ber.readSequence(), 0x31)
|
|
||||||
t.equal(ber.readString(), 'person')
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
|
@ -1,98 +0,0 @@
|
||||||
'use strict'
|
|
||||||
|
|
||||||
const { test } = require('tap')
|
|
||||||
const { BerReader, BerWriter } = require('@ldapjs/asn1')
|
|
||||||
const { SearchRequest, EqualityFilter, dn } = require('../../lib')
|
|
||||||
|
|
||||||
test('new no args', function (t) {
|
|
||||||
t.ok(new SearchRequest())
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('new with args', function (t) {
|
|
||||||
const req = new SearchRequest({
|
|
||||||
baseObject: dn.parse('cn=foo, o=test'),
|
|
||||||
filter: new EqualityFilter({
|
|
||||||
attribute: 'email',
|
|
||||||
value: 'foo@bar.com'
|
|
||||||
}),
|
|
||||||
attributes: ['cn', 'sn']
|
|
||||||
})
|
|
||||||
t.ok(req)
|
|
||||||
t.equal(req.dn.toString(), 'cn=foo, o=test')
|
|
||||||
t.equal(req.filter.toString(), '(email=foo@bar.com)')
|
|
||||||
t.equal(req.attributes.length, 2)
|
|
||||||
t.equal(req.attributes[0], 'cn')
|
|
||||||
t.equal(req.attributes[1], 'sn')
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('parse', function (t) {
|
|
||||||
const f = new EqualityFilter({
|
|
||||||
attribute: 'email',
|
|
||||||
value: 'foo@bar.com'
|
|
||||||
})
|
|
||||||
|
|
||||||
const ber = new BerWriter()
|
|
||||||
ber.writeString('cn=foo, o=test')
|
|
||||||
ber.writeEnumeration(0)
|
|
||||||
ber.writeEnumeration(0)
|
|
||||||
ber.writeInt(1)
|
|
||||||
ber.writeInt(2)
|
|
||||||
ber.writeBoolean(false)
|
|
||||||
|
|
||||||
const eqBer = f.toBer()
|
|
||||||
ber.appendBuffer(eqBer.buffer)
|
|
||||||
|
|
||||||
const req = new SearchRequest()
|
|
||||||
t.ok(req._parse(new BerReader(ber.buffer)))
|
|
||||||
t.equal(req.dn.toString(), 'cn=foo, o=test')
|
|
||||||
t.equal(req.scope, 'base')
|
|
||||||
t.equal(req.derefAliases, 0)
|
|
||||||
t.equal(req.sizeLimit, 1)
|
|
||||||
t.equal(req.timeLimit, 2)
|
|
||||||
t.equal(req.typesOnly, false)
|
|
||||||
t.equal(req.filter.toString(), '(email=foo@bar.com)')
|
|
||||||
t.equal(req.attributes.length, 0)
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('toBer', function (t) {
|
|
||||||
const req = new SearchRequest({
|
|
||||||
messageID: 123,
|
|
||||||
baseObject: dn.parse('cn=foo, o=test'),
|
|
||||||
scope: 1,
|
|
||||||
derefAliases: 2,
|
|
||||||
sizeLimit: 10,
|
|
||||||
timeLimit: 20,
|
|
||||||
typesOnly: true,
|
|
||||||
filter: new EqualityFilter({
|
|
||||||
attribute: 'email',
|
|
||||||
value: 'foo@bar.com'
|
|
||||||
}),
|
|
||||||
attributes: ['cn', 'sn']
|
|
||||||
})
|
|
||||||
|
|
||||||
t.ok(req)
|
|
||||||
|
|
||||||
const ber = new BerReader(req.toBer())
|
|
||||||
t.ok(ber)
|
|
||||||
t.equal(ber.readSequence(), 0x30)
|
|
||||||
t.equal(ber.readInt(), 123)
|
|
||||||
t.equal(ber.readSequence(), 0x63)
|
|
||||||
// Make sure we've removed spaces from between RDNs:
|
|
||||||
t.equal(ber.readString(), 'cn=foo,o=test')
|
|
||||||
t.equal(ber.readEnumeration(), 1)
|
|
||||||
t.equal(ber.readEnumeration(), 2)
|
|
||||||
t.equal(ber.readInt(), 10)
|
|
||||||
t.equal(ber.readInt(), 20)
|
|
||||||
t.ok(ber.readBoolean())
|
|
||||||
t.equal(ber.readSequence(), 0xa3)
|
|
||||||
t.equal(ber.readString(), 'email')
|
|
||||||
t.equal(ber.readString(), 'foo@bar.com')
|
|
||||||
t.ok(ber.readSequence())
|
|
||||||
t.equal(ber.readString(), 'cn')
|
|
||||||
t.equal(ber.readString(), 'sn')
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
|
@ -1,56 +0,0 @@
|
||||||
'use strict'
|
|
||||||
|
|
||||||
const { test } = require('tap')
|
|
||||||
const { BerReader, BerWriter } = require('@ldapjs/asn1')
|
|
||||||
const { SearchResponse } = require('../../lib')
|
|
||||||
|
|
||||||
test('new no args', function (t) {
|
|
||||||
t.ok(new SearchResponse())
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('new with args', function (t) {
|
|
||||||
const res = new SearchResponse({
|
|
||||||
messageID: 123,
|
|
||||||
status: 0
|
|
||||||
})
|
|
||||||
t.ok(res)
|
|
||||||
t.equal(res.messageID, 123)
|
|
||||||
t.equal(res.status, 0)
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('parse', function (t) {
|
|
||||||
const ber = new BerWriter()
|
|
||||||
ber.writeEnumeration(0)
|
|
||||||
ber.writeString('cn=root')
|
|
||||||
ber.writeString('foo')
|
|
||||||
|
|
||||||
const res = new SearchResponse()
|
|
||||||
t.ok(res._parse(new BerReader(ber.buffer)))
|
|
||||||
t.equal(res.status, 0)
|
|
||||||
t.equal(res.matchedDN, 'cn=root')
|
|
||||||
t.equal(res.errorMessage, 'foo')
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('toBer', function (t) {
|
|
||||||
const res = new SearchResponse({
|
|
||||||
messageID: 123,
|
|
||||||
status: 3,
|
|
||||||
matchedDN: 'cn=root',
|
|
||||||
errorMessage: 'foo'
|
|
||||||
})
|
|
||||||
t.ok(res)
|
|
||||||
|
|
||||||
const ber = new BerReader(res.toBer())
|
|
||||||
t.ok(ber)
|
|
||||||
t.equal(ber.readSequence(), 0x30)
|
|
||||||
t.equal(ber.readInt(), 123)
|
|
||||||
t.equal(ber.readSequence(), 0x65)
|
|
||||||
t.equal(ber.readEnumeration(), 3)
|
|
||||||
t.equal(ber.readString(), 'cn=root')
|
|
||||||
t.equal(ber.readString(), 'foo')
|
|
||||||
|
|
||||||
t.end()
|
|
||||||
})
|
|
|
@ -1,37 +0,0 @@
|
||||||
'use strict'
|
|
||||||
|
|
||||||
const { test } = require('tap')
|
|
||||||
const { BerReader, BerWriter } = require('@ldapjs/asn1')
|
|
||||||
const { UnbindRequest } = require('../../lib')
|
|
||||||
|
|
||||||
test('new no args', function (t) {
|
|
||||||
t.ok(new UnbindRequest())
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('new with args', function (t) {
|
|
||||||
const req = new UnbindRequest({})
|
|
||||||
t.ok(req)
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('parse', function (t) {
|
|
||||||
const ber = new BerWriter()
|
|
||||||
|
|
||||||
const req = new UnbindRequest()
|
|
||||||
t.ok(req._parse(new BerReader(ber.buffer)))
|
|
||||||
t.end()
|
|
||||||
})
|
|
||||||
|
|
||||||
test('toBer', function (t) {
|
|
||||||
const req = new UnbindRequest({
|
|
||||||
messageID: 123
|
|
||||||
})
|
|
||||||
t.ok(req)
|
|
||||||
|
|
||||||
const ber = new BerReader(req.toBer())
|
|
||||||
t.ok(ber)
|
|
||||||
t.equal(ber.readSequence(), 0x30)
|
|
||||||
t.equal(ber.readInt(), 123)
|
|
||||||
t.end()
|
|
||||||
})
|
|
|
@ -235,7 +235,7 @@ tap.test('bind/unbind identity anonymous', function (t) {
|
||||||
return next()
|
return next()
|
||||||
})
|
})
|
||||||
|
|
||||||
const anonDN = ldap.dn.parse('cn=anonymous')
|
const anonDN = ldap.parseDN('cn=anonymous')
|
||||||
|
|
||||||
server.listen(t.context.sock, function () {
|
server.listen(t.context.sock, function () {
|
||||||
t.ok(true, 'server startup')
|
t.ok(true, 'server startup')
|
||||||
|
@ -276,8 +276,8 @@ tap.test('bind/unbind identity user', function (t) {
|
||||||
return next()
|
return next()
|
||||||
})
|
})
|
||||||
|
|
||||||
const anonDN = ldap.dn.parse('cn=anonymous')
|
const anonDN = ldap.parseDN('cn=anonymous')
|
||||||
const testDN = ldap.dn.parse('cn=anotheruser')
|
const testDN = ldap.parseDN('cn=anotheruser')
|
||||||
|
|
||||||
server.listen(t.context.sock, function () {
|
server.listen(t.context.sock, function () {
|
||||||
t.ok(true, 'server startup')
|
t.ok(true, 'server startup')
|
||||||
|
@ -316,9 +316,7 @@ tap.test('strict routing', function (t) {
|
||||||
vasync.pipeline({
|
vasync.pipeline({
|
||||||
funcs: [
|
funcs: [
|
||||||
function setup (_, cb) {
|
function setup (_, cb) {
|
||||||
server = ldap.createServer({
|
server = ldap.createServer({})
|
||||||
// strictDN: true - on by default
|
|
||||||
})
|
|
||||||
// invalid DNs would go to default handler
|
// invalid DNs would go to default handler
|
||||||
server.search('', function (req, res, next) {
|
server.search('', function (req, res, next) {
|
||||||
t.ok(req.dn)
|
t.ok(req.dn)
|
||||||
|
@ -330,26 +328,11 @@ tap.test('strict routing', function (t) {
|
||||||
server.listen(sock, function () {
|
server.listen(sock, function () {
|
||||||
t.ok(true, 'server startup')
|
t.ok(true, 'server startup')
|
||||||
clt = ldap.createClient({
|
clt = ldap.createClient({
|
||||||
socketPath: sock,
|
socketPath: sock
|
||||||
strictDN: false
|
|
||||||
})
|
})
|
||||||
cb()
|
cb()
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
function testBad (_, cb) {
|
|
||||||
clt.search('not a dn', { scope: 'base' }, function (err, res) {
|
|
||||||
t.error(err)
|
|
||||||
res.once('error', function (err2) {
|
|
||||||
t.ok(err2)
|
|
||||||
t.equal(err2.code, ldap.LDAP_INVALID_DN_SYNTAX)
|
|
||||||
cb()
|
|
||||||
})
|
|
||||||
res.once('end', function () {
|
|
||||||
t.fail('accepted invalid dn')
|
|
||||||
cb(Error('bogus'))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
},
|
|
||||||
function testGood (_, cb) {
|
function testGood (_, cb) {
|
||||||
clt.search(testDN, { scope: 'base' }, function (err, res) {
|
clt.search(testDN, { scope: 'base' }, function (err, res) {
|
||||||
t.error(err)
|
t.error(err)
|
||||||
|
@ -373,37 +356,6 @@ tap.test('strict routing', function (t) {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
tap.test('non-strict routing', function (t) {
|
|
||||||
const server = ldap.createServer({
|
|
||||||
strictDN: false
|
|
||||||
})
|
|
||||||
const testDN = 'this ain\'t a DN'
|
|
||||||
|
|
||||||
// invalid DNs go to default handler
|
|
||||||
server.search('', function (req, res, next) {
|
|
||||||
t.ok(req.dn)
|
|
||||||
t.equal(typeof (req.dn), 'string')
|
|
||||||
t.equal(req.dn, testDN)
|
|
||||||
res.end()
|
|
||||||
next()
|
|
||||||
})
|
|
||||||
|
|
||||||
server.listen(t.context.sock, function () {
|
|
||||||
t.ok(true, 'server startup')
|
|
||||||
const clt = ldap.createClient({
|
|
||||||
socketPath: t.context.sock,
|
|
||||||
strictDN: false
|
|
||||||
})
|
|
||||||
clt.search(testDN, { scope: 'base' }, function (err, res) {
|
|
||||||
t.error(err)
|
|
||||||
res.on('end', function () {
|
|
||||||
clt.destroy()
|
|
||||||
server.close(() => t.end())
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
tap.test('close accept a callback', function (t) {
|
tap.test('close accept a callback', function (t) {
|
||||||
const server = ldap.createServer()
|
const server = ldap.createServer()
|
||||||
// callback is called when the server is closed
|
// callback is called when the server is closed
|
||||||
|
|
Loading…
Reference in New Issue