attribute was lowercasing vals, not attr name
This commit is contained in:
parent
a1fa0e46b1
commit
44bd29952e
|
@ -69,13 +69,13 @@ Attribute.prototype.parse = function(ber) {
|
|||
assert.ok(ber);
|
||||
|
||||
ber.readSequence();
|
||||
this.type = ber.readString();
|
||||
this.type = ber.readString().toLowerCase();
|
||||
|
||||
|
||||
if (ber.readSequence(Protocol.LBER_SET)) {
|
||||
var end = ber.offset + ber.length;
|
||||
while (ber.offset < end)
|
||||
this.vals.push(ber.readString().toLowerCase());
|
||||
this.vals.push(ber.readString());
|
||||
|
||||
}
|
||||
|
||||
|
|
39
lib/dn.js
39
lib/dn.js
|
@ -29,12 +29,14 @@ RDN.prototype.toString = function() {
|
|||
Object.keys(this).forEach(function(k) {
|
||||
if (str.length)
|
||||
str += '+';
|
||||
|
||||
str += k + '=' + self[k];
|
||||
});
|
||||
|
||||
return str;
|
||||
};
|
||||
|
||||
// Thank you OpenJDK!
|
||||
function parse(name) {
|
||||
if (typeof(name) !== 'string')
|
||||
throw new TypeError('name (string) required');
|
||||
|
@ -233,6 +235,43 @@ DN.prototype.parentOf = function(dn) {
|
|||
};
|
||||
|
||||
|
||||
DN.prototype.equals = function(dn) {
|
||||
if (!(dn instanceof DN))
|
||||
return false;
|
||||
|
||||
if (this.rdns.length !== dn.rdns.length)
|
||||
return false;
|
||||
|
||||
for (var i = 0; i < this.rdns.length; i++) {
|
||||
var ours = this.rdns[i];
|
||||
var theirs = dn.rdns[i];
|
||||
for (var k in ours) {
|
||||
if (ours.hasOwnProperty(k)) {
|
||||
if (!theirs.hasOwnProperty(k))
|
||||
return false;
|
||||
|
||||
if (ours[k] !== theirs[k])
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
DN.prototype.parent = function() {
|
||||
if (this.rdns.length > 1) {
|
||||
var save = this.rdns.shift();
|
||||
var dn = new DN(this.rdns);
|
||||
this.rdns.unshift(save);
|
||||
return dn;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
|
||||
module.exports = {
|
||||
|
||||
parse: parse,
|
||||
|
|
|
@ -42,18 +42,16 @@ PresenceFilter.prototype.matches = function(target) {
|
|||
if (typeof(target) !== 'object')
|
||||
throw new TypeError('target (object) required');
|
||||
|
||||
var matches = false;
|
||||
if (target.hasOwnProperty(this.attribute))
|
||||
matches = true;
|
||||
|
||||
return matches;
|
||||
console.log(this.attribute);
|
||||
return target.hasOwnProperty(this.attribute);
|
||||
};
|
||||
|
||||
|
||||
PresenceFilter.prototype.parse = function(ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
this.attribute = ber.readString();
|
||||
this.attribute = ber.buffer.slice(0, ber.length).toString('utf8');
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
@ -61,7 +59,8 @@ PresenceFilter.prototype.parse = function(ber) {
|
|||
PresenceFilter.prototype._toBer = function(ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
ber.writeString(this.attribute);
|
||||
for (var i = 0; i < this.attribute.length; i++)
|
||||
ber.writeByte(this.attribute.charCodeAt(i));
|
||||
|
||||
return ber;
|
||||
};
|
||||
|
|
|
@ -55,7 +55,7 @@ module.exports = {
|
|||
|
||||
dn: dn,
|
||||
DN: dn.DN,
|
||||
DN: dn.RDN,
|
||||
RDN: dn.RDN,
|
||||
parseDN: dn.parse,
|
||||
|
||||
filters: filters,
|
||||
|
|
|
@ -115,7 +115,21 @@ AddRequest.prototype.attributeNames = function() {
|
|||
var attrs = [];
|
||||
|
||||
for (var i = 0; i < this.attributes.length; i++)
|
||||
attrs.push[this.attributes[i].type];
|
||||
attrs.push[this.attributes[i].type.toLowerCase()];
|
||||
|
||||
return attrs;
|
||||
};
|
||||
|
||||
|
||||
AddRequest.prototype.getAttribute = function(name) {
|
||||
if (!name || typeof(name) !== 'string')
|
||||
throw new TypeEroror('attribute name (string) required');
|
||||
|
||||
name = name.toLowerCase();
|
||||
|
||||
for (var i = 0; i < this.attributes.length; i++)
|
||||
if (this.attributes[i].type === name)
|
||||
return this.attribute[i];
|
||||
|
||||
return null;
|
||||
};
|
||||
|
|
|
@ -44,7 +44,7 @@ function BindRequest(options) {
|
|||
|
||||
var self = this;
|
||||
this.__defineGetter__('type', function() { return 'BindRequest'; });
|
||||
this.__defineGetter__('_dn', function() { return self.name.toString(); });
|
||||
this.__defineGetter__('_dn', function() { return self.name; });
|
||||
}
|
||||
util.inherits(BindRequest, LDAPMessage);
|
||||
module.exports = BindRequest;
|
||||
|
|
|
@ -65,7 +65,12 @@ LDAPResult.prototype.end = function(status) {
|
|||
if (this.log.isDebugEnabled())
|
||||
this.log.debug('%s: sending: %j', this.connection.ldap.id, this.json);
|
||||
|
||||
try {
|
||||
this.connection.write(ber);
|
||||
} catch (e) {
|
||||
this.log.warn('%s failure to write message %j: %s',
|
||||
this.connection.ldap.id, this.json, e.toString());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
210
lib/server.js
210
lib/server.js
|
@ -31,7 +31,7 @@ var UnbindResponse = require('./messages/unbind_response');
|
|||
|
||||
var Ber = asn1.Ber;
|
||||
var BerReader = asn1.BerReader;
|
||||
|
||||
var DN = dn.DN;
|
||||
|
||||
|
||||
///--- Helpers
|
||||
|
@ -227,6 +227,17 @@ function Server(options) {
|
|||
log.trace('%s close; had_err=%j', c.ldap.id, had_err);
|
||||
c.end();
|
||||
});
|
||||
|
||||
c.ldap.__defineGetter__('bindDN', function() {
|
||||
return c.ldap._bindDN || 'cn=anonymous';
|
||||
});
|
||||
c.ldap.__defineSetter__('bindDN', function(val) {
|
||||
if (!(val instanceof DN))
|
||||
throw new TypeError('DN required');
|
||||
|
||||
c.ldap._bindDN = val;
|
||||
return val;
|
||||
});
|
||||
return c;
|
||||
}
|
||||
|
||||
|
@ -261,16 +272,31 @@ function Server(options) {
|
|||
|
||||
var i = 0;
|
||||
return function(err) {
|
||||
if (err) {
|
||||
function sendError(err) {
|
||||
res.status = err.code || errors.LDAP_OPERATIONS_ERROR;
|
||||
res.matchedDN = err.dn ? err.dn.toString() : req.dn.toString();
|
||||
res.matchedDN = req.dn.toString();
|
||||
res.errorMessage = err.message || '';
|
||||
return res.end();
|
||||
}
|
||||
|
||||
if (err)
|
||||
return sendError(err);
|
||||
|
||||
try {
|
||||
var next = arguments.callee;
|
||||
if (chain.handlers[i])
|
||||
return chain.handlers[i++].call(chain.backend, req, res, next);
|
||||
|
||||
if (req.protocolOp === Protocol.LDAP_REQ_BIND && res.status === 0)
|
||||
c.ldap.bindDN = req.dn;
|
||||
|
||||
} catch (e) {
|
||||
if (!e.stack)
|
||||
e.stack = e.toString();
|
||||
log.error('%s uncaught exception: %s', req.logId, e.stack);
|
||||
return sendError(new errors.OperationsError(e.message));
|
||||
}
|
||||
|
||||
}();
|
||||
});
|
||||
|
||||
|
@ -317,6 +343,21 @@ function Server(options) {
|
|||
this.__defineGetter__('connections', function() {
|
||||
return self.server.connections;
|
||||
});
|
||||
this.__defineGetter__('name', function() {
|
||||
return 'LDAPServer';
|
||||
});
|
||||
this.__defineGetter__('url', function() {
|
||||
var str;
|
||||
if (options.certificate && options.key) {
|
||||
str = 'ldaps://';
|
||||
} else {
|
||||
str = 'ldap://';
|
||||
}
|
||||
str += self.host || 'localhost';
|
||||
str += ':';
|
||||
str += self.port || 389;
|
||||
return str;
|
||||
});
|
||||
}
|
||||
util.inherits(Server, EventEmitter);
|
||||
module.exports = Server;
|
||||
|
@ -338,9 +379,15 @@ Server.prototype.add = function(name) {
|
|||
if (arguments.length < 2)
|
||||
throw new TypeError('name and at least one handler required');
|
||||
|
||||
var route = this._getRoute(dn.parse(name));
|
||||
var backend = this;
|
||||
var index = 1;
|
||||
if (typeof(arguments[1]) === 'object') {
|
||||
backend = arguments[1];
|
||||
index = 2;
|
||||
}
|
||||
var route = this._getRoute(dn.parse(name), backend);
|
||||
route['0x' + Protocol.LDAP_REQ_ADD.toString(16)] =
|
||||
mergeFunctionArgs(Array.prototype.slice.call(arguments, 1));
|
||||
mergeFunctionArgs(Array.prototype.slice.call(arguments, index));
|
||||
|
||||
return this;
|
||||
};
|
||||
|
@ -362,9 +409,15 @@ Server.prototype.bind = function(name) {
|
|||
if (arguments.length < 2)
|
||||
throw new TypeError('name and at least one handler required');
|
||||
|
||||
var route = this._getRoute(dn.parse(name));
|
||||
var backend = this;
|
||||
var index = 1;
|
||||
if (typeof(arguments[1]) === 'object') {
|
||||
backend = arguments[1];
|
||||
index = 2;
|
||||
}
|
||||
var route = this._getRoute(dn.parse(name), backend);
|
||||
route['0x' + Protocol.LDAP_REQ_BIND.toString(16)] =
|
||||
mergeFunctionArgs(Array.prototype.slice.call(arguments, 1));
|
||||
mergeFunctionArgs(Array.prototype.slice.call(arguments, index));
|
||||
|
||||
return this;
|
||||
};
|
||||
|
@ -386,9 +439,15 @@ Server.prototype.compare = function(name) {
|
|||
if (arguments.length < 2)
|
||||
throw new TypeError('name and at least one handler required');
|
||||
|
||||
var route = this._getRoute(dn.parse(name));
|
||||
var backend = this;
|
||||
var index = 1;
|
||||
if (typeof(arguments[1]) === 'object') {
|
||||
backend = arguments[1];
|
||||
index = 2;
|
||||
}
|
||||
var route = this._getRoute(dn.parse(name), backend);
|
||||
route['0x' + Protocol.LDAP_REQ_COMPARE.toString(16)] =
|
||||
mergeFunctionArgs(Array.prototype.slice.call(arguments, 1));
|
||||
mergeFunctionArgs(Array.prototype.slice.call(arguments, index));
|
||||
|
||||
return this;
|
||||
};
|
||||
|
@ -410,9 +469,15 @@ Server.prototype.del = function(name) {
|
|||
if (arguments.length < 2)
|
||||
throw new TypeError('name and at least one handler required');
|
||||
|
||||
var route = this._getRoute(dn.parse(name));
|
||||
var backend = this;
|
||||
var index = 1;
|
||||
if (typeof(arguments[1]) === 'object') {
|
||||
backend = arguments[1];
|
||||
index = 2;
|
||||
}
|
||||
var route = this._getRoute(dn.parse(name), backend);
|
||||
route['0x' + Protocol.LDAP_REQ_DELETE.toString(16)] =
|
||||
mergeFunctionArgs(Array.prototype.slice.call(arguments, 1));
|
||||
mergeFunctionArgs(Array.prototype.slice.call(arguments, index));
|
||||
|
||||
return this;
|
||||
};
|
||||
|
@ -434,9 +499,15 @@ Server.prototype.exop = function(name) {
|
|||
if (arguments.length < 2)
|
||||
throw new TypeError('name and at least one handler required');
|
||||
|
||||
var route = this._getRoute(name, this.server);
|
||||
var backend = this;
|
||||
var index = 1;
|
||||
if (typeof(arguments[1]) === 'object') {
|
||||
backend = arguments[1];
|
||||
index = 2;
|
||||
}
|
||||
var route = this._getRoute(name, backend);
|
||||
route['0x' + Protocol.LDAP_REQ_EXTENSION.toString(16)] =
|
||||
mergeFunctionArgs(Array.prototype.slice.call(arguments, 1));
|
||||
mergeFunctionArgs(Array.prototype.slice.call(arguments, index));
|
||||
|
||||
return this;
|
||||
};
|
||||
|
@ -458,9 +529,15 @@ Server.prototype.modify = function(name) {
|
|||
if (arguments.length < 2)
|
||||
throw new TypeError('name and at least one handler required');
|
||||
|
||||
var route = this._getRoute(dn.parse(name));
|
||||
var backend = this;
|
||||
var index = 1;
|
||||
if (typeof(arguments[1]) === 'object') {
|
||||
backend = arguments[1];
|
||||
index = 2;
|
||||
}
|
||||
var route = this._getRoute(dn.parse(name), backend);
|
||||
route['0x' + Protocol.LDAP_REQ_MODIFY.toString(16)] =
|
||||
mergeFunctionArgs(Array.prototype.slice.call(arguments, 1));
|
||||
mergeFunctionArgs(Array.prototype.slice.call(arguments, index));
|
||||
|
||||
return this;
|
||||
};
|
||||
|
@ -482,9 +559,15 @@ Server.prototype.modifyDN = function(name) {
|
|||
if (arguments.length < 2)
|
||||
throw new TypeError('name and at least one handler required');
|
||||
|
||||
var route = this._getRoute(dn.parse(name));
|
||||
var backend = this;
|
||||
var index = 1;
|
||||
if (typeof(arguments[1]) === 'object') {
|
||||
backend = arguments[1];
|
||||
index = 2;
|
||||
}
|
||||
var route = this._getRoute(dn.parse(name), backend);
|
||||
route['0x' + Protocol.LDAP_REQ_MODRDN.toString(16)] =
|
||||
mergeFunctionArgs(Array.prototype.slice.call(arguments, 1));
|
||||
mergeFunctionArgs(Array.prototype.slice.call(arguments, index));
|
||||
|
||||
return this;
|
||||
};
|
||||
|
@ -506,9 +589,15 @@ Server.prototype.search = function(name) {
|
|||
if (arguments.length < 2)
|
||||
throw new TypeError('name and at least one handler required');
|
||||
|
||||
var route = this._getRoute(dn.parse(name));
|
||||
var backend = this;
|
||||
var index = 1;
|
||||
if (typeof(arguments[1]) === 'object') {
|
||||
backend = arguments[1];
|
||||
index = 2;
|
||||
}
|
||||
var route = this._getRoute(dn.parse(name), backend);
|
||||
route['0x' + Protocol.LDAP_REQ_SEARCH.toString(16)] =
|
||||
mergeFunctionArgs(Array.prototype.slice.call(arguments, 1));
|
||||
mergeFunctionArgs(Array.prototype.slice.call(arguments, index));
|
||||
|
||||
return this;
|
||||
};
|
||||
|
@ -527,50 +616,15 @@ Server.prototype.unbind = function() {
|
|||
if (arguments.length < 1)
|
||||
throw new TypeError('at least one handler required');
|
||||
|
||||
var route = this._getRoute('unbind');
|
||||
var backend = this;
|
||||
var index = 0;
|
||||
if (typeof(arguments[0]) === 'object') {
|
||||
backend = arguments[0];
|
||||
index = 1;
|
||||
}
|
||||
var route = this._getRoute('unbind', backend);
|
||||
route['0x' + Protocol.LDAP_REQ_UNBIND.toString(16)] =
|
||||
mergeFunctionArgs(Array.prototype.slice.call(arguments, 0));
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* It's likely you'll write an entire backend for LDAP that does a series
|
||||
* of things, like check schema, support entries, write an audit trail, etc.
|
||||
* If such a plugin is "bundled", you can simply call `mount` with that plugin
|
||||
* and assign it a point in the DIT, to save you manually building up the
|
||||
* handler chains for all the ops.
|
||||
*
|
||||
* @param {String} name the point in the tree to mount at.
|
||||
* @param {Object} backend an LDAP Backend (See the docs).
|
||||
* @return {Server} this so you can chain.
|
||||
* @throws {TypeError} on bad input.
|
||||
*/
|
||||
Server.prototype.mount = function(name, backend) {
|
||||
if (!name || typeof(name) !== 'string')
|
||||
throw new TypeError('name (string) required');
|
||||
if (!backend || typeof(backend) !== 'object')
|
||||
throw new TypeError('backend (object) required');
|
||||
if (!backend.name || typeof(backend.name) !== 'string')
|
||||
throw new TypeError('backend is not a valid LDAP Backend');
|
||||
if (!backend.register || typeof(backend.register) !== 'function')
|
||||
throw new TypeError('backend is not a valid LDAP Backend');
|
||||
|
||||
var _dn = dn.parse(name).toString();
|
||||
name = _dn.toString();
|
||||
|
||||
var self = this;
|
||||
|
||||
// This is slightly ghetto, but easier than repeating all the code here.
|
||||
var ops = ['add', 'bind', 'compare', 'del', 'modify', 'modifyDN', 'search'];
|
||||
ops.forEach(function(o) {
|
||||
self[o](name, backend.register(o));
|
||||
});
|
||||
|
||||
// Overwrite the route table's backend with backend
|
||||
var route = this._getRoute(_dn);
|
||||
route.backend = backend;
|
||||
mergeFunctionArgs(Array.prototype.slice.call(arguments, index));
|
||||
|
||||
return this;
|
||||
};
|
||||
|
@ -578,15 +632,34 @@ Server.prototype.mount = function(name, backend) {
|
|||
|
||||
// All these just reexpose the requisite net.Server APIs
|
||||
Server.prototype.listen = function(port, host, callback) {
|
||||
if (typeof(host) === 'function')
|
||||
if (typeof(host) === 'function') {
|
||||
callback = host;
|
||||
host = '0.0.0.0';
|
||||
}
|
||||
var self = this;
|
||||
|
||||
function _callback() {
|
||||
if (typeof(port) === 'number') {
|
||||
self.host = host;
|
||||
self.port = port;
|
||||
} else {
|
||||
self.host = port;
|
||||
self.port = self.server.fd;
|
||||
}
|
||||
|
||||
return this.server.listen(port, function() {
|
||||
if (typeof(callback) === 'function')
|
||||
return callback();
|
||||
});
|
||||
}
|
||||
|
||||
if (typeof(port) === 'number') {
|
||||
return this.server.listen(port, host, _callback);
|
||||
} else {
|
||||
return this.server.listen(port, _callback);
|
||||
}
|
||||
};
|
||||
Server.prototype.listenFD = function(fd) {
|
||||
self.host = 'unix-domain-socket';
|
||||
self.port = fd;
|
||||
return this.server.listenFD(fd);
|
||||
};
|
||||
Server.prototype.close = function() {
|
||||
|
@ -641,8 +714,10 @@ Server.prototype._getHandlerChain = function(req) {
|
|||
};
|
||||
} else if (req.protocolOp === Protocol.LDAP_REQ_UNBIND) {
|
||||
return {
|
||||
backend: routes['unbind'].backend,
|
||||
handlers: routes['unbind'][op] || [defaultUnbindHandler]
|
||||
backend: routes['unbind'] ? routes['unbind'].backend : self,
|
||||
handlers: (routes['unbind'] && routes['unbind'][op] ?
|
||||
routes['unbind'][op] :
|
||||
[defaultUnbindHandler])
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -656,6 +731,7 @@ Server.prototype._getHandlerChain = function(req) {
|
|||
continue;
|
||||
|
||||
// We should be good to go.
|
||||
req.suffix = route.dn;
|
||||
return {
|
||||
backend: route.backend,
|
||||
handlers: route[op] || [defaultHandler]
|
||||
|
|
|
@ -65,27 +65,14 @@ test('match false', function(t) {
|
|||
|
||||
test('parse ok', function(t) {
|
||||
var writer = new BerWriter();
|
||||
writer.writeString('foo');
|
||||
writer.writeString('foo', 0x87);
|
||||
|
||||
var f = new PresenceFilter();
|
||||
t.ok(f);
|
||||
t.ok(f.parse(new BerReader(writer.buffer)));
|
||||
|
||||
var reader = new BerReader(writer.buffer);
|
||||
reader.readSequence();
|
||||
t.ok(f.parse(reader));
|
||||
t.ok(f.matches({ foo: 'bar' }));
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('parse bad', function(t) {
|
||||
var writer = new BerWriter();
|
||||
writer.writeInt(20);
|
||||
|
||||
var f = new PresenceFilter();
|
||||
t.ok(f);
|
||||
try {
|
||||
f.parse(new BerReader(writer.buffer));
|
||||
t.fail('Should have thrown InvalidAsn1Error');
|
||||
} catch (e) {
|
||||
t.equal(e.name, 'InvalidAsn1Error');
|
||||
}
|
||||
t.end();
|
||||
});
|
||||
|
|
|
@ -52,7 +52,7 @@ test('parse', function(t) {
|
|||
var req = new BindRequest();
|
||||
t.ok(req._parse(new BerReader(ber.buffer)));
|
||||
t.equal(req.version, 3);
|
||||
t.equal(req.dn, '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');
|
||||
|
|
Loading…
Reference in New Issue