DTrace rework so probes always fire

This commit is contained in:
Mark Cavage 2011-11-18 16:39:37 -08:00
parent 4f2dc61feb
commit 57831b56d1
5 changed files with 291 additions and 107 deletions

197
examples/inmemory.js Normal file
View File

@ -0,0 +1,197 @@
var ldap = require('../lib/index');
///--- Shared handlers
function authorize(req, res, next) {
if (!req.connection.ldap.bindDN.equals('cn=root'))
return next(new ldap.InsufficientAccessRightsError());
return next();
}
///--- Globals
var SUFFIX = 'o=smartdc';
var db = {};
var server = ldap.createServer();
server.bind('cn=root', function(req, res, next) {
if (req.dn.toString() !== 'cn=root' || req.credentials !== 'secret')
return next(new ldap.InvalidCredentialsError());
res.end();
return next();
});
server.add(SUFFIX, authorize, function(req, res, next) {
var dn = req.dn.toString();
if (db[dn])
return next(new ldap.EntryAlreadyExistsError(dn));
db[dn] = req.toObject().attributes;
res.end();
return next();
});
server.bind(SUFFIX, function(req, res, next) {
var dn = req.dn.toString();
if (!db[dn])
return next(new ldap.NoSuchObjectError(dn));
if (!dn[dn].userpassword)
return next(new ldap.NoSuchAttributeError('userPassword'));
if (db[dn].userpassword !== req.credentials)
return next(new ldap.InvalidCredentialsError());
res.end();
return next();
});
server.compare(SUFFIX, authorize, function(req, res, next) {
var dn = req.dn.toString();
if (!db[dn])
return next(new ldap.NoSuchObjectError(dn));
if (!db[dn][req.attribute])
return next(new ldap.NoSuchAttributeError(req.attribute));
var matches = false;
var vals = db[dn][req.attribute];
for (var i = 0; i < vals.length; i++) {
if (vals[i] === req.value) {
matches = true;
break;
}
}
res.end(matches);
return next();
});
server.del(SUFFIX, authorize, function(req, res, next) {
var dn = req.dn.toString();
if (!db[dn])
return next(new ldap.NoSuchObjectError(dn));
delete db[dn];
res.end();
return next();
});
server.modify(SUFFIX, authorize, function(req, res, next) {
var dn = req.dn.toString();
if (!req.changes.length)
return next(new ldap.ProtocolError('changes required'));
if (!db[dn])
return next(new ldap.NoSuchObjectError(dn));
var entry = db[dn];
for (var i = 0; i < req.changes.length; i++) {
mod = req.changes[i].modification;
switch (req.changes[i].operation) {
case 'replace':
if (!entry[mod.type])
return next(new ldap.NoSuchAttributeError(mod.type));
if (!mod.vals || !mod.vals.length) {
delete entry[mod.type];
} else {
entry[mod.type] = mod.vals;
}
break;
case 'add':
if (!entry[mod.type]) {
entry[mod.type] = mod.vals;
} else {
mod.vals.forEach(function(v) {
if (entry[mod.type].indexOf(v) === -1)
entry[mod.type].push(v);
});
}
break;
case 'delete':
if (!entry[mod.type])
return next(new ldap.NoSuchAttributeError(mod.type));
delete entry[mod.type];
break;
}
}
res.end();
return next();
});
server.search(SUFFIX, authorize, function(req, res, next) {
var dn = req.dn.toString();
if (!db[dn])
return next(new ldap.NoSuchObjectError(dn));
var scopeCheck;
switch (req.scope) {
case 'base':
if (req.filter.matches(db[dn])) {
res.send({
dn: dn,
attributes: db[dn]
});
}
res.end();
return next();
case 'one':
scopeCheck = function(k) {
if (req.dn.equals(k))
return true;
var parent = ldap.parseDN(k).parent();
return (parent ? parent.equals(req.dn) : false);
};
break;
case 'sub':
scopeCheck = function(k) {
return (req.dn.equals(k) || req.dn.parentOf(k));
};
break;
}
Object.keys(db).forEach(function(key) {
if (!scopeCheck(key))
return;
if (req.filter.matches(db[key])) {
res.send({
dn: key,
attributes: db[key]
});
}
});
res.end();
return next();
});
///--- Fire it up
server.listen(1389, function() {
console.log('LDAP server up at: %s', server.url);
});

View File

@ -3,6 +3,8 @@
var assert = require('assert');
var util = require('util');
var dtrace = require('../dtrace');
var LDAPMessage = require('./result');
var Protocol = require('../protocol');
@ -38,6 +40,21 @@ UnbindResponse.prototype.end = function(status) {
this.log.trace('%s: unbinding!', this.connection.ldap.id);
this.connection.end();
var self = this;
if (self._dtraceOp && self._dtraceId) {
dtrace.fire('server-' + self._dtraceOp + '-done', function() {
var c = self.connection || {ldap: {}};
return [
self._dtraceId || 0,
(c.remoteAddress || ''),
c.ldap.bindDN ? c.ldap.bindDN.toString() : '',
(self.requestDN ? self.requestDN.toString() : ''),
0,
''
];
});
}
};

