Replace messages with @ldapjs/messages
This commit is contained in:
parent
685465843d
commit
f18dee40a2
|
@ -4,6 +4,9 @@ module.exports = {
|
|||
es2021: true,
|
||||
node: true
|
||||
},
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest'
|
||||
},
|
||||
extends: [
|
||||
'standard'
|
||||
],
|
||||
|
|
4
.npmrc
4
.npmrc
|
@ -1,7 +1,3 @@
|
|||
# npm general settings
|
||||
package-lock=false
|
||||
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)|
|
||||
|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|
|
||||
|strictDN |Force strict DN parsing for client methods (Default is true)|
|
||||
|reconnect |Try to reconnect when the connection gets lost (Default is false)|
|
||||
|
||||
### 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`
|
||||
, `searchReference`, `error` and `end` event.
|
||||
`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
|
||||
(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
|
||||
|
@ -306,7 +305,7 @@ client.search('o=example', opts, (err, res) => {
|
|||
assert.ifError(err);
|
||||
|
||||
res.on('searchRequest', (searchRequest) => {
|
||||
console.log('searchRequest: ', searchRequest.messageID);
|
||||
console.log('searchRequest: ', searchRequest.messageId);
|
||||
});
|
||||
res.on('searchEntry', (entry) => {
|
||||
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`.
|
||||
|
||||
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
|
||||
|
||||
|
|
|
@ -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 VError = require('verror').VError
|
||||
|
||||
const Attribute = require('../attribute')
|
||||
const Change = require('../change')
|
||||
const Attribute = require('@ldapjs/attribute')
|
||||
const Change = require('@ldapjs/change')
|
||||
const Control = require('../controls/index').Control
|
||||
const { Control: LdapControl } = require('@ldapjs/controls')
|
||||
const SearchPager = require('./search_pager')
|
||||
const Protocol = require('@ldapjs/protocol')
|
||||
const dn = require('../dn')
|
||||
const { DN } = require('@ldapjs/dn')
|
||||
const errors = require('../errors')
|
||||
const filters = require('@ldapjs/filter')
|
||||
const messages = require('../messages')
|
||||
const Parser = require('../messages/parser')
|
||||
const url = require('../url')
|
||||
const CorkedEmitter = require('../corked_emitter')
|
||||
|
||||
/// --- Globals
|
||||
|
||||
const AbandonRequest = messages.AbandonRequest
|
||||
const AddRequest = messages.AddRequest
|
||||
const BindRequest = messages.BindRequest
|
||||
const CompareRequest = messages.CompareRequest
|
||||
const DeleteRequest = messages.DeleteRequest
|
||||
const ExtendedRequest = messages.ExtendedRequest
|
||||
const ModifyRequest = messages.ModifyRequest
|
||||
const ModifyDNRequest = messages.ModifyDNRequest
|
||||
const SearchRequest = messages.SearchRequest
|
||||
const UnbindRequest = messages.UnbindRequest
|
||||
const UnbindResponse = messages.UnbindResponse
|
||||
|
||||
const LDAPResult = messages.LDAPResult
|
||||
const SearchEntry = messages.SearchEntry
|
||||
const SearchReference = messages.SearchReference
|
||||
// var SearchResponse = messages.SearchResponse
|
||||
const Parser = messages.Parser
|
||||
const messages = require('@ldapjs/messages')
|
||||
const {
|
||||
AbandonRequest,
|
||||
AddRequest,
|
||||
BindRequest,
|
||||
CompareRequest,
|
||||
DeleteRequest,
|
||||
ExtensionRequest: ExtendedRequest,
|
||||
ModifyRequest,
|
||||
ModifyDnRequest: ModifyDNRequest,
|
||||
SearchRequest,
|
||||
UnbindRequest,
|
||||
LdapResult: LDAPResult,
|
||||
SearchResultEntry: SearchEntry,
|
||||
SearchResultReference: SearchReference
|
||||
} = messages
|
||||
|
||||
const PresenceFilter = filters.PresenceFilter
|
||||
|
||||
|
@ -79,13 +78,11 @@ function validateControls (controls) {
|
|||
return controls
|
||||
}
|
||||
|
||||
function ensureDN (input, strict) {
|
||||
if (dn.DN.isDN(input)) {
|
||||
return dn
|
||||
} else if (strict) {
|
||||
return dn.parse(input)
|
||||
function ensureDN (input) {
|
||||
if (DN.isDn(input)) {
|
||||
return DN
|
||||
} else if (typeof (input) === 'string') {
|
||||
return input
|
||||
return DN.fromString(input)
|
||||
} else {
|
||||
throw new Error('invalid DN')
|
||||
}
|
||||
|
@ -137,7 +134,6 @@ function Client (options) {
|
|||
failAfter: parseInt(rOpts.failAfter, 10) || Infinity
|
||||
}
|
||||
}
|
||||
this.strictDN = (options.strictDN !== undefined) ? options.strictDN : true
|
||||
|
||||
this.queue = requestQueueFactory({
|
||||
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
|
||||
* 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 {Function} callback of the form f(err).
|
||||
* @throws {TypeError} on invalid input.
|
||||
*/
|
||||
Client.prototype.abandon = function abandon (messageID, controls, callback) {
|
||||
assert.number(messageID, 'messageID')
|
||||
Client.prototype.abandon = function abandon (messageId, controls, callback) {
|
||||
assert.number(messageId, 'messageId')
|
||||
if (typeof (controls) === 'function') {
|
||||
callback = controls
|
||||
controls = []
|
||||
|
@ -194,7 +190,7 @@ Client.prototype.abandon = function abandon (messageID, controls, callback) {
|
|||
assert.func(callback, 'callback')
|
||||
|
||||
const req = new AbandonRequest({
|
||||
abandonID: messageID,
|
||||
abandonId: messageId,
|
||||
controls: controls
|
||||
})
|
||||
|
||||
|
@ -249,7 +245,7 @@ Client.prototype.add = function add (name, entry, controls, callback) {
|
|||
}
|
||||
|
||||
const req = new AddRequest({
|
||||
entry: ensureDN(name, this.strictDN),
|
||||
entry: ensureDN(name),
|
||||
attributes: entry,
|
||||
controls: controls
|
||||
})
|
||||
|
@ -271,7 +267,12 @@ Client.prototype.bind = function bind (name,
|
|||
controls,
|
||||
callback,
|
||||
_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')
|
||||
if (typeof (controls) === 'function') {
|
||||
callback = controls
|
||||
|
@ -326,7 +327,7 @@ Client.prototype.compare = function compare (name,
|
|||
assert.func(callback, 'callback')
|
||||
|
||||
const req = new CompareRequest({
|
||||
entry: ensureDN(name, this.strictDN),
|
||||
entry: ensureDN(name),
|
||||
attribute: attr,
|
||||
value: value,
|
||||
controls: controls
|
||||
|
@ -358,7 +359,7 @@ Client.prototype.del = function del (name, controls, callback) {
|
|||
assert.func(callback, 'callback')
|
||||
|
||||
const req = new DeleteRequest({
|
||||
entry: ensureDN(name, this.strictDN),
|
||||
entry: ensureDN(name),
|
||||
controls: controls
|
||||
})
|
||||
|
||||
|
@ -469,7 +470,7 @@ Client.prototype.modify = function modify (name, change, controls, callback) {
|
|||
assert.func(callback, 'callback')
|
||||
|
||||
const req = new ModifyRequest({
|
||||
object: ensureDN(name, this.strictDN),
|
||||
object: ensureDN(name),
|
||||
changes: changes,
|
||||
controls: controls
|
||||
})
|
||||
|
@ -505,18 +506,16 @@ Client.prototype.modifyDN = function modifyDN (name,
|
|||
}
|
||||
assert.func(callback)
|
||||
|
||||
const DN = ensureDN(name)
|
||||
// TODO: is non-strict handling desired here?
|
||||
const newDN = dn.parse(newName)
|
||||
const newDN = DN.fromString(newName)
|
||||
|
||||
const req = new ModifyDNRequest({
|
||||
entry: DN,
|
||||
entry: DN.fromString(name),
|
||||
deleteOldRdn: true,
|
||||
controls: controls
|
||||
})
|
||||
|
||||
if (newDN.length !== 1) {
|
||||
req.newRdn = dn.parse(newDN.rdns.shift().toString())
|
||||
req.newRdn = DN.fromString(newDN.shift().toString())
|
||||
req.newSuperior = newDN
|
||||
} else {
|
||||
req.newRdn = newDN
|
||||
|
@ -594,7 +593,7 @@ Client.prototype.search = function search (base,
|
|||
}
|
||||
|
||||
const self = this
|
||||
const baseDN = ensureDN(base, this.strictDN)
|
||||
const baseDN = ensureDN(base)
|
||||
|
||||
function sendRequest (ctrls, emitter, cb) {
|
||||
const req = new SearchRequest({
|
||||
|
@ -877,15 +876,46 @@ Client.prototype.connect = function connect () {
|
|||
})
|
||||
|
||||
// The "router"
|
||||
//
|
||||
// This is invoked after the incoming BER has been parsed into a JavaScript
|
||||
// object.
|
||||
tracker.parser.on('message', function onMessage (message) {
|
||||
message.connection = self._socket
|
||||
const callback = tracker.fetch(message.messageID)
|
||||
const { message: trackedMessage, callback } = tracker.fetch(message.messageId)
|
||||
|
||||
if (!callback) {
|
||||
log.error({ message: message.json }, 'unsolicited message')
|
||||
log.error({ message: message.pojo }, 'unsolicited message')
|
||||
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)
|
||||
})
|
||||
|
||||
|
@ -1084,8 +1114,16 @@ Client.prototype._onClose = function _onClose (closeError) {
|
|||
return cb(new ConnectionError(tracker.id + ' closed'))
|
||||
} else {
|
||||
// Unbinds will be communicated as a success since we're closed
|
||||
const unbind = new UnbindResponse({ messageID: msgid })
|
||||
unbind.status = 'unbind'
|
||||
// TODO: we are faking this "UnbindResponse" object in order to make
|
||||
// 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)
|
||||
}
|
||||
})
|
||||
|
@ -1203,21 +1241,23 @@ Client.prototype._sendSocket = function _sendSocket (message,
|
|||
function messageCallback (msg) {
|
||||
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 (msg instanceof SearchEntry || msg instanceof SearchReference) {
|
||||
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)
|
||||
} else {
|
||||
tracker.remove(message.messageID)
|
||||
tracker.remove(message.messageId)
|
||||
// Potentially mark client as idle
|
||||
self._updateIdle()
|
||||
|
||||
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('end', msg)
|
||||
|
@ -1231,7 +1271,7 @@ Client.prototype._sendSocket = function _sendSocket (message,
|
|||
|
||||
function onRequestTimeout () {
|
||||
self.emit('timeout', message)
|
||||
const cb = tracker.fetch(message.messageID)
|
||||
const { callback: cb } = tracker.fetch(message.messageId)
|
||||
if (cb) {
|
||||
// FIXME: the timed-out request should be abandoned
|
||||
cb(new errors.TimeoutError('request timeout (client interrupt)'))
|
||||
|
@ -1240,8 +1280,8 @@ Client.prototype._sendSocket = function _sendSocket (message,
|
|||
|
||||
function writeCallback () {
|
||||
if (expect === 'abandon') {
|
||||
// Mark the messageID specified as abandoned
|
||||
tracker.abandon(message.abandonID)
|
||||
// Mark the messageId specified as abandoned
|
||||
tracker.abandon(message.abandonId)
|
||||
// No need to track the abandon request itself
|
||||
tracker.remove(message.id)
|
||||
return callback(null)
|
||||
|
@ -1273,10 +1313,11 @@ Client.prototype._sendSocket = function _sendSocket (message,
|
|||
timer = setTimeout(onRequestTimeout, self.timeout)
|
||||
}
|
||||
|
||||
log.trace('sending request %j', message.json)
|
||||
log.trace('sending request %j', message.pojo)
|
||||
|
||||
try {
|
||||
return conn.write(message.toBer(), writeCallback)
|
||||
const messageBer = message.toBer()
|
||||
return conn.write(messageBer.buffer, writeCallback)
|
||||
} catch (e) {
|
||||
if (timer) { clearTimeout(timer) }
|
||||
|
||||
|
|
|
@ -62,13 +62,23 @@ module.exports = function messageTrackerFactory (options) {
|
|||
*/
|
||||
tracker.abandon = function abandonMessage (msgID) {
|
||||
if (messages.has(msgID) === false) return false
|
||||
const toAbandon = messages.get(msgID)
|
||||
abandoned.set(msgID, {
|
||||
age: currentID,
|
||||
cb: messages.get(msgID)
|
||||
message: toAbandon.message,
|
||||
cb: toAbandon.callback
|
||||
})
|
||||
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
|
||||
* that have been given time to be resolved.
|
||||
|
@ -79,10 +89,10 @@ module.exports = function messageTrackerFactory (options) {
|
|||
* @method fetch
|
||||
*/
|
||||
tracker.fetch = function fetchMessage (msgID) {
|
||||
const messageCB = messages.get(msgID)
|
||||
if (messageCB) {
|
||||
const tracked = messages.get(msgID)
|
||||
if (tracked) {
|
||||
purgeAbandoned(msgID, abandoned)
|
||||
return messageCB
|
||||
return tracked
|
||||
}
|
||||
|
||||
// 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.
|
||||
const abandonedMsg = abandoned.get(msgID)
|
||||
if (abandonedMsg) {
|
||||
return abandonedMsg.cb
|
||||
return { message: abandonedMsg, callback: abandonedMsg.cb }
|
||||
}
|
||||
|
||||
return null
|
||||
|
@ -110,7 +120,7 @@ module.exports = function messageTrackerFactory (options) {
|
|||
messages.forEach((val, key) => {
|
||||
purgeAbandoned(key, abandoned)
|
||||
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.
|
||||
*
|
||||
* @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.
|
||||
*
|
||||
* @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
|
||||
// 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.
|
||||
message.messageID = currentID
|
||||
messages.set(currentID, callback)
|
||||
message.messageId = currentID
|
||||
messages.set(currentID, { callback, message })
|
||||
}
|
||||
|
||||
return tracker
|
||||
|
|
|
@ -2,13 +2,8 @@
|
|||
|
||||
const EventEmitter = require('events').EventEmitter
|
||||
const util = require('util')
|
||||
|
||||
const assert = require('assert-plus')
|
||||
|
||||
// var dn = require('../dn')
|
||||
// var messages = require('../messages/index')
|
||||
const { PagedResultsControl } = require('@ldapjs/controls')
|
||||
|
||||
const CorkedEmitter = require('../corked_emitter.js')
|
||||
|
||||
/// --- API
|
||||
|
@ -94,13 +89,13 @@ SearchPager.prototype._onEnd = function _onEnd (res) {
|
|||
if (this.listeners('pageError').length > 0) {
|
||||
this.emit('pageError', err)
|
||||
// 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
|
||||
// provides, so it's only a possibility if 'pageError' is used instead.
|
||||
this.emit('end', res)
|
||||
} else {
|
||||
this.emit('error', err)
|
||||
// No end event possible per explaination above.
|
||||
// No end event possible per explanation above.
|
||||
}
|
||||
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 client = require('./client')
|
||||
const Attribute = require('./attribute')
|
||||
const Change = require('./change')
|
||||
const Attribute = require('@ldapjs/attribute')
|
||||
const Change = require('@ldapjs/change')
|
||||
const Protocol = require('@ldapjs/protocol')
|
||||
const Server = require('./server')
|
||||
|
||||
const controls = require('./controls')
|
||||
const persistentSearch = require('./persistent_search')
|
||||
const dn = require('./dn')
|
||||
const dn = require('@ldapjs/dn')
|
||||
const errors = require('./errors')
|
||||
const filters = require('@ldapjs/filter')
|
||||
const messages = require('./messages')
|
||||
|
@ -43,7 +43,7 @@ module.exports = {
|
|||
dn: dn,
|
||||
DN: dn.DN,
|
||||
RDN: dn.RDN,
|
||||
parseDN: dn.parse,
|
||||
parseDN: dn.DN.fromString,
|
||||
|
||||
persistentSearch: persistentSearch,
|
||||
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.
|
||||
|
||||
const LDAPMessage = require('./message')
|
||||
const LDAPResult = require('./result')
|
||||
const messages = require('@ldapjs/messages')
|
||||
|
||||
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 UnbindRequest = require('./unbind_request')
|
||||
const UnbindResponse = require('./unbind_response')
|
||||
|
||||
/// --- API
|
||||
|
||||
module.exports = {
|
||||
|
||||
LDAPMessage: LDAPMessage,
|
||||
LDAPResult: LDAPResult,
|
||||
LDAPMessage: messages.LdapMessage,
|
||||
LDAPResult: messages.LdapResult,
|
||||
Parser: Parser,
|
||||
|
||||
AbandonRequest: AbandonRequest,
|
||||
AbandonResponse: AbandonResponse,
|
||||
AddRequest: AddRequest,
|
||||
AddResponse: AddResponse,
|
||||
BindRequest: BindRequest,
|
||||
BindResponse: BindResponse,
|
||||
CompareRequest: CompareRequest,
|
||||
CompareResponse: CompareResponse,
|
||||
DeleteRequest: DeleteRequest,
|
||||
DeleteResponse: DeleteResponse,
|
||||
ExtendedRequest: ExtendedRequest,
|
||||
ExtendedResponse: ExtendedResponse,
|
||||
ModifyRequest: ModifyRequest,
|
||||
ModifyResponse: ModifyResponse,
|
||||
ModifyDNRequest: ModifyDNRequest,
|
||||
ModifyDNResponse: ModifyDNResponse,
|
||||
SearchRequest: SearchRequest,
|
||||
SearchEntry: SearchEntry,
|
||||
SearchReference: SearchReference,
|
||||
AbandonRequest: messages.AbandonRequest,
|
||||
AbandonResponse: messages.AbandonResponse,
|
||||
AddRequest: messages.AddRequest,
|
||||
AddResponse: messages.AddResponse,
|
||||
BindRequest: messages.BindRequest,
|
||||
BindResponse: messages.BindResponse,
|
||||
CompareRequest: messages.CompareRequest,
|
||||
CompareResponse: messages.CompareResponse,
|
||||
DeleteRequest: messages.DeleteRequest,
|
||||
DeleteResponse: messages.DeleteResponse,
|
||||
ExtendedRequest: messages.ExtensionRequest,
|
||||
ExtendedResponse: messages.ExtensionResponse,
|
||||
ModifyRequest: messages.ModifyRequest,
|
||||
ModifyResponse: messages.ModifyResponse,
|
||||
ModifyDNRequest: messages.ModifyDnRequest,
|
||||
ModifyDNResponse: messages.ModifyDnResponse,
|
||||
SearchRequest: messages.SearchRequest,
|
||||
SearchEntry: messages.SearchResultEntry,
|
||||
SearchReference: messages.SearchResultReference,
|
||||
SearchResponse: SearchResponse,
|
||||
UnbindRequest: UnbindRequest,
|
||||
UnbindResponse: UnbindResponse
|
||||
UnbindRequest: messages.UnbindRequest
|
||||
|
||||
}
|
||||
|
|
|
@ -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 asn1 = require('@ldapjs/asn1')
|
||||
// var VError = require('verror').VError
|
||||
const logger = require('../logger')
|
||||
|
||||
const AbandonRequest = require('./abandon_request')
|
||||
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 messages = require('@ldapjs/messages')
|
||||
const AbandonRequest = messages.AbandonRequest
|
||||
const AddRequest = messages.AddRequest
|
||||
const AddResponse = messages.AddResponse
|
||||
const BindRequest = messages.BindRequest
|
||||
const BindResponse = messages.BindResponse
|
||||
const CompareRequest = messages.CompareRequest
|
||||
const CompareResponse = messages.CompareResponse
|
||||
const DeleteRequest = messages.DeleteRequest
|
||||
const DeleteResponse = messages.DeleteResponse
|
||||
const ExtendedRequest = messages.ExtensionRequest
|
||||
const ExtendedResponse = messages.ExtensionResponse
|
||||
const ModifyRequest = messages.ModifyRequest
|
||||
const ModifyResponse = messages.ModifyResponse
|
||||
const ModifyDNRequest = messages.ModifyDnRequest
|
||||
const ModifyDNResponse = messages.ModifyDnResponse
|
||||
const SearchRequest = messages.SearchRequest
|
||||
const SearchEntry = messages.SearchResultEntry
|
||||
const SearchReference = messages.SearchResultReference
|
||||
const SearchResponse = require('./search_response')
|
||||
const UnbindRequest = require('./unbind_request')
|
||||
// var UnbindResponse = require('./unbind_response')
|
||||
|
||||
const LDAPResult = require('./result')
|
||||
// var Message = require('./message')
|
||||
const UnbindRequest = messages.UnbindRequest
|
||||
const LDAPResult = messages.LdapResult
|
||||
|
||||
const Protocol = require('@ldapjs/protocol')
|
||||
|
||||
/// --- Globals
|
||||
|
||||
// var Ber = asn1.Ber
|
||||
const BerReader = asn1.BerReader
|
||||
|
||||
/// --- API
|
||||
|
@ -52,6 +48,13 @@ function Parser (options = {}) {
|
|||
}
|
||||
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) {
|
||||
if (!data || !Buffer.isBuffer(data)) { throw new TypeError('data (buffer) required') }
|
||||
|
||||
|
@ -64,9 +67,9 @@ Parser.prototype.write = function (data) {
|
|||
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
|
||||
try {
|
||||
|
@ -80,9 +83,22 @@ Parser.prototype.write = function (data) {
|
|||
return false
|
||||
} else if (ber.remain > ber.length) {
|
||||
// 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)
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -92,13 +108,25 @@ Parser.prototype.write = function (data) {
|
|||
|
||||
let message
|
||||
try {
|
||||
// Bail here if peer isn't speaking protocol at all
|
||||
message = this.getMessage(ber)
|
||||
if (Object.prototype.toString.call(ber) === '[object BerReader]') {
|
||||
// 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) {
|
||||
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) {
|
||||
this.emit('error', e, message)
|
||||
return false
|
||||
|
@ -113,7 +141,7 @@ Parser.prototype.getMessage = function (ber) {
|
|||
|
||||
const self = this
|
||||
|
||||
const messageID = ber.readInt()
|
||||
const messageId = ber.readInt()
|
||||
const type = ber.readSequence()
|
||||
|
||||
let Message
|
||||
|
@ -203,7 +231,7 @@ Parser.prototype.getMessage = function (ber) {
|
|||
new Error('Op 0x' + (type ? type.toString(16) : '??') +
|
||||
' not supported'),
|
||||
new LDAPResult({
|
||||
messageID: messageID,
|
||||
messageId: messageId,
|
||||
protocolOp: type || Protocol.operations.LDAP_RES_EXTENSION
|
||||
}))
|
||||
|
||||
|
@ -211,7 +239,7 @@ Parser.prototype.getMessage = function (ber) {
|
|||
}
|
||||
|
||||
return new Message({
|
||||
messageID: messageID,
|
||||
messageId: messageId,
|
||||
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.
|
||||
|
||||
const assert = require('assert-plus')
|
||||
const util = require('util')
|
||||
|
||||
const LDAPResult = require('./result')
|
||||
const SearchEntry = require('./search_entry')
|
||||
const SearchReference = require('./search_reference')
|
||||
const Attribute = require('@ldapjs/attribute')
|
||||
const {
|
||||
SearchResultEntry: SearchEntry,
|
||||
SearchResultReference: SearchReference,
|
||||
SearchResultDone
|
||||
} = require('@ldapjs/messages')
|
||||
|
||||
const dtrace = require('../dtrace')
|
||||
const parseDN = require('../dn').parse
|
||||
const parseURL = require('../url').parse
|
||||
const Protocol = require('@ldapjs/protocol')
|
||||
const parseDN = require('@ldapjs/dn').DN.fromString
|
||||
|
||||
/// --- API
|
||||
|
||||
function SearchResponse (options) {
|
||||
options = options || {}
|
||||
assert.object(options)
|
||||
class SearchResponse extends SearchResultDone {
|
||||
attributes
|
||||
notAttributes
|
||||
sentEntries
|
||||
|
||||
options.protocolOp = Protocol.operations.LDAP_RES_SEARCH
|
||||
LDAPResult.call(this, options)
|
||||
constructor (options = {}) {
|
||||
super(options)
|
||||
|
||||
this.attributes = options.attributes ? options.attributes.slice() : []
|
||||
this.notAttributes = []
|
||||
this.sentEntries = 0
|
||||
this.attributes = options.attributes ? options.attributes.slice() : []
|
||||
this.notAttributes = []
|
||||
this.sentEntries = 0
|
||||
}
|
||||
}
|
||||
util.inherits(SearchResponse, LDAPResult)
|
||||
|
||||
/**
|
||||
* Allows you to send a SearchEntry back to the client.
|
||||
|
@ -44,12 +44,16 @@ SearchResponse.prototype.send = function (entry, nofiltering) {
|
|||
const savedAttrs = {}
|
||||
let save = null
|
||||
if (entry instanceof SearchEntry || entry instanceof SearchReference) {
|
||||
if (!entry.messageID) { entry.messageID = this.messageID }
|
||||
if (entry.messageID !== this.messageID) { throw new Error('SearchEntry messageID mismatch') }
|
||||
if (!entry.messageId) { entry.messageId = this.messageId }
|
||||
if (entry.messageId !== this.messageId) {
|
||||
throw new Error('SearchEntry messageId mismatch')
|
||||
}
|
||||
} else {
|
||||
if (!entry.attributes) { throw new Error('entry.attributes required') }
|
||||
|
||||
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) {
|
||||
const _a = a.toLowerCase()
|
||||
if (!nofiltering && _a.length && _a[0] === '_') {
|
||||
|
@ -69,39 +73,24 @@ SearchResponse.prototype.send = function (entry, nofiltering) {
|
|||
save = entry
|
||||
entry = new SearchEntry({
|
||||
objectName: typeof (save.dn) === 'string' ? parseDN(save.dn) : save.dn,
|
||||
messageID: self.messageID,
|
||||
log: self.log
|
||||
messageId: self.messageId,
|
||||
attributes: Attribute.fromObject(entry.attributes)
|
||||
})
|
||||
entry.fromObject(save)
|
||||
}
|
||||
|
||||
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++
|
||||
|
||||
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
|
||||
Object.keys(savedAttrs).forEach(function (k) {
|
||||
save.attributes[k] = savedAttrs[k]
|
||||
})
|
||||
} catch (e) {
|
||||
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)
|
||||
|
||||
const entry = new SearchEntry({
|
||||
messageID: this.messageID,
|
||||
log: this.log,
|
||||
objectName: object.objectName || object.dn
|
||||
messageId: this.messageId,
|
||||
objectName: object.objectName || object.dn,
|
||||
attributes: object.attributes ?? []
|
||||
})
|
||||
entry.fromObject((object.attributes || object))
|
||||
return entry
|
||||
}
|
||||
|
||||
|
@ -122,15 +110,10 @@ SearchResponse.prototype.createSearchReference = function (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
|
||||
return new SearchReference({
|
||||
messageID: self.messageID,
|
||||
log: self.log,
|
||||
uris: uris
|
||||
messageId: self.messageId,
|
||||
uri: 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')
|
||||
const VError = require('verror').VError
|
||||
|
||||
const dn = require('./dn')
|
||||
const { DN, RDN } = require('@ldapjs/dn')
|
||||
const dtrace = require('./dtrace')
|
||||
const errors = require('./errors')
|
||||
const Protocol = require('@ldapjs/protocol')
|
||||
|
||||
const messages = require('@ldapjs/messages')
|
||||
|
||||
const Parser = require('./messages').Parser
|
||||
const AbandonResponse = require('./messages/abandon_response')
|
||||
const AddResponse = require('./messages/add_response')
|
||||
const BindResponse = require('./messages/bind_response')
|
||||
const CompareResponse = require('./messages/compare_response')
|
||||
const DeleteResponse = require('./messages/del_response')
|
||||
const ExtendedResponse = require('./messages/ext_response')
|
||||
// var LDAPResult = require('./messages/result')
|
||||
const ModifyResponse = require('./messages/modify_response')
|
||||
const ModifyDNResponse = require('./messages/moddn_response')
|
||||
const SearchRequest = require('./messages/search_request')
|
||||
const LdapResult = messages.LdapResult
|
||||
const AbandonResponse = messages.AbandonResponse
|
||||
const AddResponse = messages.AddResponse
|
||||
const BindResponse = messages.BindResponse
|
||||
const CompareResponse = messages.CompareResponse
|
||||
const DeleteResponse = messages.DeleteResponse
|
||||
const ExtendedResponse = messages.ExtensionResponse
|
||||
const ModifyResponse = messages.ModifyResponse
|
||||
const ModifyDnResponse = messages.ModifyDnResponse
|
||||
const SearchRequest = messages.SearchRequest
|
||||
const SearchResponse = require('./messages/search_response')
|
||||
const UnbindResponse = require('./messages/unbind_response')
|
||||
|
||||
/// --- Globals
|
||||
|
||||
// var Ber = asn1.Ber
|
||||
// var BerReader = asn1.BerReader
|
||||
const DN = dn.DN
|
||||
// const DN = dn.DN
|
||||
|
||||
// var sprintf = util.format
|
||||
|
||||
|
@ -93,13 +94,20 @@ function getResponse (req) {
|
|||
Response = ModifyResponse
|
||||
break
|
||||
case Protocol.operations.LDAP_REQ_MODRDN:
|
||||
Response = ModifyDNResponse
|
||||
Response = ModifyDnResponse
|
||||
break
|
||||
case Protocol.operations.LDAP_REQ_SEARCH:
|
||||
Response = SearchResponse
|
||||
break
|
||||
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
|
||||
default:
|
||||
return null
|
||||
|
@ -107,16 +115,83 @@ function getResponse (req) {
|
|||
assert.ok(Response)
|
||||
|
||||
const res = new Response({
|
||||
messageID: req.messageID,
|
||||
log: req.log,
|
||||
messageId: req.messageId,
|
||||
attributes: ((req instanceof SearchRequest) ? req.attributes : undefined)
|
||||
})
|
||||
res.log = req.log
|
||||
res.connection = req.connection
|
||||
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
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
assert.ok(req)
|
||||
assert.ok(res)
|
||||
|
@ -157,69 +232,6 @@ function noExOpHandler (req, res, 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
|
||||
|
||||
/**
|
||||
|
@ -261,8 +273,6 @@ function Server (options) {
|
|||
|
||||
this._chain = []
|
||||
this.log = options.log
|
||||
this.strictDN = (options.strictDN !== undefined) ? options.strictDN : true
|
||||
|
||||
const log = this.log
|
||||
|
||||
function setupConnection (c) {
|
||||
|
@ -277,12 +287,12 @@ function Server (options) {
|
|||
c.remotePort = c.socket.remotePort
|
||||
}
|
||||
|
||||
const rdn = new dn.RDN({ cn: 'anonymous' })
|
||||
const rdn = new RDN({ cn: 'anonymous' })
|
||||
|
||||
c.ldap = {
|
||||
id: c.remoteAddress + ':' + c.remotePort,
|
||||
config: options,
|
||||
_bindDN: new DN([rdn])
|
||||
_bindDN: new DN({ rdns: [rdn] })
|
||||
}
|
||||
c.addListener('timeout', function () {
|
||||
log.trace('%s timed out', c.ldap.id)
|
||||
|
@ -305,7 +315,9 @@ function Server (options) {
|
|||
return c.ldap._bindDN
|
||||
})
|
||||
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
|
||||
return val
|
||||
|
@ -327,11 +339,13 @@ function Server (options) {
|
|||
log: options.log
|
||||
})
|
||||
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.logId = conn.ldap.id + '::' + req.messageID
|
||||
req.logId = conn.ldap.id + '::' + req.messageId
|
||||
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)
|
||||
if (!res) {
|
||||
|
@ -343,31 +357,47 @@ function Server (options) {
|
|||
// parse string DNs for routing/etc
|
||||
try {
|
||||
switch (req.protocolOp) {
|
||||
case Protocol.operations.LDAP_REQ_BIND:
|
||||
req.name = dn.parse(req.name)
|
||||
case Protocol.operations.LDAP_REQ_BIND: {
|
||||
req.name = DN.fromString(req.name)
|
||||
break
|
||||
}
|
||||
|
||||
case Protocol.operations.LDAP_REQ_ADD:
|
||||
case Protocol.operations.LDAP_REQ_COMPARE:
|
||||
case Protocol.operations.LDAP_REQ_DELETE:
|
||||
req.entry = dn.parse(req.entry)
|
||||
case Protocol.operations.LDAP_REQ_DELETE: {
|
||||
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
|
||||
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
|
||||
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
|
||||
break
|
||||
case Protocol.operations.LDAP_REQ_SEARCH:
|
||||
req.baseObject = dn.parse(req.baseObject)
|
||||
}
|
||||
|
||||
case Protocol.operations.LDAP_REQ_SEARCH: {
|
||||
break
|
||||
default:
|
||||
}
|
||||
|
||||
default: {
|
||||
break
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
if (self.strictDN) {
|
||||
return res.end(errors.LDAP_INVALID_DN_SYNTAX)
|
||||
}
|
||||
return res.end(errors.LDAP_INVALID_DN_SYNTAX)
|
||||
}
|
||||
|
||||
res.connection = conn
|
||||
|
@ -409,16 +439,16 @@ function Server (options) {
|
|||
if (req.protocolOp === Protocol.operations.LDAP_REQ_BIND && res.status === 0) {
|
||||
// 0 length == anonymous bind
|
||||
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 {
|
||||
conn.ldap.bindDN = req.dn
|
||||
conn.ldap.bindDN = DN.fromString(req.dn)
|
||||
}
|
||||
}
|
||||
|
||||
// unbind clear bindDN for safety
|
||||
// conn should terminate on unbind (RFC4511 4.3)
|
||||
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()
|
||||
|
@ -725,12 +755,10 @@ Server.prototype.getConnections = function (callback) {
|
|||
}
|
||||
|
||||
Server.prototype._getRoute = function (_dn, backend) {
|
||||
assert.ok(dn)
|
||||
|
||||
if (!backend) { backend = this }
|
||||
|
||||
let name
|
||||
if (_dn instanceof dn.DN) {
|
||||
if (Object.prototype.toString.call(_dn) === '[object LdapDn]') {
|
||||
name = _dn.toString()
|
||||
} else {
|
||||
name = _dn
|
||||
|
@ -757,10 +785,10 @@ Server.prototype._sortedRouteKeys = function _sortedRouteKeys () {
|
|||
Object.keys(this.routes).forEach(function (key) {
|
||||
const _dn = self.routes[key].dn
|
||||
// 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()
|
||||
reversed.rdns.reverse()
|
||||
reversedRDNsToKeys[reversed.format()] = key
|
||||
reversed.reverse()
|
||||
reversedRDNsToKeys[reversed.toString()] = key
|
||||
}
|
||||
})
|
||||
const output = []
|
||||
|
@ -777,11 +805,9 @@ Server.prototype._sortedRouteKeys = function _sortedRouteKeys () {
|
|||
return this._routeKeyCache
|
||||
}
|
||||
|
||||
Server.prototype._getHandlerChain = function _getHandlerChain (req, res) {
|
||||
Server.prototype._getHandlerChain = function _getHandlerChain (req) {
|
||||
assert.ok(req)
|
||||
|
||||
fireDTraceProbe(req, res)
|
||||
|
||||
const self = this
|
||||
const routes = this.routes
|
||||
let route
|
||||
|
@ -837,7 +863,7 @@ Server.prototype._getHandlerChain = function _getHandlerChain (req, res) {
|
|||
const keys = this._sortedRouteKeys()
|
||||
let fallbackHandler = [noSuffixHandler]
|
||||
// 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++) {
|
||||
const suffix = keys[i]
|
||||
|
@ -884,7 +910,7 @@ Server.prototype._mount = function (op, name, argv, notDN) {
|
|||
backend = argv[0]
|
||||
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()
|
||||
argv.slice(index).forEach(function (a) {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
const querystring = require('querystring')
|
||||
const url = require('url')
|
||||
const dn = require('./dn')
|
||||
const { DN } = require('@ldapjs/dn')
|
||||
const filter = require('@ldapjs/filter')
|
||||
|
||||
module.exports = {
|
||||
|
@ -38,7 +38,7 @@ module.exports = {
|
|||
|
||||
if (u.pathname) {
|
||||
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) {
|
||||
|
|
16
package.json
16
package.json
|
@ -10,16 +10,14 @@
|
|||
"url": "git://github.com/ldapjs/node-ldapjs.git"
|
||||
},
|
||||
"main": "lib/index.js",
|
||||
"directories": {
|
||||
"lib": "./lib"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ldapjs/asn1": "1.2.0",
|
||||
"@ldapjs/controls": "^1.0.0",
|
||||
"@ldapjs/filter": "1.0.0",
|
||||
"@ldapjs/asn1": "2.0.0-rc.4",
|
||||
"@ldapjs/attribute": "1.0.0-rc.5",
|
||||
"@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",
|
||||
"abstract-logging": "^2.0.0",
|
||||
"assert-plus": "^1.0.0",
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
module.exports = {
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest'
|
||||
},
|
||||
|
||||
rules: {
|
||||
'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 getPort = require('get-port')
|
||||
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 { Attribute, Change } = ldap
|
||||
|
||||
const {
|
||||
SearchRequest,
|
||||
SearchResultEntry,
|
||||
SearchResultReference,
|
||||
SearchResultDone
|
||||
} = messages
|
||||
|
||||
const SUFFIX = 'dc=test'
|
||||
const LDAP_CONNECT_TIMEOUT = process.env.LDAP_CONNECT_TIMEOUT || 0
|
||||
|
@ -44,7 +54,7 @@ tap.beforeEach((t) => {
|
|||
|
||||
// LDAP whoami
|
||||
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()
|
||||
return next()
|
||||
})
|
||||
|
@ -88,21 +98,21 @@ tap.beforeEach((t) => {
|
|||
if (req.dn.equals('cn=ref,' + SUFFIX)) {
|
||||
res.send(res.createSearchReference('ldap://localhost'))
|
||||
} 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({
|
||||
objectName: req.dn,
|
||||
attributes: {
|
||||
'foo;binary': 'wr0gKyDCvCA9IMK+',
|
||||
gb18030: Buffer.from([0xB5, 0xE7, 0xCA, 0xD3, 0xBB, 0xFA]),
|
||||
objectclass: 'binary'
|
||||
}
|
||||
attributes
|
||||
}))
|
||||
} else {
|
||||
const attributes = []
|
||||
attributes.push(new Attribute({ type: 'cn', values: ['unit', 'test'] }))
|
||||
attributes.push(new Attribute({ type: 'SN', values: ['testy'] }))
|
||||
const e = res.createSearchEntry({
|
||||
objectName: req.dn,
|
||||
attributes: {
|
||||
cn: ['unit', 'test'],
|
||||
SN: 'testy'
|
||||
}
|
||||
attributes
|
||||
})
|
||||
res.send(e)
|
||||
res.send(e)
|
||||
|
@ -142,13 +152,14 @@ tap.beforeEach((t) => {
|
|||
end = (end > max || end < min) ? max : end
|
||||
let i
|
||||
for (i = start; i < end; i++) {
|
||||
res.send({
|
||||
dn: util.format('o=%d, cn=paged', i),
|
||||
attributes: {
|
||||
res.send(new SearchResultEntry({
|
||||
messageId: res.id,
|
||||
entry: `o=${i},cn=paged`,
|
||||
attributes: Attribute.fromObject({
|
||||
o: [i],
|
||||
objectclass: ['pagedResult']
|
||||
}
|
||||
})
|
||||
})
|
||||
}))
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
@ -156,39 +167,40 @@ tap.beforeEach((t) => {
|
|||
let cookie = null
|
||||
let pageSize = 0
|
||||
req.controls.forEach(function (control) {
|
||||
if (control.type === ldap.PagedResultsControl.OID) {
|
||||
if (control.type === controls.PagedResultsControl.OID) {
|
||||
pageSize = control.value.size
|
||||
cookie = control.value.cookie
|
||||
}
|
||||
})
|
||||
|
||||
if (cookie && Buffer.isBuffer(cookie)) {
|
||||
// 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 {
|
||||
if (!cookie || Buffer.isBuffer(cookie) === false) {
|
||||
// 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) {
|
||||
const min = 0
|
||||
const max = 100
|
||||
|
@ -484,7 +496,7 @@ tap.test('add success', function (t) {
|
|||
const attrs = [
|
||||
new Attribute({
|
||||
type: 'cn',
|
||||
vals: ['test']
|
||||
values: ['test']
|
||||
})
|
||||
]
|
||||
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) {
|
||||
const { BerReader } = require('@ldapjs/asn1')
|
||||
const dn = `cn=add, ${SUFFIX}`
|
||||
const dn = `cn=add,${SUFFIX}`
|
||||
const attribute = 'thumbnailPhoto'
|
||||
const binary = 0xa5
|
||||
const entry = {
|
||||
|
@ -519,7 +531,7 @@ tap.test('add buffer', function (t) {
|
|||
const write = t.context.client._socket.write
|
||||
t.context.client._socket.write = (data, encoding, cb) => {
|
||||
const reader = new BerReader(data)
|
||||
t.equal(data.byteLength, 49)
|
||||
t.equal(data.byteLength, 48)
|
||||
t.ok(reader.readSequence())
|
||||
t.equal(reader.readInt(), 0x1)
|
||||
t.equal(reader.readSequence(), 0x68)
|
||||
|
@ -621,7 +633,7 @@ tap.test('modify success', function (t) {
|
|||
type: 'Replace',
|
||||
modification: new Attribute({
|
||||
type: 'cn',
|
||||
vals: ['test']
|
||||
values: ['test']
|
||||
})
|
||||
})
|
||||
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
|
||||
tap.test('can delete attributes', function (t) {
|
||||
const change = new Change({
|
||||
type: 'Delete',
|
||||
modification: { cn: null }
|
||||
modification: new Attribute({ type: 'cn', values: [null] })
|
||||
})
|
||||
t.context.client.modify('cn=modify,' + SUFFIX, change, function (err, res) {
|
||||
t.error(err)
|
||||
|
@ -667,7 +664,7 @@ tap.test('modify array success', function (t) {
|
|||
operation: 'Replace',
|
||||
modification: new Attribute({
|
||||
type: 'cn',
|
||||
vals: ['test']
|
||||
values: ['test']
|
||||
})
|
||||
}),
|
||||
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) {
|
||||
t.context.client.modifyDN('cn=old, ' + SUFFIX, 'cn=new', function (err, res) {
|
||||
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) {
|
||||
const { BerReader, BerWriter } = require('@ldapjs/asn1')
|
||||
const ModifyDNRequest = require('../lib/messages/moddn_request')
|
||||
const ber = new BerWriter()
|
||||
const { ModifyDnRequest } = messages
|
||||
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 newRdn = entry.replace(/(.*?),.*/, '$1')
|
||||
const deleteOldRdn = true
|
||||
const req = new ModifyDNRequest({
|
||||
entry: entry,
|
||||
deleteOldRdn: deleteOldRdn,
|
||||
controls: []
|
||||
|
||||
const req = new ModifyDnRequest({
|
||||
entry,
|
||||
deleteOldRdn,
|
||||
newRdn,
|
||||
newSuperior
|
||||
})
|
||||
req.newRdn = newRdn
|
||||
req.newSuperior = newSuperior
|
||||
req._toBer(ber)
|
||||
const reader = new BerReader(ber.buffer)
|
||||
t.equal(reader.readString(), entry)
|
||||
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.equal(req.entry.toString(), 'cn=Test User,ou=A Long OU,ou=Another Long OU,ou=Another Long OU,dc=acompany,DC=io')
|
||||
t.equal(req.newRdn.toString(), 'cn=Test User')
|
||||
t.equal(req.deleteOldRdn, true)
|
||||
t.equal(req.newSuperior.toString(), 'ou=A New Long OU,ou=Another New Long OU,ou=An OU,dc=acompany,dc=io')
|
||||
|
||||
t.end()
|
||||
})
|
||||
|
||||
tap.test('search basic', function (t) {
|
||||
const { SearchResultEntry, SearchResultDone } = messages
|
||||
|
||||
t.context.client.search('cn=test, ' + SUFFIX, '(objectclass=*)', function (err, res) {
|
||||
t.error(err)
|
||||
t.ok(res)
|
||||
let gotEntry = 0
|
||||
res.on('searchEntry', function (entry) {
|
||||
t.ok(entry)
|
||||
t.ok(entry instanceof ldap.SearchEntry)
|
||||
t.ok(entry instanceof SearchResultEntry)
|
||||
t.equal(entry.dn.toString(), 'cn=test,' + SUFFIX)
|
||||
t.ok(entry.attributes)
|
||||
t.ok(entry.attributes.length)
|
||||
t.equal(entry.attributes[0].type, 'cn')
|
||||
t.equal(entry.attributes[1].type, 'SN')
|
||||
t.ok(entry.object)
|
||||
gotEntry++
|
||||
})
|
||||
res.on('error', function (err) {
|
||||
|
@ -777,7 +753,7 @@ tap.test('search basic', function (t) {
|
|||
})
|
||||
res.on('end', function (res) {
|
||||
t.ok(res)
|
||||
t.ok(res instanceof ldap.SearchResponse)
|
||||
t.ok(res instanceof SearchResultDone)
|
||||
t.equal(res.status, 0)
|
||||
t.equal(gotEntry, 2)
|
||||
t.end()
|
||||
|
@ -844,7 +820,7 @@ tap.test('search paged', { timeout: 10000 }, function (t) {
|
|||
t2.error(err)
|
||||
res.on('searchEntry', entryListener)
|
||||
res.on('searchRequest', (searchRequest) => {
|
||||
t2.ok(searchRequest instanceof ldap.SearchRequest)
|
||||
t2.ok(searchRequest instanceof SearchRequest)
|
||||
if (currentSearchRequest === null) {
|
||||
t2.equal(countPages, 0)
|
||||
}
|
||||
|
@ -855,7 +831,7 @@ tap.test('search paged', { timeout: 10000 }, function (t) {
|
|||
res.on('end', function (result) {
|
||||
t2.equal(countEntries, 1000)
|
||||
t2.equal(countPages, 10)
|
||||
t2.equal(result.messageID, currentSearchRequest.messageID)
|
||||
t2.equal(result.messageId, currentSearchRequest.messageId)
|
||||
t2.end()
|
||||
})
|
||||
|
||||
|
@ -871,7 +847,7 @@ tap.test('search paged', { timeout: 10000 }, function (t) {
|
|||
function pageListener (result) {
|
||||
countPages += 1
|
||||
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++
|
||||
// cancel after 9 to verify callback usage
|
||||
if (countPages === 9) {
|
||||
// another page should never be encountered
|
||||
// another page should never be encountered
|
||||
res.removeListener('page', pageListener)
|
||||
.on('page', t2.fail.bind(null, 'unexpected page'))
|
||||
return cb(new Error())
|
||||
|
@ -995,7 +971,12 @@ tap.test('search paged', { timeout: 10000 }, function (t) {
|
|||
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) {
|
||||
let preventry = null
|
||||
const sssrcontrol = new ldap.ServerSideSortingRequestControl(
|
||||
|
@ -1025,6 +1006,7 @@ tap.test('search - sssvlv', { timeout: 10000 }, function (t) {
|
|||
})
|
||||
})
|
||||
})
|
||||
|
||||
t.test('ssv - desc', function (t2) {
|
||||
let preventry = null
|
||||
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) {
|
||||
// This test is disabled.
|
||||
// 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()
|
||||
})
|
||||
|
||||
|
@ -1158,7 +1142,7 @@ tap.test('search referral', function (t) {
|
|||
res.on('searchReference', function (referral) {
|
||||
gotReferral = true
|
||||
t.ok(referral)
|
||||
t.ok(referral instanceof ldap.SearchReference)
|
||||
t.ok(referral instanceof SearchResultReference)
|
||||
t.ok(referral.uris)
|
||||
t.ok(referral.uris.length)
|
||||
})
|
||||
|
@ -1167,7 +1151,7 @@ tap.test('search referral', function (t) {
|
|||
})
|
||||
res.on('end', function (res) {
|
||||
t.ok(res)
|
||||
t.ok(res instanceof ldap.SearchResponse)
|
||||
t.ok(res instanceof SearchResultDone)
|
||||
t.equal(res.status, 0)
|
||||
t.equal(gotEntry, 0)
|
||||
t.ok(gotReferral)
|
||||
|
@ -1184,14 +1168,13 @@ tap.test('search rootDSE', function (t) {
|
|||
t.ok(entry)
|
||||
t.equal(entry.dn.toString(), '')
|
||||
t.ok(entry.attributes)
|
||||
t.ok(entry.object)
|
||||
})
|
||||
res.on('error', function (err) {
|
||||
t.fail(err)
|
||||
})
|
||||
res.on('end', function (res) {
|
||||
t.ok(res)
|
||||
t.ok(res instanceof ldap.SearchResponse)
|
||||
t.ok(res instanceof SearchResultDone)
|
||||
t.equal(res.status, 0)
|
||||
t.end()
|
||||
})
|
||||
|
@ -1204,12 +1187,16 @@ tap.test('search empty attribute', function (t) {
|
|||
t.ok(res)
|
||||
let gotEntry = 0
|
||||
res.on('searchEntry', function (entry) {
|
||||
const obj = entry.toObject()
|
||||
t.equal('dc=empty', obj.dn)
|
||||
t.ok(obj.member)
|
||||
t.equal(obj.member.length, 0)
|
||||
t.ok(obj['member;range=0-1'])
|
||||
t.ok(obj['member;range=0-1'].length)
|
||||
const obj = entry.pojo
|
||||
t.equal('dc=empty', obj.objectName)
|
||||
|
||||
const member = entry.attributes[0]
|
||||
t.ok(member)
|
||||
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++
|
||||
})
|
||||
res.on('error', function (err) {
|
||||
|
@ -1217,7 +1204,7 @@ tap.test('search empty attribute', function (t) {
|
|||
})
|
||||
res.on('end', function (res) {
|
||||
t.ok(res)
|
||||
t.ok(res instanceof ldap.SearchResponse)
|
||||
t.ok(res instanceof SearchResultDone)
|
||||
t.equal(res.status, 0)
|
||||
t.equal(gotEntry, 1)
|
||||
t.end()
|
||||
|
@ -1234,12 +1221,12 @@ tap.test('GH-21 binary attributes', function (t) {
|
|||
const expect2 = Buffer.from([0xB5, 0xE7, 0xCA, 0xD3, 0xBB, 0xFA])
|
||||
res.on('searchEntry', function (entry) {
|
||||
t.ok(entry)
|
||||
t.ok(entry instanceof ldap.SearchEntry)
|
||||
t.ok(entry instanceof SearchResultEntry)
|
||||
t.equal(entry.dn.toString(), 'cn=bin,' + SUFFIX)
|
||||
t.ok(entry.attributes)
|
||||
t.ok(entry.attributes.length)
|
||||
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'),
|
||||
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)
|
||||
for (let i = 0; i < expect2.length; i++) { t.equal(expect2[i], entry.attributes[1].buffers[0][i]) }
|
||||
|
||||
t.ok(entry.object)
|
||||
gotEntry++
|
||||
})
|
||||
res.on('error', function (err) {
|
||||
|
@ -1256,7 +1242,7 @@ tap.test('GH-21 binary attributes', function (t) {
|
|||
})
|
||||
res.on('end', function (res) {
|
||||
t.ok(res)
|
||||
t.ok(res instanceof ldap.SearchResponse)
|
||||
t.ok(res instanceof SearchResultDone)
|
||||
t.equal(res.status, 0)
|
||||
t.equal(gotEntry, 1)
|
||||
t.end()
|
||||
|
@ -1267,7 +1253,7 @@ tap.test('GH-21 binary attributes', function (t) {
|
|||
tap.test('GH-23 case insensitive attribute filtering', function (t) {
|
||||
const opts = {
|
||||
filter: '(objectclass=*)',
|
||||
attributes: ['Cn']
|
||||
attributes: ['@Cn']
|
||||
}
|
||||
t.context.client.search('cn=test, ' + SUFFIX, opts, function (err, res) {
|
||||
t.error(err)
|
||||
|
@ -1275,12 +1261,11 @@ tap.test('GH-23 case insensitive attribute filtering', function (t) {
|
|||
let gotEntry = 0
|
||||
res.on('searchEntry', function (entry) {
|
||||
t.ok(entry)
|
||||
t.ok(entry instanceof ldap.SearchEntry)
|
||||
t.ok(entry instanceof SearchResultEntry)
|
||||
t.equal(entry.dn.toString(), 'cn=test,' + SUFFIX)
|
||||
t.ok(entry.attributes)
|
||||
t.ok(entry.attributes.length)
|
||||
t.equal(entry.attributes[0].type, 'cn')
|
||||
t.ok(entry.object)
|
||||
gotEntry++
|
||||
})
|
||||
res.on('error', function (err) {
|
||||
|
@ -1288,7 +1273,7 @@ tap.test('GH-23 case insensitive attribute filtering', function (t) {
|
|||
})
|
||||
res.on('end', function (res) {
|
||||
t.ok(res)
|
||||
t.ok(res instanceof ldap.SearchResponse)
|
||||
t.ok(res instanceof SearchResultDone)
|
||||
t.equal(res.status, 0)
|
||||
t.equal(gotEntry, 2)
|
||||
t.end()
|
||||
|
@ -1307,13 +1292,12 @@ tap.test('GH-24 attribute selection of *', function (t) {
|
|||
let gotEntry = 0
|
||||
res.on('searchEntry', function (entry) {
|
||||
t.ok(entry)
|
||||
t.ok(entry instanceof ldap.SearchEntry)
|
||||
t.ok(entry instanceof SearchResultEntry)
|
||||
t.equal(entry.dn.toString(), 'cn=test,' + SUFFIX)
|
||||
t.ok(entry.attributes)
|
||||
t.ok(entry.attributes.length)
|
||||
t.equal(entry.attributes[0].type, 'cn')
|
||||
t.equal(entry.attributes[1].type, 'SN')
|
||||
t.ok(entry.object)
|
||||
gotEntry++
|
||||
})
|
||||
res.on('error', function (err) {
|
||||
|
@ -1321,7 +1305,7 @@ tap.test('GH-24 attribute selection of *', function (t) {
|
|||
})
|
||||
res.on('end', function (res) {
|
||||
t.ok(res)
|
||||
t.ok(res instanceof ldap.SearchResponse)
|
||||
t.ok(res instanceof SearchResultDone)
|
||||
t.equal(res.status, 0)
|
||||
t.equal(gotEntry, 2)
|
||||
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) {
|
||||
getPort().then(function (unusedPortNumber) {
|
||||
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 { getSock, uuid } = require('./utils')
|
||||
const { SearchResultEntry } = require('@ldapjs/messages')
|
||||
const Attribute = require('@ldapjs/attribute')
|
||||
const ldap = require('../lib')
|
||||
|
||||
function search (t, options, callback) {
|
||||
|
@ -40,18 +42,20 @@ tap.beforeEach((t) => {
|
|||
})
|
||||
|
||||
server.search(suffix, function (req, res) {
|
||||
const entry = {
|
||||
dn: 'cn=foo, ' + suffix,
|
||||
attributes: {
|
||||
const entry = new SearchResultEntry({
|
||||
entry: 'cn=foo,' + suffix,
|
||||
attributes: Attribute.fromObject({
|
||||
objectclass: ['person', 'top'],
|
||||
cn: 'Pogo Stick',
|
||||
sn: 'Stick',
|
||||
givenname: 'ogo',
|
||||
mail: uuid() + '@pogostick.org'
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
if (req.filter.matches(entry.attributes)) { res.send(entry) }
|
||||
if (req.filter.matches(entry.attributes)) {
|
||||
res.send(entry)
|
||||
}
|
||||
|
||||
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.
|
||||
const filter =
|
||||
'(|(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 = {
|
||||
filter: 'cn=*ogo*',
|
||||
scope: 'one',
|
||||
attributes: 'dn'
|
||||
attributes: '@dn'
|
||||
}
|
||||
return search(t, searchOpts)
|
||||
})
|
||||
|
|
|
@ -109,7 +109,7 @@ tap.test('#fetch', t => {
|
|||
t.test('returns handler for fetched message', async t => {
|
||||
const tracker = messageTrackerFactory({ id: 'foo', parser: {} })
|
||||
tracker.track({}, handler)
|
||||
const fetched = tracker.fetch(1)
|
||||
const { callback: fetched } = tracker.fetch(1)
|
||||
t.equal(fetched, handler)
|
||||
|
||||
function handler () {}
|
||||
|
@ -120,7 +120,7 @@ tap.test('#fetch', t => {
|
|||
tracker.track({}, handler)
|
||||
tracker.track({ abandon: 'message' }, () => {})
|
||||
tracker.abandon(1)
|
||||
const fetched = tracker.fetch(1)
|
||||
const { callback: fetched } = tracker.fetch(1)
|
||||
t.equal(fetched, handler)
|
||||
|
||||
function handler () {}
|
||||
|
@ -185,13 +185,13 @@ tap.test('#remove', 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 msg = {}
|
||||
tracker.track(msg, handler)
|
||||
|
||||
t.same(msg, { messageID: 1 })
|
||||
const cb = tracker.fetch(1)
|
||||
t.same(msg, { messageId: 1 })
|
||||
const { callback: cb } = tracker.fetch(1)
|
||||
t.equal(cb, 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'
|
||||
|
||||
const { test } = require('tap')
|
||||
const { Parser, LDAPMessage, LDAP_REQ_EXTENSION } = require('../../lib')
|
||||
const { Parser } = require('../../lib')
|
||||
|
||||
test('wrong protocol error', function (t) {
|
||||
const p = new Parser()
|
||||
|
@ -14,36 +14,3 @@ test('wrong protocol error', function (t) {
|
|||
// Send some bogus data to incur an error
|
||||
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()
|
||||
})
|
||||
|
||||
const anonDN = ldap.dn.parse('cn=anonymous')
|
||||
const anonDN = ldap.parseDN('cn=anonymous')
|
||||
|
||||
server.listen(t.context.sock, function () {
|
||||
t.ok(true, 'server startup')
|
||||
|
@ -276,8 +276,8 @@ tap.test('bind/unbind identity user', function (t) {
|
|||
return next()
|
||||
})
|
||||
|
||||
const anonDN = ldap.dn.parse('cn=anonymous')
|
||||
const testDN = ldap.dn.parse('cn=anotheruser')
|
||||
const anonDN = ldap.parseDN('cn=anonymous')
|
||||
const testDN = ldap.parseDN('cn=anotheruser')
|
||||
|
||||
server.listen(t.context.sock, function () {
|
||||
t.ok(true, 'server startup')
|
||||
|
@ -316,9 +316,7 @@ tap.test('strict routing', function (t) {
|
|||
vasync.pipeline({
|
||||
funcs: [
|
||||
function setup (_, cb) {
|
||||
server = ldap.createServer({
|
||||
// strictDN: true - on by default
|
||||
})
|
||||
server = ldap.createServer({})
|
||||
// invalid DNs would go to default handler
|
||||
server.search('', function (req, res, next) {
|
||||
t.ok(req.dn)
|
||||
|
@ -330,26 +328,11 @@ tap.test('strict routing', function (t) {
|
|||
server.listen(sock, function () {
|
||||
t.ok(true, 'server startup')
|
||||
clt = ldap.createClient({
|
||||
socketPath: sock,
|
||||
strictDN: false
|
||||
socketPath: sock
|
||||
})
|
||||
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) {
|
||||
clt.search(testDN, { scope: 'base' }, function (err, res) {
|
||||
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) {
|
||||
const server = ldap.createServer()
|
||||
// callback is called when the server is closed
|
||||
|
|
Loading…
Reference in New Issue