Support invalid DNs in client/server
Some LDAP implementations (mainly AD and Outlook) accept and/or output DNs that are not valid. To support interaction with these invalid DNs a strictDN flag (default: true) has been added to the client and server constructors. Setting this flag to false will allow use of non-conforming DNs. When disabling strictDN in the ldapjs client, strings which wouldn't parse into a DN can then be passed to the ldap operation methods. It also means that some methods (such as search) may return results with string-formatted DNs instead of DN objects. When disabling strictDN in the ldapjs server, incoming requests that contain invalid DNs will be routed to the default ('') handler for that operation type. It is your responsiblity to differentiate between string-type and object-type DNs in those handlers. Fix mcavage/node-ldapjs#222 Fix mcavage/node-ldapjs#146 Fix mcavage/node-ldapjs#113 Fix mcavage/node-ldapjs#104
This commit is contained in:
parent
58f58883cd
commit
408e7c9f99
|
@ -80,6 +80,18 @@ function validateControls(controls) {
|
|||
return controls;
|
||||
}
|
||||
|
||||
function ensureDN(input, strict) {
|
||||
if (dn.DN.isDN(input)) {
|
||||
return dn;
|
||||
} else if (strict) {
|
||||
return dn.parse(input);
|
||||
} else if (typeof (input) === 'string') {
|
||||
return input;
|
||||
} else {
|
||||
throw new Error('invalid DN');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue to contain LDAP requests.
|
||||
*
|
||||
|
@ -318,6 +330,7 @@ function Client(options) {
|
|||
failAfter: parseInt(rOpts.failAfter, 10) || Infinity
|
||||
};
|
||||
}
|
||||
this.strictDN = (options.strictDN !== undefined) ? options.strictDN : true;
|
||||
|
||||
this.queue = new RequestQueue({
|
||||
size: parseInt((options.queueSize || 0), 10),
|
||||
|
@ -394,7 +407,7 @@ Client.prototype.abandon = function abandon(messageID, controls, callback) {
|
|||
* @throws {TypeError} on invalid input.
|
||||
*/
|
||||
Client.prototype.add = function add(name, entry, controls, callback) {
|
||||
assert.string(name, 'name');
|
||||
assert.ok(name, 'name');
|
||||
assert.object(entry, 'entry');
|
||||
if (typeof (controls) === 'function') {
|
||||
callback = controls;
|
||||
|
@ -427,7 +440,7 @@ Client.prototype.add = function add(name, entry, controls, callback) {
|
|||
}
|
||||
|
||||
var req = new AddRequest({
|
||||
entry: dn.parse(name),
|
||||
entry: ensureDN(name, this.strictDN),
|
||||
attributes: entry,
|
||||
controls: controls
|
||||
});
|
||||
|
@ -487,7 +500,7 @@ Client.prototype.compare = function compare(name,
|
|||
value,
|
||||
controls,
|
||||
callback) {
|
||||
assert.string(name, 'name');
|
||||
assert.ok(name, 'name');
|
||||
assert.string(attr, 'attr');
|
||||
assert.string(value, 'value');
|
||||
if (typeof (controls) === 'function') {
|
||||
|
@ -499,7 +512,7 @@ Client.prototype.compare = function compare(name,
|
|||
assert.func(callback, 'callback');
|
||||
|
||||
var req = new CompareRequest({
|
||||
entry: dn.parse(name),
|
||||
entry: ensureDN(name, this.strictDN),
|
||||
attribute: attr,
|
||||
value: value,
|
||||
controls: controls
|
||||
|
@ -523,7 +536,7 @@ Client.prototype.compare = function compare(name,
|
|||
* @throws {TypeError} on invalid input.
|
||||
*/
|
||||
Client.prototype.del = function del(name, controls, callback) {
|
||||
assert.string(name, 'name');
|
||||
assert.ok(name, 'name');
|
||||
if (typeof (controls) === 'function') {
|
||||
callback = controls;
|
||||
controls = [];
|
||||
|
@ -533,7 +546,7 @@ Client.prototype.del = function del(name, controls, callback) {
|
|||
assert.func(callback, 'callback');
|
||||
|
||||
var req = new DeleteRequest({
|
||||
entry: dn.parse(name),
|
||||
entry: ensureDN(name, this.strictDN),
|
||||
controls: controls
|
||||
});
|
||||
|
||||
|
@ -596,7 +609,7 @@ Client.prototype.exop = function exop(name, value, controls, callback) {
|
|||
* @throws {TypeError} on invalid input.
|
||||
*/
|
||||
Client.prototype.modify = function modify(name, change, controls, callback) {
|
||||
assert.string(name, 'name');
|
||||
assert.ok(name, 'name');
|
||||
assert.object(change, 'change');
|
||||
|
||||
var changes = [];
|
||||
|
@ -651,7 +664,7 @@ Client.prototype.modify = function modify(name, change, controls, callback) {
|
|||
assert.func(callback, 'callback');
|
||||
|
||||
var req = new ModifyRequest({
|
||||
object: dn.parse(name),
|
||||
object: ensureDN(name, this.strictDN),
|
||||
changes: changes,
|
||||
controls: controls
|
||||
});
|
||||
|
@ -678,20 +691,18 @@ Client.prototype.modifyDN = function modifyDN(name,
|
|||
newName,
|
||||
controls,
|
||||
callback) {
|
||||
if (typeof (name) !== 'string')
|
||||
throw new TypeError('name (string) required');
|
||||
if (typeof (newName) !== 'string')
|
||||
throw new TypeError('newName (string) required');
|
||||
assert.ok(name, 'name');
|
||||
assert.string(newName, 'newName');
|
||||
if (typeof (controls) === 'function') {
|
||||
callback = controls;
|
||||
controls = [];
|
||||
} else {
|
||||
controls = validateControls(controls);
|
||||
}
|
||||
if (typeof (callback) !== 'function')
|
||||
throw new TypeError('callback (function) required');
|
||||
assert.func(callback);
|
||||
|
||||
var DN = dn.parse(name);
|
||||
var DN = ensureDN(name);
|
||||
// TODO: is non-strict handling desired here?
|
||||
var newDN = dn.parse(newName);
|
||||
|
||||
var req = new ModifyDNRequest({
|
||||
|
@ -739,8 +750,7 @@ Client.prototype.search = function search(base,
|
|||
controls,
|
||||
callback,
|
||||
_bypass) {
|
||||
if (typeof (base) !== 'string' && !(base instanceof dn.DN))
|
||||
throw new TypeError('base (string) required');
|
||||
assert.ok(base, 'base');
|
||||
if (Array.isArray(options) || (options instanceof Control)) {
|
||||
controls = options;
|
||||
options = {};
|
||||
|
@ -762,15 +772,13 @@ Client.prototype.search = function search(base,
|
|||
} else if (!filters.isFilter(options.filter)) {
|
||||
throw new TypeError('options.filter (Filter) required');
|
||||
}
|
||||
|
||||
if (typeof (controls) === 'function') {
|
||||
callback = controls;
|
||||
controls = [];
|
||||
} else {
|
||||
controls = validateControls(controls);
|
||||
}
|
||||
if (typeof (callback) !== 'function')
|
||||
throw new TypeError('callback (function) required');
|
||||
assert.func(callback, 'callback');
|
||||
|
||||
if (options.attributes) {
|
||||
if (!Array.isArray(options.attributes)) {
|
||||
|
@ -783,9 +791,10 @@ Client.prototype.search = function search(base,
|
|||
}
|
||||
|
||||
var self = this;
|
||||
var baseDN = ensureDN(base, this.strictDN);
|
||||
function sendRequest(ctrls, emitter, cb) {
|
||||
var req = new SearchRequest({
|
||||
baseObject: typeof (base) === 'string' ? dn.parse(base) : base,
|
||||
baseObject: baseDN,
|
||||
scope: options.scope || 'base',
|
||||
filter: options.filter,
|
||||
derefAliases: options.derefAliases || Protocol.NEVER_DEREF_ALIASES,
|
||||
|
|
15
lib/dn.js
15
lib/dn.js
|
@ -218,6 +218,7 @@ DN.prototype.toString = function () {
|
|||
return _dn.join(this.rdnSpaced ? ', ' : ',');
|
||||
};
|
||||
|
||||
|
||||
DN.prototype.spaced = function (spaces) {
|
||||
this.rdnSpaced = (spaces === false) ? false : true;
|
||||
return this;
|
||||
|
@ -357,6 +358,20 @@ DN.prototype.unshift = function (rdn) {
|
|||
};
|
||||
|
||||
|
||||
DN.isDN = function (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
|
||||
|
||||
|
|
|
@ -3,19 +3,9 @@
|
|||
var assert = require('assert');
|
||||
var util = require('util');
|
||||
|
||||
var asn1 = require('asn1');
|
||||
|
||||
var LDAPMessage = require('./message');
|
||||
var LDAPResult = require('./result');
|
||||
|
||||
var dn = require('../dn');
|
||||
var Attribute = require('../attribute');
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
///--- Globals
|
||||
|
||||
var Ber = asn1.Ber;
|
||||
|
||||
|
||||
|
||||
///--- API
|
||||
|
|
|
@ -3,19 +3,16 @@
|
|||
var assert = require('assert');
|
||||
var util = require('util');
|
||||
|
||||
var asn1 = require('asn1');
|
||||
|
||||
var LDAPMessage = require('./message');
|
||||
var LDAPResult = require('./result');
|
||||
|
||||
var dn = require('../dn');
|
||||
var Attribute = require('../attribute');
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var Ber = asn1.Ber;
|
||||
|
||||
var isDN = dn.DN.isDN;
|
||||
|
||||
|
||||
///--- API
|
||||
|
@ -24,8 +21,10 @@ function AddRequest(options) {
|
|||
if (options) {
|
||||
if (typeof (options) !== 'object')
|
||||
throw new TypeError('options must be an object');
|
||||
if (options.entry && !(options.entry instanceof dn.DN))
|
||||
throw new TypeError('options.entry must be a DN');
|
||||
if (options.entry &&
|
||||
!(isDN(options.entry) || typeof (options.entry) === 'string')) {
|
||||
throw new TypeError('options.entry must be a DN or string');
|
||||
}
|
||||
if (options.attributes) {
|
||||
if (!Array.isArray(options.attributes))
|
||||
throw new TypeError('options.attributes must be [Attribute]');
|
||||
|
@ -55,7 +54,7 @@ module.exports = AddRequest;
|
|||
AddRequest.prototype._parse = function (ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
this.entry = dn.parse(ber.readString());
|
||||
this.entry = ber.readString();
|
||||
|
||||
ber.readSequence();
|
||||
|
||||
|
|
|
@ -6,16 +6,11 @@ var util = require('util');
|
|||
var asn1 = require('asn1');
|
||||
|
||||
var LDAPMessage = require('./message');
|
||||
var LDAPResult = require('./result');
|
||||
|
||||
|
||||
var dn = require('../dn');
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
///--- Globals
|
||||
|
||||
var Ber = asn1.Ber;
|
||||
|
||||
var LDAP_BIND_SIMPLE = 'simple';
|
||||
var LDAP_BIND_SASL = 'sasl';
|
||||
|
||||
|
@ -49,7 +44,7 @@ BindRequest.prototype._parse = function (ber) {
|
|||
assert.ok(ber);
|
||||
|
||||
this.version = ber.readInt();
|
||||
this.name = dn.parse(ber.readString());
|
||||
this.name = ber.readString();
|
||||
|
||||
var t = ber.peek();
|
||||
|
||||
|
|
|
@ -4,21 +4,26 @@ var assert = require('assert');
|
|||
var util = require('util');
|
||||
|
||||
var LDAPMessage = require('./message');
|
||||
var LDAPResult = require('./result');
|
||||
|
||||
var dn = require('../dn');
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var isDN = dn.DN.isDN;
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
function CompareRequest(options) {
|
||||
if (options) {
|
||||
if (typeof (options) !== 'object')
|
||||
throw new TypeError('options must be an object');
|
||||
if (options.entry && !(options.entry instanceof dn.DN))
|
||||
throw new TypeError('options.entry must be a DN');
|
||||
if (options.entry &&
|
||||
!(isDN(options.entry) || typeof (options.entry) === 'string')) {
|
||||
throw new TypeError('options.entry must be a DN or string');
|
||||
}
|
||||
if (options.attribute && typeof (options.attribute) !== 'string')
|
||||
throw new TypeError('options.attribute must be a string');
|
||||
if (options.value && typeof (options.value) !== 'string')
|
||||
|
@ -36,9 +41,7 @@ function CompareRequest(options) {
|
|||
|
||||
var self = this;
|
||||
this.__defineGetter__('type', function () { return 'CompareRequest'; });
|
||||
this.__defineGetter__('_dn', function () {
|
||||
return self.entry ? self.entry.toString() : '';
|
||||
});
|
||||
this.__defineGetter__('_dn', function () { return self.entry; });
|
||||
}
|
||||
util.inherits(CompareRequest, LDAPMessage);
|
||||
module.exports = CompareRequest;
|
||||
|
@ -47,7 +50,7 @@ module.exports = CompareRequest;
|
|||
CompareRequest.prototype._parse = function (ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
this.entry = dn.parse(ber.readString());
|
||||
this.entry = ber.readString();
|
||||
|
||||
ber.readSequence();
|
||||
this.attribute = ber.readString().toLowerCase();
|
||||
|
|
|
@ -3,19 +3,15 @@
|
|||
var assert = require('assert');
|
||||
var util = require('util');
|
||||
|
||||
var asn1 = require('asn1');
|
||||
|
||||
var LDAPMessage = require('./message');
|
||||
var LDAPResult = require('./result');
|
||||
|
||||
var dn = require('../dn');
|
||||
var Attribute = require('../attribute');
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var Ber = asn1.Ber;
|
||||
|
||||
var isDN = dn.DN.isDN;
|
||||
|
||||
|
||||
///--- API
|
||||
|
@ -24,8 +20,10 @@ function DeleteRequest(options) {
|
|||
if (options) {
|
||||
if (typeof (options) !== 'object')
|
||||
throw new TypeError('options must be an object');
|
||||
if (options.entry && !(options.entry instanceof dn.DN))
|
||||
throw new TypeError('options.entry must be a DN');
|
||||
if (options.entry &&
|
||||
!(isDN(options.entry) || typeof (options.entry) === 'string')) {
|
||||
throw new TypeError('options.entry must be a DN or string');
|
||||
}
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
|
@ -46,7 +44,7 @@ module.exports = DeleteRequest;
|
|||
DeleteRequest.prototype._parse = function (ber, length) {
|
||||
assert.ok(ber);
|
||||
|
||||
this.entry = dn.parse(ber.buffer.slice(0, length).toString('utf8'));
|
||||
this.entry = ber.buffer.slice(0, length).toString('utf8');
|
||||
ber._offset += ber.length;
|
||||
|
||||
return true;
|
||||
|
|
|
@ -3,22 +3,11 @@
|
|||
var assert = require('assert');
|
||||
var util = require('util');
|
||||
|
||||
var asn1 = require('asn1');
|
||||
|
||||
var LDAPMessage = require('./message');
|
||||
var LDAPResult = require('./result');
|
||||
|
||||
var dn = require('../dn');
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var Ber = asn1.Ber;
|
||||
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
function ExtendedRequest(options) {
|
||||
|
|
|
@ -3,19 +3,14 @@
|
|||
var assert = require('assert');
|
||||
var util = require('util');
|
||||
|
||||
var asn1 = require('asn1');
|
||||
|
||||
var LDAPMessage = require('./message');
|
||||
var LDAPResult = require('./result');
|
||||
|
||||
var dn = require('../dn');
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
var dn = require('../dn');
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var Ber = asn1.Ber;
|
||||
var isDN = dn.DN.isDN;
|
||||
|
||||
|
||||
///--- API
|
||||
|
@ -24,14 +19,16 @@ function ModifyDNRequest(options) {
|
|||
if (options) {
|
||||
if (typeof (options) !== 'object')
|
||||
throw new TypeError('options must be an object');
|
||||
if (options.entry && !(options.entry instanceof dn.DN))
|
||||
throw new TypeError('options.entry must be a DN');
|
||||
if (options.newRdn && !(options.newRdn instanceof dn.DN))
|
||||
if (options.entry &&
|
||||
!(isDN(options.entry) || typeof (options.entry) === 'string')) {
|
||||
throw new TypeError('options.entry must be a DN or string');
|
||||
}
|
||||
if (options.newRdn && !isDN(options.newRdn))
|
||||
throw new TypeError('options.newRdn must be a DN');
|
||||
if (options.deleteOldRdn !== undefined &&
|
||||
typeof (options.deleteOldRdn) !== 'boolean')
|
||||
throw new TypeError('options.deleteOldRdn must be a boolean');
|
||||
if (options.newSuperior && !(options.newSuperior instanceof dn.DN))
|
||||
if (options.newSuperior && !isDN(options.newSuperior))
|
||||
throw new TypeError('options.newSuperior must be a DN');
|
||||
|
||||
} else {
|
||||
|
@ -57,7 +54,7 @@ module.exports = ModifyDNRequest;
|
|||
ModifyDNRequest.prototype._parse = function (ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
this.entry = dn.parse(ber.readString());
|
||||
this.entry = ber.readString();
|
||||
this.newRdn = dn.parse(ber.readString());
|
||||
this.deleteOldRdn = ber.readBoolean();
|
||||
if (ber.peek() === 0x80)
|
||||
|
|
|
@ -4,8 +4,6 @@ var assert = require('assert');
|
|||
var util = require('util');
|
||||
|
||||
var LDAPMessage = require('./message');
|
||||
var LDAPResult = require('./result');
|
||||
|
||||
var dn = require('../dn');
|
||||
var Attribute = require('../attribute');
|
||||
var Change = require('../change');
|
||||
|
@ -13,14 +11,21 @@ var Protocol = require('../protocol');
|
|||
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
var isDN = dn.DN.isDN;
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
function ModifyRequest(options) {
|
||||
if (options) {
|
||||
if (typeof (options) !== 'object')
|
||||
throw new TypeError('options must be an object');
|
||||
if (options.object && !(options.object instanceof dn.DN))
|
||||
throw new TypeError('options.object must be a DN');
|
||||
if (options.object &&
|
||||
!(isDN(options.object) || typeof (options.object) === 'string')) {
|
||||
throw new TypeError('options.object must be a DN or string');
|
||||
}
|
||||
if (options.attributes) {
|
||||
if (!Array.isArray(options.attributes))
|
||||
throw new TypeError('options.attributes must be [Attribute]');
|
||||
|
@ -50,7 +55,7 @@ module.exports = ModifyRequest;
|
|||
ModifyRequest.prototype._parse = function (ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
this.object = dn.parse(ber.readString());
|
||||
this.object = ber.readString();
|
||||
|
||||
ber.readSequence();
|
||||
var end = ber.offset + ber.length;
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
// Copyright 2014 Joyent, Inc. All rights reserved.
|
||||
|
||||
|
||||
function NoOpResponse(options) {
|
||||
if (!options)
|
||||
options = {};
|
||||
if (typeof (options) !== 'object')
|
||||
throw new TypeError('options must be an object');
|
||||
|
||||
options.protocolOp = 0;
|
||||
LDAPMessage.call(this, options);
|
||||
this.__defineGetter__('type', function () { return 'NoOpResponse'; });
|
||||
}
|
||||
util.inherits(NoOpResponse, LDAPMessage);
|
||||
module.exports = NoOpResponse;
|
||||
|
||||
NoOpResponse.prototype.end = function () {};
|
||||
|
||||
NoOpResponse.prototype._json = function (j) { return j; };
|
|
@ -7,7 +7,6 @@ var asn1 = require('asn1');
|
|||
|
||||
var LDAPMessage = require('./message');
|
||||
var LDAPResult = require('./result');
|
||||
|
||||
var dn = require('../dn');
|
||||
var filters = require('../filters');
|
||||
var Protocol = require('../protocol');
|
||||
|
@ -16,10 +15,10 @@ var Protocol = require('../protocol');
|
|||
|
||||
///--- Globals
|
||||
|
||||
var DN = dn.DN;
|
||||
var Ber = asn1.Ber;
|
||||
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
function SearchRequest(options) {
|
||||
|
@ -35,9 +34,7 @@ function SearchRequest(options) {
|
|||
|
||||
var self = this;
|
||||
this.__defineGetter__('type', function () { return 'SearchRequest'; });
|
||||
this.__defineGetter__('_dn', function () {
|
||||
return self.baseObject;
|
||||
});
|
||||
this.__defineGetter__('_dn', function () { return self.baseObject; });
|
||||
this.__defineGetter__('scope', function () {
|
||||
switch (self._scope) {
|
||||
case Protocol.SCOPE_BASE_OBJECT: return 'base';
|
||||
|
@ -67,7 +64,7 @@ function SearchRequest(options) {
|
|||
}
|
||||
});
|
||||
|
||||
this.baseObject = options.baseObject || new dn.DN([ {} ]);
|
||||
this.baseObject = options.baseObject || new DN([ {} ]);
|
||||
this.scope = options.scope || 'base';
|
||||
this.derefAliases = options.derefAliases || Protocol.NEVER_DEREF_ALIASES;
|
||||
this.sizeLimit = options.sizeLimit || 0;
|
||||
|
@ -93,7 +90,7 @@ SearchRequest.prototype.newResult = function () {
|
|||
SearchRequest.prototype._parse = function (ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
this.baseObject = dn.parse(ber.readString());
|
||||
this.baseObject = ber.readString();
|
||||
this.scope = ber.readEnumeration();
|
||||
this.derefAliases = ber.readEnumeration();
|
||||
this.sizeLimit = ber.readInt();
|
||||
|
|
|
@ -3,11 +3,7 @@
|
|||
var assert = require('assert');
|
||||
var util = require('util');
|
||||
|
||||
var asn1 = require('asn1');
|
||||
|
||||
var LDAPMessage = require('./message');
|
||||
var LDAPResult = require('./result');
|
||||
|
||||
var dn = require('../dn');
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
|
@ -15,8 +11,6 @@ var Protocol = require('../protocol');
|
|||
|
||||
///--- Globals
|
||||
|
||||
var Ber = asn1.Ber;
|
||||
|
||||
var DN = dn.DN;
|
||||
var RDN = dn.RDN;
|
||||
|
||||
|
|
|
@ -124,6 +124,45 @@ function getResponse(req) {
|
|||
}
|
||||
|
||||
|
||||
function decodeDN(req, strict) {
|
||||
assert.ok(req);
|
||||
var parse;
|
||||
if (strict) {
|
||||
parse = dn.parse;
|
||||
} else {
|
||||
parse = function (input) {
|
||||
try {
|
||||
return dn.parse(input);
|
||||
} catch (e) {
|
||||
return input;
|
||||
}
|
||||
};
|
||||
}
|
||||
switch (req.protocolOp) {
|
||||
case Protocol.LDAP_REQ_BIND:
|
||||
req.name = parse(req.name);
|
||||
break;
|
||||
case Protocol.LDAP_REQ_ADD:
|
||||
case Protocol.LDAP_REQ_COMPARE:
|
||||
case Protocol.LDAP_REQ_DELETE:
|
||||
req.entry = parse(req.entry);
|
||||
break;
|
||||
case Protocol.LDAP_REQ_MODIFY:
|
||||
req.object = parse(req.object);
|
||||
break;
|
||||
case Protocol.LDAP_REQ_MODRDN:
|
||||
req.entry = parse(req.entry);
|
||||
// TODO: handle newRdn/Superior
|
||||
break;
|
||||
case Protocol.LDAP_REQ_SEARCH:
|
||||
req.baseObject = parse(req.baseObject);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function defaultHandler(req, res, next) {
|
||||
assert.ok(req);
|
||||
assert.ok(res);
|
||||
|
@ -276,6 +315,7 @@ function Server(options) {
|
|||
|
||||
this._chain = [];
|
||||
this.log = options.log;
|
||||
this.strictDN = (options.strictDN !== undefined) ? options.strictDN : true;
|
||||
|
||||
var log = this.log;
|
||||
|
||||
|
@ -355,6 +395,9 @@ function Server(options) {
|
|||
return false;
|
||||
}
|
||||
|
||||
// parse string DNs for routing/etc
|
||||
decodeDN(req, this.strictDN);
|
||||
|
||||
res.connection = c;
|
||||
res.logId = req.logId;
|
||||
res.requestDN = req.dn;
|
||||
|
@ -808,12 +851,15 @@ Server.prototype._getHandlerChain = function _getHandlerChain(req, res) {
|
|||
assert.ok(req.dn);
|
||||
var keys = this._sortedRouteKeys();
|
||||
var fallbackHandler = [noSuffixHandler];
|
||||
// invalid DNs in non-strict mode are routed to the default handler
|
||||
var testDN = (typeof (req.dn) === 'string') ? '' : req.dn;
|
||||
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
var suffix = keys[i];
|
||||
route = routes[suffix];
|
||||
assert.ok(route.dn);
|
||||
// Match a valid route or the route wildcard ('')
|
||||
if (route.dn.equals(req.dn) || route.dn.parentOf(req.dn) || suffix === '') {
|
||||
if (route.dn.equals(testDN) || route.dn.parentOf(testDN) || suffix === '') {
|
||||
if (route[op]) {
|
||||
// We should be good to go.
|
||||
req.suffix = route.dn;
|
||||
|
|
|
@ -143,3 +143,18 @@ test('rdn spacing', function (t) {
|
|||
t.equals(dn2.spaced(false).toString(), 'cn=foo,dc=bar');
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('isDN duck-testing', function (t) {
|
||||
var valid = dn.parse('cn=foo');
|
||||
var isDN = dn.DN.isDN;
|
||||
t.notOk(isDN(null));
|
||||
t.notOk(isDN('cn=foo'));
|
||||
t.ok(isDN(valid));
|
||||
var duck = {
|
||||
rdns: [ {look: 'ma'}, {a: 'dn'} ],
|
||||
toString: function () { return 'look=ma, a=dn'; }
|
||||
};
|
||||
t.ok(isDN(duck));
|
||||
t.end();
|
||||
});
|
||||
|
|
|
@ -53,8 +53,6 @@ test('parse', function (t) {
|
|||
t.ok(req._parse(new BerReader(ber.buffer)));
|
||||
t.equal(req.version, 3);
|
||||
t.equal(req.dn.toString(), 'cn=root');
|
||||
t.ok(req.name.constructor);
|
||||
t.equal(req.name.constructor.name, 'DN');
|
||||
t.equal(req.credentials, 'secret');
|
||||
t.end();
|
||||
});
|
||||
|
|
|
@ -36,7 +36,7 @@ test('new with args', function (t) {
|
|||
value: 'testy'
|
||||
});
|
||||
t.ok(req);
|
||||
t.equal(req.dn, 'cn=foo, o=test');
|
||||
t.equal(req.dn.toString(), 'cn=foo, o=test');
|
||||
t.equal(req.attribute, 'sn');
|
||||
t.equal(req.value, 'testy');
|
||||
t.end();
|
||||
|
|
|
@ -196,3 +196,36 @@ test('route unbind', function (t) {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('non-strict route', function (t) {
|
||||
server = ldap.createServer({
|
||||
strictDN: false
|
||||
});
|
||||
sock = getSock();
|
||||
var 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(sock, function () {
|
||||
t.ok(true, 'server startup');
|
||||
var clt = ldap.createClient({
|
||||
socketPath: sock,
|
||||
strictDN: false
|
||||
});
|
||||
clt.search(testDN, {scope: 'base'}, function (err, res) {
|
||||
t.ifError(err);
|
||||
res.on('end', function () {
|
||||
clt.destroy();
|
||||
server.close();
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue