Refactor server route handling
Force route lookups to proceed lexically through mounted endpoint DNs. Mounting to a null ('') DN will act as the default route for requests which aren't matched by defined routes. Fix mcavage/node-ldapjs#154 Fix mcavage/node-ldapjs#111
This commit is contained in:
parent
0427732c10
commit
e92977b514
112
lib/server.js
112
lib/server.js
|
@ -716,12 +716,45 @@ Server.prototype._getRoute = function (_dn, backend) {
|
|||
this.routes[name] = {};
|
||||
this.routes[name].backend = backend;
|
||||
this.routes[name].dn = _dn;
|
||||
// Force regeneration of the route key cache on next request
|
||||
this._routeKeyCache = null;
|
||||
}
|
||||
|
||||
return this.routes[name];
|
||||
};
|
||||
|
||||
|
||||
Server.prototype._sortedRouteKeys = function _sortedRouteKeys() {
|
||||
// The filtered/sorted route keys are cached to prevent needlessly
|
||||
// regenerating the list for every incoming request.
|
||||
if (!this._routeKeyCache) {
|
||||
var self = this;
|
||||
var reversedRDNsToKeys = {};
|
||||
// Generate mapping of reversedRDNs(DN) -> routeKey
|
||||
Object.keys(this.routes).forEach(function (key) {
|
||||
var _dn = self.routes[key].dn;
|
||||
// Ignore non-DN routes such as exop or unbind
|
||||
if (_dn instanceof dn.DN) {
|
||||
var reversed = _dn.clone();
|
||||
reversed.rdns.reverse();
|
||||
reversedRDNsToKeys[reversed.spaced(true).toString()] = key;
|
||||
}
|
||||
});
|
||||
var output = [];
|
||||
// Reverse-sort on reversedRDS(DN) in order to output routeKey list.
|
||||
// This will place more specific DNs in front of their parents:
|
||||
// 1. dc=test, dc=domain, dc=sub
|
||||
// 2. dc=test, dc=domain
|
||||
// 3. dc=other, dc=foobar
|
||||
Object.keys(reversedRDNsToKeys).sort().reverse().forEach(function (_dn) {
|
||||
output.push(reversedRDNsToKeys[_dn]);
|
||||
});
|
||||
this._routeKeyCache = output;
|
||||
}
|
||||
return this._routeKeyCache;
|
||||
};
|
||||
|
||||
|
||||
Server.prototype._getHandlerChain = function _getHandlerChain(req, res) {
|
||||
assert.ok(req);
|
||||
|
||||
|
@ -738,59 +771,70 @@ Server.prototype._getHandlerChain = function _getHandlerChain(req, res) {
|
|||
}
|
||||
|
||||
var op = '0x' + req.protocolOp.toString(16);
|
||||
|
||||
var self = this;
|
||||
var routes = this.routes;
|
||||
var keys = Object.keys(routes);
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
var r = keys[i];
|
||||
var route = routes[r];
|
||||
var route;
|
||||
|
||||
// Special cases are abandons, exops and unbinds, handle those first.
|
||||
if (req.protocolOp === Protocol.LDAP_REQ_EXTENSION) {
|
||||
if (r === req.requestName) {
|
||||
return {
|
||||
backend: routes.backend,
|
||||
handlers: route[op] || [noExOpHandler]
|
||||
};
|
||||
}
|
||||
} else if (req.protocolOp === Protocol.LDAP_REQ_UNBIND) {
|
||||
// Special cases are exops, unbinds and abandons. Handle those first.
|
||||
if (req.protocolOp === Protocol.LDAP_REQ_EXTENSION) {
|
||||
route = routes[req.requestName];
|
||||
if (route) {
|
||||
return {
|
||||
backend: routes['unbind'] ? routes['unbind'].backend : self,
|
||||
handlers: function getUnbindChain() {
|
||||
if (routes['unbind'] && routes['unbind'][op])
|
||||
return routes['unbind'][op];
|
||||
|
||||
self.log.debug('%s unbind request %j', req.logId, req.json);
|
||||
return [defaultNoOpHandler];
|
||||
}
|
||||
backend: route.backend,
|
||||
handlers: (route[op] ? route[op] : [noExOpHandler])
|
||||
};
|
||||
} else if (req.protocolOp === Protocol.LDAP_REQ_ABANDON) {
|
||||
} else {
|
||||
return {
|
||||
backend: self,
|
||||
handlers: [defaultNoOpHandler]
|
||||
handlers: [noExOpHandler]
|
||||
};
|
||||
} else if (route[op]) {
|
||||
// Otherwise, match via DN rules
|
||||
assert.ok(req.dn);
|
||||
assert.ok(route.dn);
|
||||
}
|
||||
} else if (req.protocolOp === Protocol.LDAP_REQ_UNBIND) {
|
||||
route = routes['unbind'];
|
||||
return {
|
||||
backend: route ? route.backend : self,
|
||||
handlers: function getUnbindChain() {
|
||||
if (route && route[op])
|
||||
return route[op];
|
||||
|
||||
if (route.dn.equals(req.dn) || route.dn.parentOf(req.dn)) {
|
||||
self.log.debug('%s unbind request %j', req.logId, req.json);
|
||||
return [defaultNoOpHandler];
|
||||
}
|
||||
};
|
||||
} else if (req.protocolOp === Protocol.LDAP_REQ_ABANDON) {
|
||||
return {
|
||||
backend: self,
|
||||
handlers: [defaultNoOpHandler]
|
||||
};
|
||||
}
|
||||
|
||||
// Otherwise, match via DN rules
|
||||
assert.ok(req.dn);
|
||||
var keys = this._sortedRouteKeys();
|
||||
var fallbackHandler = [noSuffixHandler];
|
||||
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[op]) {
|
||||
// We should be good to go.
|
||||
req.suffix = route.dn;
|
||||
return {
|
||||
backend: route.backend,
|
||||
handlers: route[op] || [defaultHandler]
|
||||
handlers: route[op]
|
||||
};
|
||||
} else {
|
||||
// We found a valid suffix but not a valid operation.
|
||||
// There might be a more generic suffix with a legitimate operation.
|
||||
fallbackHandler = [defaultHandler];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We're here, so nothing matched.
|
||||
return {
|
||||
backend: self,
|
||||
handlers: [(req.protocolOp !== Protocol.LDAP_REQ_EXTENSION ?
|
||||
noSuffixHandler : noExOpHandler)]
|
||||
handlers: fallbackHandler
|
||||
};
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue