GH-14: Client needs to handle search references

This commit is contained in:
Mark Cavage 2011-09-27 11:49:33 -07:00
parent 50a0c59d1f
commit f4b49a20ae
6 changed files with 197 additions and 15 deletions

View File

@ -33,6 +33,7 @@ var UnbindRequest = messages.UnbindRequest;
var LDAPResult = messages.LDAPResult; var LDAPResult = messages.LDAPResult;
var SearchEntry = messages.SearchEntry; var SearchEntry = messages.SearchEntry;
var SearchReference = messages.SearchReference;
var SearchResponse = messages.SearchResponse; var SearchResponse = messages.SearchResponse;
var Parser = messages.Parser; var Parser = messages.Parser;
@ -587,6 +588,7 @@ Client.prototype._send = function(message, expect, callback, connection) {
self.log.debug('%s: response received: %j', conn.ldap.id, res.json); self.log.debug('%s: response received: %j', conn.ldap.id, res.json);
var err = null; var err = null;
if (res instanceof LDAPResult) { if (res instanceof LDAPResult) {
delete conn.ldap.messages[message.messageID]; delete conn.ldap.messages[message.messageID];
@ -602,11 +604,18 @@ Client.prototype._send = function(message, expect, callback, connection) {
return callback(null, res); return callback(null, res);
callback.emit('end', res); callback.emit('end', res);
} else if (res instanceof SearchEntry) { } else if (res instanceof SearchEntry) {
assert.ok(callback instanceof EventEmitter); assert.ok(callback instanceof EventEmitter);
callback.emit('searchEntry', res); callback.emit('searchEntry', res);
} else if (res instanceof SearchReference) {
assert.ok(callback instanceof EventEmitter);
callback.emit('searchReference', res);
} else if (res instanceof Error) { } else if (res instanceof Error) {
return callback(res); return callback(res);
} else { } else {
delete conn.ldap.messages[message.messageID]; delete conn.ldap.messages[message.messageID];

View File

@ -20,6 +20,7 @@ var ModifyDNRequest = require('./moddn_request');
var ModifyDNResponse = require('./moddn_response'); var ModifyDNResponse = require('./moddn_response');
var SearchRequest = require('./search_request'); var SearchRequest = require('./search_request');
var SearchEntry = require('./search_entry'); var SearchEntry = require('./search_entry');
var SearchReference = require('./search_reference');
var SearchResponse = require('./search_response'); var SearchResponse = require('./search_response');
var UnbindRequest = require('./unbind_request'); var UnbindRequest = require('./unbind_request');
var UnbindResponse = require('./unbind_response'); var UnbindResponse = require('./unbind_response');
@ -50,6 +51,7 @@ module.exports = {
ModifyDNResponse: ModifyDNResponse, ModifyDNResponse: ModifyDNResponse,
SearchRequest: SearchRequest, SearchRequest: SearchRequest,
SearchEntry: SearchEntry, SearchEntry: SearchEntry,
SearchReference: SearchReference,
SearchResponse: SearchResponse, SearchResponse: SearchResponse,
UnbindRequest: UnbindRequest, UnbindRequest: UnbindRequest,
UnbindResponse: UnbindResponse UnbindResponse: UnbindResponse

View File

@ -22,6 +22,7 @@ var ModifyDNRequest = require('./moddn_request');
var ModifyDNResponse = require('./moddn_response'); var ModifyDNResponse = require('./moddn_response');
var SearchRequest = require('./search_request'); var SearchRequest = require('./search_request');
var SearchEntry = require('./search_entry'); var SearchEntry = require('./search_entry');
var SearchReference = require('./search_reference');
var SearchResponse = require('./search_response'); var SearchResponse = require('./search_response');
var UnbindRequest = require('./unbind_request'); var UnbindRequest = require('./unbind_request');
var UnbindResponse = require('./unbind_response'); var UnbindResponse = require('./unbind_response');
@ -211,6 +212,10 @@ Parser.prototype._newMessage = function(ber) {
Message = SearchEntry; Message = SearchEntry;
break; break;
case Protocol.LDAP_REP_SEARCH_REF:
Message = SearchReference;
break;
case Protocol.LDAP_REP_SEARCH: case Protocol.LDAP_REP_SEARCH:
Message = SearchResponse; Message = SearchResponse;
break; break;

View File

@ -0,0 +1,106 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
var assert = require('assert');
var util = require('util');
var asn1 = require('asn1');
var LDAPMessage = require('./message');
var Protocol = require('../protocol');
var dn = require('../dn');
var url = require('../url');
///--- Globals
var BerWriter = asn1.BerWriter;
var parseURL = url.parse;
///--- API
function SearchReference(options) {
if (options) {
if (typeof(options) !== 'object')
throw new TypeError('options must be an object');
if (options.objectName && !(options.objectName instanceof dn.DN))
throw new TypeError('options.objectName must be a DN');
} else {
options = {};
}
options.protocolOp = Protocol.LDAP_REP_SEARCH_REF;
LDAPMessage.call(this, options);
this.uris = options.uris || [];
var self = this;
this.__defineGetter__('type', function() { return 'SearchReference'; });
this.__defineGetter__('object', function() {
return {
dn: self.dn.toString(),
uris: self.uris.slice()
};
});
this.__defineGetter__('_dn', function() {
return new dn.DN('');
});
this.__defineGetter__('urls', function() {
return self.uris;
});
this.__defineSetter__('urls', function(u) {
self.uris = u.slice();
});
}
util.inherits(SearchReference, LDAPMessage);
module.exports = SearchReference;
SearchReference.prototype.toObject = function() {
return this.object;
};
SearchReference.prototype.fromObject = function(obj) {
if (typeof(obj) !== 'object')
throw new TypeError('object required');
this.uris = obj.uris ? obj.uris.slice() : [];
return true;
};
SearchReference.prototype._json = function(j) {
assert.ok(j);
j.uris = this.uris.slice();
return j;
};
SearchReference.prototype._parse = function(ber, length) {
assert.ok(ber);
while (ber.offset < length) {
var _url = ber.readString();
parseURL(_url);
this.uris.push(_url);
}
return true;
};
SearchReference.prototype._toBer = function(ber) {
assert.ok(ber);
this.uris.forEach(function(u) {
ber.writeString(u.href || u);
});
return ber;
};

View File

@ -5,8 +5,10 @@ var util = require('util');
var LDAPResult = require('./result'); var LDAPResult = require('./result');
var SearchEntry = require('./search_entry'); var SearchEntry = require('./search_entry');
var SearchReference = require('./search_reference');
var parseDN = require('../dn').parse; var parseDN = require('../dn').parse;
var parseURL = require('../url').parse;
var Protocol = require('../protocol'); var Protocol = require('../protocol');
@ -45,7 +47,12 @@ SearchResponse.prototype.send = function(entry, nofiltering) {
var self = this; var self = this;
if (!(entry instanceof SearchEntry)) { if (entry instanceof SearchEntry || entry instanceof SearchReference) {
if (!entry.messageID)
entry.messageID = this.messageID;
if (entry.messageID !== this.messageID)
throw new Error('SearchEntry messageID mismatch');
} else {
if (!entry.dn) if (!entry.dn)
throw new Error('entry.dn required'); throw new Error('entry.dn required');
if (!entry.attributes) if (!entry.attributes)
@ -72,11 +79,6 @@ SearchResponse.prototype.send = function(entry, nofiltering) {
log4js: self.log4js log4js: self.log4js
}); });
entry.fromObject(save); entry.fromObject(save);
} else {
if (!entry.messageID)
entry.messageID = this.messageID;
if (entry.messageID !== this.messageID)
throw new Error('SearchEntry messageID mismatch');
} }
try { try {
@ -108,3 +110,24 @@ SearchResponse.prototype.createSearchEntry = function(object) {
return entry; return entry;
}; };
SearchResponse.prototype.createSearchReference = function(uris) {
if (!uris)
throw new TypeError('uris ([string]) required');
if (!Array.isArray(uris))
uris = [uris];
for (var i = 0; i < uris.length; i++) {
if (typeof(uris[i]) == 'string')
uris[i] = parseURL(uris[i]);
}
var self = this;
return new SearchReference({
messageID: self.messageID,
log4js: self.log4js,
uris: uris
});
};

View File

@ -76,15 +76,21 @@ test('setup', function(t) {
}); });
server.search(SUFFIX, function(req, res, next) { server.search(SUFFIX, function(req, res, next) {
var e = res.createSearchEntry({
objectName: req.dn, if (!req.dn.equals('cn=ref,' + SUFFIX)) {
attributes: { var e = res.createSearchEntry({
cn: ['unit', 'test'], objectName: req.dn,
sn: 'testy' attributes: {
} cn: ['unit', 'test'],
}); sn: 'testy'
res.send(e); }
res.send(e); });
res.send(e);
res.send(e);
} else {
res.send(res.createSearchReference('ldap://localhost'));
}
res.end(); res.end();
return next(); return next();
}); });
@ -344,6 +350,37 @@ test('search basic', function(t) {
}); });
test('search referral', function(t) {
client.search('cn=ref, ' + SUFFIX, '(objectclass=*)', function(err, res) {
t.ifError(err);
t.ok(res);
var gotEntry = 0;
var gotReferral = false;
res.on('searchEntry', function(entry) {
gotEntry++;
});
res.on('searchReference', function(referral) {
gotReferral = true;
t.ok(referral);
t.ok(referral instanceof ldap.SearchReference);
t.ok(referral.uris);
t.ok(referral.uris.length);
});
res.on('error', function(err) {
t.fail(err);
});
res.on('end', function(res) {
t.ok(res);
t.ok(res instanceof ldap.SearchResponse);
t.equal(res.status, 0);
t.equal(gotEntry, 0);
t.ok(gotReferral);
t.end();
});
});
});
test('shutdown', function(t) { test('shutdown', function(t) {
client.unbind(function() { client.unbind(function() {
server.on('close', function() { server.on('close', function() {