View File

@ -135,27 +135,7 @@ function defaultHandler(req, res, next) {
}
function defaultAbandonHandler(req, res, next) {
assert.ok(req);
assert.ok(res);
assert.ok(next);
res.end();
return next();
}
function defaultUnbindHandler(req, res, next) {
assert.ok(req);
assert.ok(res);
assert.ok(next);
res.end();
return next();
}
function defaultAnonymousBindHandler(req, res, next) {
function defaultNoOpHandler(req, res, next) {
assert.ok(req);
assert.ok(res);
assert.ok(next);
@ -187,32 +167,69 @@ function noExOpHandler(req, res, next) {
}
function getArgumentsWithDTrace(args, op, cb1, cb2) {
assert.ok(op);
function fireDTraceProbe(req, res) {
assert.ok(req);
var index = 0;
if (typeof(args[0]) === 'object')
index = 1;
req._dtraceId = res._dtraceId = dtrace._nextId();
var probeArgs = [
req._dtraceId,
req.connection.remoteAddress || 'localhost',
req.connection.ldap.bindDN.toString(),
req.dn.toString(),
];
args.splice(index, 0, function(req, res, next) {
req._dtraceId = res._dtraceId = dtrace._nextId();
res._dtraceOp = op;
dtrace.fire('server-' + res._dtraceOp + '-start', function() {
return [
res._dtraceId,
req.connection.remoteAddress,
req.connection.ldap.bindDN.toString(),
req.dn.toString(),
cb1 ? cb1(req, res) : undefined,
cb2 ? cb2(req, res) : undefined
];
});
return next();
var op;
switch (req.protocolOp) {
case Protocol.LDAP_REQ_ABANDON:
op = 'abandon';
break;
case Protocol.LDAP_REQ_ADD:
op = 'add';
probeArgs.push(req.attributes.length);
break;
case Protocol.LDAP_REQ_BIND:
op = 'bind';
break;
case Protocol.LDAP_REQ_COMPARE:
op = 'compare';
probeArgs.push(req.attribute);
probeArgs.push(req.value);
break;
case Protocol.LDAP_REQ_DELETE:
op = 'delete';
break;
case Protocol.LDAP_REQ_EXTENSION:
op = 'exop';
probeArgs.push(req.name);
probeArgs.push(req.value);
break;
case Protocol.LDAP_REQ_MODIFY:
op = 'modify';
probeArgs.push(req.changes.length);
break;
case Protocol.LDAP_REQ_MODRDN:
op = 'modifydn';
probeArgs.push(req.newRdn.toString());
probeArgs.push((req.newSuperior ? req.newSuperior.toString() : ''));
break;
case Protocol.LDAP_REQ_SEARCH:
op = 'search';
probeArgs.push(req.scope);
probeArgs.push(req.filter.toString());
break;
case Protocol.LDAP_REQ_UNBIND:
op = 'unbind';
break;
}
res._dtraceOp = op;
dtrace.fire('server-' + op + '-start', function() {
return probeArgs;
});
return args;
}
///--- API
/**
@ -338,7 +355,7 @@ function Server(options) {
res.logId = req.logId;
res.requestDN = req.dn;
var chain = self._getHandlerChain(req);
var chain = self._getHandlerChain(req, res);
var i = 0;
return function(err) {
@ -468,12 +485,7 @@ module.exports = Server;
* @throws {TypeError} on bad input
*/
Server.prototype.add = function(name) {
var args = getArgumentsWithDTrace(Array.prototype.slice.call(arguments, 1),
'add',
function(req, res) {
return req.attributes.length;
});
var args = Array.prototype.slice.call(arguments, 1);
return this._mount(Protocol.LDAP_REQ_ADD, name, args);
};
@ -489,9 +501,7 @@ Server.prototype.add = function(name) {
* @throws {TypeError} on bad input
*/
Server.prototype.bind = function(name) {
var args = getArgumentsWithDTrace(Array.prototype.slice.call(arguments, 1),
'bind');
var args = Array.prototype.slice.call(arguments, 1);
return this._mount(Protocol.LDAP_REQ_BIND, name, args);
};
@ -507,15 +517,7 @@ Server.prototype.bind = function(name) {
* @throws {TypeError} on bad input
*/
Server.prototype.compare = function(name) {
var args = getArgumentsWithDTrace(Array.prototype.slice.call(arguments, 1),
'compare',
function(req, res) {
return req.attribute;
},
function(req, res) {
return req.value;
});
var args = Array.prototype.slice.call(arguments, 1);
return this._mount(Protocol.LDAP_REQ_COMPARE, name, args);
};
@ -531,9 +533,7 @@ Server.prototype.compare = function(name) {
* @throws {TypeError} on bad input
*/
Server.prototype.del = function(name) {
var args = getArgumentsWithDTrace(Array.prototype.slice.call(arguments, 1),
'delete');
var args = Array.prototype.slice.call(arguments, 1);
return this._mount(Protocol.LDAP_REQ_DELETE, name, args);
};
@ -549,15 +549,7 @@ Server.prototype.del = function(name) {
* @throws {TypeError} on bad input.
*/
Server.prototype.exop = function(name) {
var args = getArgumentsWithDTrace(Array.prototype.slice.call(arguments, 1),
'exop',
function(req, res) {
return req.name;
},
function(req, res) {
return req.value;
});
var args = Array.prototype.slice.call(arguments, 1);
return this._mount(Protocol.LDAP_REQ_EXTENSION, name, args, true);
};
@ -573,12 +565,7 @@ Server.prototype.exop = function(name) {
* @throws {TypeError} on bad input
*/
Server.prototype.modify = function(name) {
var args = getArgumentsWithDTrace(Array.prototype.slice.call(arguments, 1),
'modify',
function(req, res) {
return req.changes.length;
});
var args = Array.prototype.slice.call(arguments, 1);
return this._mount(Protocol.LDAP_REQ_MODIFY, name, args);
};
@ -594,16 +581,7 @@ Server.prototype.modify = function(name) {
* @throws {TypeError} on bad input
*/
Server.prototype.modifyDN = function(name) {
var args = getArgumentsWithDTrace(Array.prototype.slice.call(arguments, 1),
'modifydn',
function(req, res) {
return req.newRdn.toString();
},
function(req, res) {
return (req.newSuperior ?
req.newSuperior.toString() : '');
});
var args = Array.prototype.slice.call(arguments, 1);
return this._mount(Protocol.LDAP_REQ_MODRDN, name, args);
};
@ -619,15 +597,7 @@ Server.prototype.modifyDN = function(name) {
* @throws {TypeError} on bad input
*/
Server.prototype.search = function(name) {
var args = getArgumentsWithDTrace(Array.prototype.slice.call(arguments, 1),
'search',
function(req, res) {
return req.scope;
},
function(req, res) {
return req.filter.toString();
});
var args = Array.prototype.slice.call(arguments, 1);
return this._mount(Protocol.LDAP_REQ_SEARCH, name, args);
};
@ -642,9 +612,7 @@ Server.prototype.search = function(name) {
* @throws {TypeError} on bad input
*/
Server.prototype.unbind = function() {
var args = getArgumentsWithDTrace(Array.prototype.slice.call(arguments, 1),
'unbind');
var args = Array.prototype.slice.call(arguments, 0);
return this._mount(Protocol.LDAP_REQ_UNBIND, 'unbind', args, true);
};
@ -726,16 +694,18 @@ Server.prototype._getRoute = function(_dn, backend) {
};
Server.prototype._getHandlerChain = function(req) {
Server.prototype._getHandlerChain = function(req, res) {
assert.ok(req);
fireDTraceProbe(req, res);
// check anonymous bind
if (req.protocolOp === Protocol.LDAP_REQ_BIND &&
req.dn.toString() === '' &&
req.credentials === '') {
return {
backend: self,
handlers: [defaultAnonymousBindHandler]
handlers: [defaultNoOpHandler]
};
}
@ -763,7 +733,7 @@ Server.prototype._getHandlerChain = function(req) {
return routes['unbind'][op];
self.log.debug('%s unbind request %j', req.logId, req.json);
return [defaultUnbindHandler];
return [defaultNoOpHandler];
}
return {
@ -773,7 +743,7 @@ Server.prototype._getHandlerChain = function(req) {
} else if (req.protocolOp === Protocol.LDAP_REQ_ABANDON) {
return {
backend: self,
handlers: [defaultAbandonHandler]
handlers: [defaultNoOpHandler]
};
}
@ -811,7 +781,7 @@ Server.prototype._mount = function(op, name, argv, notDN) {
if (typeof(name) !== 'string')
throw new TypeError('name (string) required');
if (argv.length < 1)
if (!argv.length)
throw new Error('at least one handler required');
var backend = this;

View File

@ -3,7 +3,7 @@
"name": "ldapjs",
"homepage": "http://ldapjs.org",
"description": "LDAP client and server APIs",
"version": "0.3.1",
"version": "0.3.2",
"repository": {
"type": "git",
"url": "git://github.com/mcavage/node-ldapjs.git"