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;
|
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.
|
* Queue to contain LDAP requests.
|
||||||
*
|
*
|
||||||
|
@ -318,6 +330,7 @@ function Client(options) {
|
||||||
failAfter: parseInt(rOpts.failAfter, 10) || Infinity
|
failAfter: parseInt(rOpts.failAfter, 10) || Infinity
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
this.strictDN = (options.strictDN !== undefined) ? options.strictDN : true;
|
||||||
|
|
||||||
this.queue = new RequestQueue({
|
this.queue = new RequestQueue({
|
||||||
size: parseInt((options.queueSize || 0), 10),
|
size: parseInt((options.queueSize || 0), 10),
|
||||||
|
@ -394,7 +407,7 @@ Client.prototype.abandon = function abandon(messageID, controls, callback) {
|
||||||
* @throws {TypeError} on invalid input.
|
* @throws {TypeError} on invalid input.
|
||||||
*/
|
*/
|
||||||
Client.prototype.add = function add(name, entry, controls, callback) {
|
Client.prototype.add = function add(name, entry, controls, callback) {
|
||||||
assert.string(name, 'name');
|
assert.ok(name, 'name');
|
||||||
assert.object(entry, 'entry');
|
assert.object(entry, 'entry');
|
||||||
if (typeof (controls) === 'function') {
|
if (typeof (controls) === 'function') {
|
||||||
callback = controls;
|
callback = controls;
|
||||||
|
@ -427,7 +440,7 @@ Client.prototype.add = function add(name, entry, controls, callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var req = new AddRequest({
|
var req = new AddRequest({
|
||||||
entry: dn.parse(name),
|
entry: ensureDN(name, this.strictDN),
|
||||||
attributes: entry,
|
attributes: entry,
|
||||||
controls: controls
|
controls: controls
|
||||||
});
|
});
|
||||||
|
@ -487,7 +500,7 @@ Client.prototype.compare = function compare(name,
|
||||||
value,
|
value,
|
||||||
controls,
|
controls,
|
||||||
callback) {
|
callback) {
|
||||||
assert.string(name, 'name');
|
assert.ok(name, 'name');
|
||||||
assert.string(attr, 'attr');
|
assert.string(attr, 'attr');
|
||||||
assert.string(value, 'value');
|
assert.string(value, 'value');
|
||||||
if (typeof (controls) === 'function') {
|
if (typeof (controls) === 'function') {
|
||||||
|
@ -499,7 +512,7 @@ Client.prototype.compare = function compare(name,
|
||||||
assert.func(callback, 'callback');
|
assert.func(callback, 'callback');
|
||||||
|
|
||||||
var req = new CompareRequest({
|
var req = new CompareRequest({
|
||||||
entry: dn.parse(name),
|
entry: ensureDN(name, this.strictDN),
|
||||||
attribute: attr,
|
attribute: attr,
|
||||||
value: value,
|
value: value,
|
||||||
controls: controls
|
controls: controls
|
||||||
|
@ -523,7 +536,7 @@ Client.prototype.compare = function compare(name,
|
||||||
* @throws {TypeError} on invalid input.
|
* @throws {TypeError} on invalid input.
|
||||||
*/
|
*/
|
||||||
Client.prototype.del = function del(name, controls, callback) {
|
Client.prototype.del = function del(name, controls, callback) {
|
||||||
assert.string(name, 'name');
|
assert.ok(name, 'name');
|
||||||
if (typeof (controls) === 'function') {
|
if (typeof (controls) === 'function') {
|
||||||
callback = controls;
|
callback = controls;
|
||||||
controls = [];
|
controls = [];
|
||||||
|
@ -533,7 +546,7 @@ Client.prototype.del = function del(name, controls, callback) {
|
||||||
assert.func(callback, 'callback');
|
assert.func(callback, 'callback');
|
||||||
|
|
||||||
var req = new DeleteRequest({
|
var req = new DeleteRequest({
|
||||||
entry: dn.parse(name),
|
entry: ensureDN(name, this.strictDN),
|
||||||
controls: controls
|
controls: controls
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -596,7 +609,7 @@ Client.prototype.exop = function exop(name, value, controls, callback) {
|
||||||
* @throws {TypeError} on invalid input.
|
* @throws {TypeError} on invalid input.
|
||||||
*/
|
*/
|
||||||
Client.prototype.modify = function modify(name, change, controls, callback) {
|
Client.prototype.modify = function modify(name, change, controls, callback) {
|
||||||
assert.string(name, 'name');
|
assert.ok(name, 'name');
|
||||||
assert.object(change, 'change');
|
assert.object(change, 'change');
|
||||||
|
|
||||||
var changes = [];
|
var changes = [];
|
||||||
|
@ -651,7 +664,7 @@ Client.prototype.modify = function modify(name, change, controls, callback) {
|
||||||
assert.func(callback, 'callback');
|
assert.func(callback, 'callback');
|
||||||
|
|
||||||
var req = new ModifyRequest({
|
var req = new ModifyRequest({
|
||||||
object: dn.parse(name),
|
object: ensureDN(name, this.strictDN),
|
||||||
changes: changes,
|
changes: changes,
|
||||||
controls: controls
|
controls: controls
|
||||||
});
|
});
|
||||||
|
@ -678,20 +691,18 @@ Client.prototype.modifyDN = function modifyDN(name,
|
||||||
newName,
|
newName,
|
||||||
controls,
|
controls,
|
||||||
callback) {
|
callback) {
|
||||||
if (typeof (name) !== 'string')
|
assert.ok(name, 'name');
|
||||||
throw new TypeError('name (string) required');
|
assert.string(newName, 'newName');
|
||||||
if (typeof (newName) !== 'string')
|
|
||||||
throw new TypeError('newName (string) required');
|
|
||||||
if (typeof (controls) === 'function') {
|
if (typeof (controls) === 'function') {
|
||||||
callback = controls;
|
callback = controls;
|
||||||
controls = [];
|
controls = [];
|
||||||
} else {
|
} else {
|
||||||
controls = validateControls(controls);
|
controls = validateControls(controls);
|
||||||
}
|
}
|
||||||
if (typeof (callback) !== 'function')
|
assert.func(callback);
|
||||||
throw new TypeError('callback (function) required');
|
|
||||||
|
|
||||||
var DN = dn.parse(name);
|
var DN = ensureDN(name);
|
||||||
|
// TODO: is non-strict handling desired here?
|
||||||
var newDN = dn.parse(newName);
|
var newDN = dn.parse(newName);
|
||||||
|
|
||||||
var req = new ModifyDNRequest({
|
var req = new ModifyDNRequest({
|
||||||
|
@ -739,8 +750,7 @@ Client.prototype.search = function search(base,
|
||||||
controls,
|
controls,
|
||||||
callback,
|
callback,
|
||||||
_bypass) {
|
_bypass) {
|
||||||
if (typeof (base) !== 'string' && !(base instanceof dn.DN))
|
assert.ok(base, 'base');
|
||||||
throw new TypeError('base (string) required');
|
|
||||||
if (Array.isArray(options) || (options instanceof Control)) {
|
if (Array.isArray(options) || (options instanceof Control)) {
|
||||||
controls = options;
|
controls = options;
|
||||||
options = {};
|
options = {};
|
||||||
|
@ -762,15 +772,13 @@ Client.prototype.search = function search(base,
|
||||||
} else if (!filters.isFilter(options.filter)) {
|
} else if (!filters.isFilter(options.filter)) {
|
||||||
throw new TypeError('options.filter (Filter) required');
|
throw new TypeError('options.filter (Filter) required');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof (controls) === 'function') {
|
if (typeof (controls) === 'function') {
|
||||||
callback = controls;
|
callback = controls;
|
||||||
controls = [];
|
controls = [];
|
||||||
} else {
|
} else {
|
||||||
controls = validateControls(controls);
|
controls = validateControls(controls);
|
||||||
}
|
}
|
||||||
if (typeof (callback) !== 'function')
|
assert.func(callback, 'callback');
|
||||||
throw new TypeError('callback (function) required');
|
|
||||||
|
|
||||||
if (options.attributes) {
|
if (options.attributes) {
|
||||||
if (!Array.isArray(options.attributes)) {
|
if (!Array.isArray(options.attributes)) {
|
||||||
|
@ -783,9 +791,10 @@ Client.prototype.search = function search(base,
|
||||||
}
|
}
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
var baseDN = ensureDN(base, this.strictDN);
|
||||||
function sendRequest(ctrls, emitter, cb) {
|
function sendRequest(ctrls, emitter, cb) {
|
||||||
var req = new SearchRequest({
|
var req = new SearchRequest({
|
||||||
baseObject: typeof (base) === 'string' ? dn.parse(base) : base,
|
baseObject: baseDN,
|
||||||
scope: options.scope || 'base',
|
scope: options.scope || 'base',
|
||||||
filter: options.filter,
|
filter: options.filter,
|
||||||
derefAliases: options.derefAliases || Protocol.NEVER_DEREF_ALIASES,
|
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 ? ', ' : ',');
|
return _dn.join(this.rdnSpaced ? ', ' : ',');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
DN.prototype.spaced = function (spaces) {
|
DN.prototype.spaced = function (spaces) {
|
||||||
this.rdnSpaced = (spaces === false) ? false : true;
|
this.rdnSpaced = (spaces === false) ? false : true;
|
||||||
return this;
|
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
|
///--- Exports
|
||||||
|
|
||||||
|
|
|
@ -3,19 +3,9 @@
|
||||||
var assert = require('assert');
|
var assert = require('assert');
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
|
|
||||||
var asn1 = require('asn1');
|
|
||||||
|
|
||||||
var LDAPMessage = require('./message');
|
var LDAPMessage = require('./message');
|
||||||
var LDAPResult = require('./result');
|
|
||||||
|
|
||||||
var dn = require('../dn');
|
|
||||||
var Attribute = require('../attribute');
|
|
||||||
var Protocol = require('../protocol');
|
var Protocol = require('../protocol');
|
||||||
|
|
||||||
///--- Globals
|
|
||||||
|
|
||||||
var Ber = asn1.Ber;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
///--- API
|
///--- API
|
||||||
|
|
|
@ -3,19 +3,16 @@
|
||||||
var assert = require('assert');
|
var assert = require('assert');
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
|
|
||||||
var asn1 = require('asn1');
|
|
||||||
|
|
||||||
var LDAPMessage = require('./message');
|
var LDAPMessage = require('./message');
|
||||||
var LDAPResult = require('./result');
|
|
||||||
|
|
||||||
var dn = require('../dn');
|
var dn = require('../dn');
|
||||||
var Attribute = require('../attribute');
|
var Attribute = require('../attribute');
|
||||||
var Protocol = require('../protocol');
|
var Protocol = require('../protocol');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
///--- Globals
|
///--- Globals
|
||||||
|
|
||||||
var Ber = asn1.Ber;
|
var isDN = dn.DN.isDN;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
///--- API
|
///--- API
|
||||||
|
@ -24,8 +21,10 @@ function AddRequest(options) {
|
||||||
if (options) {
|
if (options) {
|
||||||
if (typeof (options) !== 'object')
|
if (typeof (options) !== 'object')
|
||||||
throw new TypeError('options must be an object');
|
throw new TypeError('options must be an object');
|
||||||
if (options.entry && !(options.entry instanceof dn.DN))
|
if (options.entry &&
|
||||||
throw new TypeError('options.entry must be a DN');
|
!(isDN(options.entry) || typeof (options.entry) === 'string')) {
|
||||||
|
throw new TypeError('options.entry must be a DN or string');
|
||||||
|
}
|
||||||
if (options.attributes) {
|
if (options.attributes) {
|
||||||
if (!Array.isArray(options.attributes))
|
if (!Array.isArray(options.attributes))
|
||||||
throw new TypeError('options.attributes must be [Attribute]');
|
throw new TypeError('options.attributes must be [Attribute]');
|
||||||
|
@ -55,7 +54,7 @@ module.exports = AddRequest;
|
||||||
AddRequest.prototype._parse = function (ber) {
|
AddRequest.prototype._parse = function (ber) {
|
||||||
assert.ok(ber);
|
assert.ok(ber);
|
||||||
|
|
||||||
this.entry = dn.parse(ber.readString());
|
this.entry = ber.readString();
|
||||||
|
|
||||||
ber.readSequence();
|
ber.readSequence();
|
||||||
|
|
||||||
|
|
|
@ -6,16 +6,11 @@ var util = require('util');
|
||||||
var asn1 = require('asn1');
|
var asn1 = require('asn1');
|
||||||
|
|
||||||
var LDAPMessage = require('./message');
|
var LDAPMessage = require('./message');
|
||||||
var LDAPResult = require('./result');
|
|
||||||
|
|
||||||
|
|
||||||
var dn = require('../dn');
|
|
||||||
var Protocol = require('../protocol');
|
var Protocol = require('../protocol');
|
||||||
|
|
||||||
///--- Globals
|
///--- Globals
|
||||||
|
|
||||||
var Ber = asn1.Ber;
|
var Ber = asn1.Ber;
|
||||||
|
|
||||||
var LDAP_BIND_SIMPLE = 'simple';
|
var LDAP_BIND_SIMPLE = 'simple';
|
||||||
var LDAP_BIND_SASL = 'sasl';
|
var LDAP_BIND_SASL = 'sasl';
|
||||||
|
|
||||||
|
@ -49,7 +44,7 @@ BindRequest.prototype._parse = function (ber) {
|
||||||
assert.ok(ber);
|
assert.ok(ber);
|
||||||
|
|
||||||
this.version = ber.readInt();
|
this.version = ber.readInt();
|
||||||
this.name = dn.parse(ber.readString());
|
this.name = ber.readString();
|
||||||
|
|
||||||
var t = ber.peek();
|
var t = ber.peek();
|
||||||
|
|
||||||
|
|
|
@ -4,21 +4,26 @@ var assert = require('assert');
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
|
|
||||||
var LDAPMessage = require('./message');
|
var LDAPMessage = require('./message');
|
||||||
var LDAPResult = require('./result');
|
|
||||||
|
|
||||||
var dn = require('../dn');
|
var dn = require('../dn');
|
||||||
var Protocol = require('../protocol');
|
var Protocol = require('../protocol');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///--- Globals
|
||||||
|
|
||||||
|
var isDN = dn.DN.isDN;
|
||||||
|
|
||||||
|
|
||||||
///--- API
|
///--- API
|
||||||
|
|
||||||
function CompareRequest(options) {
|
function CompareRequest(options) {
|
||||||
if (options) {
|
if (options) {
|
||||||
if (typeof (options) !== 'object')
|
if (typeof (options) !== 'object')
|
||||||
throw new TypeError('options must be an object');
|
throw new TypeError('options must be an object');
|
||||||
if (options.entry && !(options.entry instanceof dn.DN))
|
if (options.entry &&
|
||||||
throw new TypeError('options.entry must be a DN');
|
!(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')
|
if (options.attribute && typeof (options.attribute) !== 'string')
|
||||||
throw new TypeError('options.attribute must be a string');
|
throw new TypeError('options.attribute must be a string');
|
||||||
if (options.value && typeof (options.value) !== 'string')
|
if (options.value && typeof (options.value) !== 'string')
|
||||||
|
@ -36,9 +41,7 @@ function CompareRequest(options) {
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
this.__defineGetter__('type', function () { return 'CompareRequest'; });
|
this.__defineGetter__('type', function () { return 'CompareRequest'; });
|
||||||
this.__defineGetter__('_dn', function () {
|
this.__defineGetter__('_dn', function () { return self.entry; });
|
||||||
return self.entry ? self.entry.toString() : '';
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
util.inherits(CompareRequest, LDAPMessage);
|
util.inherits(CompareRequest, LDAPMessage);
|
||||||
module.exports = CompareRequest;
|
module.exports = CompareRequest;
|
||||||
|
@ -47,7 +50,7 @@ module.exports = CompareRequest;
|
||||||
CompareRequest.prototype._parse = function (ber) {
|
CompareRequest.prototype._parse = function (ber) {
|
||||||
assert.ok(ber);
|
assert.ok(ber);
|
||||||
|
|
||||||
this.entry = dn.parse(ber.readString());
|
this.entry = ber.readString();
|
||||||
|
|
||||||
ber.readSequence();
|
ber.readSequence();
|
||||||
this.attribute = ber.readString().toLowerCase();
|
this.attribute = ber.readString().toLowerCase();
|
||||||
|
|
|
@ -3,19 +3,15 @@
|
||||||
var assert = require('assert');
|
var assert = require('assert');
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
|
|
||||||
var asn1 = require('asn1');
|
|
||||||
|
|
||||||
var LDAPMessage = require('./message');
|
var LDAPMessage = require('./message');
|
||||||
var LDAPResult = require('./result');
|
|
||||||
|
|
||||||
var dn = require('../dn');
|
var dn = require('../dn');
|
||||||
var Attribute = require('../attribute');
|
|
||||||
var Protocol = require('../protocol');
|
var Protocol = require('../protocol');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
///--- Globals
|
///--- Globals
|
||||||
|
|
||||||
var Ber = asn1.Ber;
|
var isDN = dn.DN.isDN;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
///--- API
|
///--- API
|
||||||
|
@ -24,8 +20,10 @@ function DeleteRequest(options) {
|
||||||
if (options) {
|
if (options) {
|
||||||
if (typeof (options) !== 'object')
|
if (typeof (options) !== 'object')
|
||||||
throw new TypeError('options must be an object');
|
throw new TypeError('options must be an object');
|
||||||
if (options.entry && !(options.entry instanceof dn.DN))
|
if (options.entry &&
|
||||||
throw new TypeError('options.entry must be a DN');
|
!(isDN(options.entry) || typeof (options.entry) === 'string')) {
|
||||||
|
throw new TypeError('options.entry must be a DN or string');
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
options = {};
|
options = {};
|
||||||
}
|
}
|
||||||
|
@ -46,7 +44,7 @@ module.exports = DeleteRequest;
|
||||||
DeleteRequest.prototype._parse = function (ber, length) {
|
DeleteRequest.prototype._parse = function (ber, length) {
|
||||||
assert.ok(ber);
|
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;
|
ber._offset += ber.length;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -3,22 +3,11 @@
|
||||||
var assert = require('assert');
|
var assert = require('assert');
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
|
|
||||||
var asn1 = require('asn1');
|
|
||||||
|
|
||||||
var LDAPMessage = require('./message');
|
var LDAPMessage = require('./message');
|
||||||
var LDAPResult = require('./result');
|
|
||||||
|
|
||||||
var dn = require('../dn');
|
|
||||||
var Protocol = require('../protocol');
|
var Protocol = require('../protocol');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
///--- Globals
|
|
||||||
|
|
||||||
var Ber = asn1.Ber;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
///--- API
|
///--- API
|
||||||
|
|
||||||
function ExtendedRequest(options) {
|
function ExtendedRequest(options) {
|
||||||
|
|
|
@ -3,19 +3,14 @@
|
||||||
var assert = require('assert');
|
var assert = require('assert');
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
|
|
||||||
var asn1 = require('asn1');
|
|
||||||
|
|
||||||
var LDAPMessage = require('./message');
|
var LDAPMessage = require('./message');
|
||||||
var LDAPResult = require('./result');
|
|
||||||
|
|
||||||
var dn = require('../dn');
|
|
||||||
var Protocol = require('../protocol');
|
var Protocol = require('../protocol');
|
||||||
|
var dn = require('../dn');
|
||||||
|
|
||||||
|
|
||||||
///--- Globals
|
///--- Globals
|
||||||
|
|
||||||
var Ber = asn1.Ber;
|
var isDN = dn.DN.isDN;
|
||||||
|
|
||||||
|
|
||||||
///--- API
|
///--- API
|
||||||
|
@ -24,14 +19,16 @@ function ModifyDNRequest(options) {
|
||||||
if (options) {
|
if (options) {
|
||||||
if (typeof (options) !== 'object')
|
if (typeof (options) !== 'object')
|
||||||
throw new TypeError('options must be an object');
|
throw new TypeError('options must be an object');
|
||||||
if (options.entry && !(options.entry instanceof dn.DN))
|
if (options.entry &&
|
||||||
throw new TypeError('options.entry must be a DN');
|
!(isDN(options.entry) || typeof (options.entry) === 'string')) {
|
||||||
if (options.newRdn && !(options.newRdn instanceof dn.DN))
|
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');
|
throw new TypeError('options.newRdn must be a DN');
|
||||||
if (options.deleteOldRdn !== undefined &&
|
if (options.deleteOldRdn !== undefined &&
|
||||||
typeof (options.deleteOldRdn) !== 'boolean')
|
typeof (options.deleteOldRdn) !== 'boolean')
|
||||||
throw new TypeError('options.deleteOldRdn must be a 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');
|
throw new TypeError('options.newSuperior must be a DN');
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -57,7 +54,7 @@ module.exports = ModifyDNRequest;
|
||||||
ModifyDNRequest.prototype._parse = function (ber) {
|
ModifyDNRequest.prototype._parse = function (ber) {
|
||||||
assert.ok(ber);
|
assert.ok(ber);
|
||||||
|
|
||||||
this.entry = dn.parse(ber.readString());
|
this.entry = ber.readString();
|
||||||
this.newRdn = dn.parse(ber.readString());
|
this.newRdn = dn.parse(ber.readString());
|
||||||
this.deleteOldRdn = ber.readBoolean();
|
this.deleteOldRdn = ber.readBoolean();
|
||||||
if (ber.peek() === 0x80)
|
if (ber.peek() === 0x80)
|
||||||
|
|
|
@ -4,8 +4,6 @@ var assert = require('assert');
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
|
|
||||||
var LDAPMessage = require('./message');
|
var LDAPMessage = require('./message');
|
||||||
var LDAPResult = require('./result');
|
|
||||||
|
|
||||||
var dn = require('../dn');
|
var dn = require('../dn');
|
||||||
var Attribute = require('../attribute');
|
var Attribute = require('../attribute');
|
||||||
var Change = require('../change');
|
var Change = require('../change');
|
||||||
|
@ -13,14 +11,21 @@ var Protocol = require('../protocol');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///--- API
|
||||||
|
|
||||||
|
var isDN = dn.DN.isDN;
|
||||||
|
|
||||||
|
|
||||||
///--- API
|
///--- API
|
||||||
|
|
||||||
function ModifyRequest(options) {
|
function ModifyRequest(options) {
|
||||||
if (options) {
|
if (options) {
|
||||||
if (typeof (options) !== 'object')
|
if (typeof (options) !== 'object')
|
||||||
throw new TypeError('options must be an object');
|
throw new TypeError('options must be an object');
|
||||||
if (options.object && !(options.object instanceof dn.DN))
|
if (options.object &&
|
||||||
throw new TypeError('options.object must be a DN');
|
!(isDN(options.object) || typeof (options.object) === 'string')) {
|
||||||
|
throw new TypeError('options.object must be a DN or string');
|
||||||
|
}
|
||||||
if (options.attributes) {
|
if (options.attributes) {
|
||||||
if (!Array.isArray(options.attributes))
|
if (!Array.isArray(options.attributes))
|
||||||
throw new TypeError('options.attributes must be [Attribute]');
|
throw new TypeError('options.attributes must be [Attribute]');
|
||||||
|
@ -50,7 +55,7 @@ module.exports = ModifyRequest;
|
||||||
ModifyRequest.prototype._parse = function (ber) {
|
ModifyRequest.prototype._parse = function (ber) {
|
||||||
assert.ok(ber);
|
assert.ok(ber);
|
||||||
|
|
||||||
this.object = dn.parse(ber.readString());
|
this.object = ber.readString();
|
||||||
|
|
||||||
ber.readSequence();
|
ber.readSequence();
|
||||||
var end = ber.offset + ber.length;
|
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 LDAPMessage = require('./message');
|
||||||
var LDAPResult = require('./result');
|
var LDAPResult = require('./result');
|
||||||
|
|
||||||
var dn = require('../dn');
|
var dn = require('../dn');
|
||||||
var filters = require('../filters');
|
var filters = require('../filters');
|
||||||
var Protocol = require('../protocol');
|
var Protocol = require('../protocol');
|
||||||
|
@ -16,10 +15,10 @@ var Protocol = require('../protocol');
|
||||||
|
|
||||||
///--- Globals
|
///--- Globals
|
||||||
|
|
||||||
|
var DN = dn.DN;
|
||||||
var Ber = asn1.Ber;
|
var Ber = asn1.Ber;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
///--- API
|
///--- API
|
||||||
|
|
||||||
function SearchRequest(options) {
|
function SearchRequest(options) {
|
||||||
|
@ -35,9 +34,7 @@ function SearchRequest(options) {
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
this.__defineGetter__('type', function () { return 'SearchRequest'; });
|
this.__defineGetter__('type', function () { return 'SearchRequest'; });
|
||||||
this.__defineGetter__('_dn', function () {
|
this.__defineGetter__('_dn', function () { return self.baseObject; });
|
||||||
return self.baseObject;
|
|
||||||
});
|
|
||||||
this.__defineGetter__('scope', function () {
|
this.__defineGetter__('scope', function () {
|
||||||
switch (self._scope) {
|
switch (self._scope) {
|
||||||
case Protocol.SCOPE_BASE_OBJECT: return 'base';
|
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.scope = options.scope || 'base';
|
||||||
this.derefAliases = options.derefAliases || Protocol.NEVER_DEREF_ALIASES;
|
this.derefAliases = options.derefAliases || Protocol.NEVER_DEREF_ALIASES;
|
||||||
this.sizeLimit = options.sizeLimit || 0;
|
this.sizeLimit = options.sizeLimit || 0;
|
||||||
|
@ -93,7 +90,7 @@ SearchRequest.prototype.newResult = function () {
|
||||||
SearchRequest.prototype._parse = function (ber) {
|
SearchRequest.prototype._parse = function (ber) {
|
||||||
assert.ok(ber);
|
assert.ok(ber);
|
||||||
|
|
||||||
this.baseObject = dn.parse(ber.readString());
|
this.baseObject = ber.readString();
|
||||||
this.scope = ber.readEnumeration();
|
this.scope = ber.readEnumeration();
|
||||||
this.derefAliases = ber.readEnumeration();
|
this.derefAliases = ber.readEnumeration();
|
||||||
this.sizeLimit = ber.readInt();
|
this.sizeLimit = ber.readInt();
|
||||||
|
|
|
@ -3,11 +3,7 @@
|
||||||
var assert = require('assert');
|
var assert = require('assert');
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
|
|
||||||
var asn1 = require('asn1');
|
|
||||||
|
|
||||||
var LDAPMessage = require('./message');
|
var LDAPMessage = require('./message');
|
||||||
var LDAPResult = require('./result');
|
|
||||||
|
|
||||||
var dn = require('../dn');
|
var dn = require('../dn');
|
||||||
var Protocol = require('../protocol');
|
var Protocol = require('../protocol');
|
||||||
|
|
||||||
|
@ -15,8 +11,6 @@ var Protocol = require('../protocol');
|
||||||
|
|
||||||
///--- Globals
|
///--- Globals
|
||||||
|
|
||||||
var Ber = asn1.Ber;
|
|
||||||
|
|
||||||
var DN = dn.DN;
|
var DN = dn.DN;
|
||||||
var RDN = dn.RDN;
|
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) {
|
function defaultHandler(req, res, next) {
|
||||||
assert.ok(req);
|
assert.ok(req);
|
||||||
assert.ok(res);
|
assert.ok(res);
|
||||||
|
@ -276,6 +315,7 @@ function Server(options) {
|
||||||
|
|
||||||
this._chain = [];
|
this._chain = [];
|
||||||
this.log = options.log;
|
this.log = options.log;
|
||||||
|
this.strictDN = (options.strictDN !== undefined) ? options.strictDN : true;
|
||||||
|
|
||||||
var log = this.log;
|
var log = this.log;
|
||||||
|
|
||||||
|
@ -355,6 +395,9 @@ function Server(options) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parse string DNs for routing/etc
|
||||||
|
decodeDN(req, this.strictDN);
|
||||||
|
|
||||||
res.connection = c;
|
res.connection = c;
|
||||||
res.logId = req.logId;
|
res.logId = req.logId;
|
||||||
res.requestDN = req.dn;
|
res.requestDN = req.dn;
|
||||||
|
@ -808,12 +851,15 @@ Server.prototype._getHandlerChain = function _getHandlerChain(req, res) {
|
||||||
assert.ok(req.dn);
|
assert.ok(req.dn);
|
||||||
var keys = this._sortedRouteKeys();
|
var keys = this._sortedRouteKeys();
|
||||||
var fallbackHandler = [noSuffixHandler];
|
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++) {
|
for (var i = 0; i < keys.length; i++) {
|
||||||
var suffix = keys[i];
|
var suffix = keys[i];
|
||||||
route = routes[suffix];
|
route = routes[suffix];
|
||||||
assert.ok(route.dn);
|
assert.ok(route.dn);
|
||||||
// Match a valid route or the route wildcard ('')
|
// 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]) {
|
if (route[op]) {
|
||||||
// We should be good to go.
|
// We should be good to go.
|
||||||
req.suffix = route.dn;
|
req.suffix = route.dn;
|
||||||
|
|
|
@ -143,3 +143,18 @@ test('rdn spacing', function (t) {
|
||||||
t.equals(dn2.spaced(false).toString(), 'cn=foo,dc=bar');
|
t.equals(dn2.spaced(false).toString(), 'cn=foo,dc=bar');
|
||||||
t.end();
|
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.ok(req._parse(new BerReader(ber.buffer)));
|
||||||
t.equal(req.version, 3);
|
t.equal(req.version, 3);
|
||||||
t.equal(req.dn.toString(), 'cn=root');
|
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.equal(req.credentials, 'secret');
|
||||||
t.end();
|
t.end();
|
||||||
});
|
});
|
||||||
|
|
|
@ -36,7 +36,7 @@ test('new with args', function (t) {
|
||||||
value: 'testy'
|
value: 'testy'
|
||||||
});
|
});
|
||||||
t.ok(req);
|
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.attribute, 'sn');
|
||||||
t.equal(req.value, 'testy');
|
t.equal(req.value, 'testy');
|
||||||
t.end();
|
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