attribute was lowercasing vals, not attr name

This commit is contained in:
Mark Cavage 2011-08-10 10:57:58 -07:00
parent a1fa0e46b1
commit 44bd29952e
10 changed files with 222 additions and 102 deletions

View File

@ -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());
}

View File

@ -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,

View File

@ -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;
};

View File

@ -55,7 +55,7 @@ module.exports = {
dn: dn,
DN: dn.DN,
DN: dn.RDN,
RDN: dn.RDN,
parseDN: dn.parse,
filters: filters,

View File

@ -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;
};

View File

@ -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;

View File

@ -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());
}
};

View File

@ -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]

View File

@ -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();
});

View File

@ -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');