GH-14: Client needs to handle search references
This commit is contained in:
parent
50a0c59d1f
commit
f4b49a20ae
|
@ -33,6 +33,7 @@ var UnbindRequest = messages.UnbindRequest;
|
|||
|
||||
var LDAPResult = messages.LDAPResult;
|
||||
var SearchEntry = messages.SearchEntry;
|
||||
var SearchReference = messages.SearchReference;
|
||||
var SearchResponse = messages.SearchResponse;
|
||||
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);
|
||||
|
||||
var err = null;
|
||||
|
||||
if (res instanceof LDAPResult) {
|
||||
delete conn.ldap.messages[message.messageID];
|
||||
|
||||
|
@ -602,11 +604,18 @@ Client.prototype._send = function(message, expect, callback, connection) {
|
|||
return callback(null, res);
|
||||
|
||||
callback.emit('end', res);
|
||||
|
||||
} else if (res instanceof SearchEntry) {
|
||||
assert.ok(callback instanceof EventEmitter);
|
||||
callback.emit('searchEntry', res);
|
||||
|
||||
} else if (res instanceof SearchReference) {
|
||||
assert.ok(callback instanceof EventEmitter);
|
||||
callback.emit('searchReference', res);
|
||||
|
||||
} else if (res instanceof Error) {
|
||||
return callback(res);
|
||||
|
||||
} else {
|
||||
delete conn.ldap.messages[message.messageID];
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ var ModifyDNRequest = require('./moddn_request');
|
|||
var ModifyDNResponse = require('./moddn_response');
|
||||
var SearchRequest = require('./search_request');
|
||||
var SearchEntry = require('./search_entry');
|
||||
var SearchReference = require('./search_reference');
|
||||
var SearchResponse = require('./search_response');
|
||||
var UnbindRequest = require('./unbind_request');
|
||||
var UnbindResponse = require('./unbind_response');
|
||||
|
@ -50,6 +51,7 @@ module.exports = {
|
|||
ModifyDNResponse: ModifyDNResponse,
|
||||
SearchRequest: SearchRequest,
|
||||
SearchEntry: SearchEntry,
|
||||
SearchReference: SearchReference,
|
||||
SearchResponse: SearchResponse,
|
||||
UnbindRequest: UnbindRequest,
|
||||
UnbindResponse: UnbindResponse
|
||||
|
|
|
@ -22,6 +22,7 @@ var ModifyDNRequest = require('./moddn_request');
|
|||
var ModifyDNResponse = require('./moddn_response');
|
||||
var SearchRequest = require('./search_request');
|
||||
var SearchEntry = require('./search_entry');
|
||||
var SearchReference = require('./search_reference');
|
||||
var SearchResponse = require('./search_response');
|
||||
var UnbindRequest = require('./unbind_request');
|
||||
var UnbindResponse = require('./unbind_response');
|
||||
|
@ -211,6 +212,10 @@ Parser.prototype._newMessage = function(ber) {
|
|||
Message = SearchEntry;
|
||||
break;
|
||||
|
||||
case Protocol.LDAP_REP_SEARCH_REF:
|
||||
Message = SearchReference;
|
||||
break;
|
||||
|
||||
case Protocol.LDAP_REP_SEARCH:
|
||||
Message = SearchResponse;
|
||||
break;
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -5,8 +5,10 @@ var util = require('util');
|
|||
|
||||
var LDAPResult = require('./result');
|
||||
var SearchEntry = require('./search_entry');
|
||||
var SearchReference = require('./search_reference');
|
||||
|
||||
var parseDN = require('../dn').parse;
|
||||
var parseURL = require('../url').parse;
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
|
||||
|
@ -45,7 +47,12 @@ SearchResponse.prototype.send = function(entry, nofiltering) {
|
|||
|
||||
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)
|
||||
throw new Error('entry.dn required');
|
||||
if (!entry.attributes)
|
||||
|
@ -72,11 +79,6 @@ SearchResponse.prototype.send = function(entry, nofiltering) {
|
|||
log4js: self.log4js
|
||||
});
|
||||
entry.fromObject(save);
|
||||
} else {
|
||||
if (!entry.messageID)
|
||||
entry.messageID = this.messageID;
|
||||
if (entry.messageID !== this.messageID)
|
||||
throw new Error('SearchEntry messageID mismatch');
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -108,3 +110,24 @@ SearchResponse.prototype.createSearchEntry = function(object) {
|
|||
|
||||
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
|
||||
});
|
||||
};
|
||||
|
|
|
@ -76,6 +76,8 @@ test('setup', function(t) {
|
|||
});
|
||||
|
||||
server.search(SUFFIX, function(req, res, next) {
|
||||
|
||||
if (!req.dn.equals('cn=ref,' + SUFFIX)) {
|
||||
var e = res.createSearchEntry({
|
||||
objectName: req.dn,
|
||||
attributes: {
|
||||
|
@ -85,6 +87,10 @@ test('setup', function(t) {
|
|||
});
|
||||
res.send(e);
|
||||
res.send(e);
|
||||
} else {
|
||||
res.send(res.createSearchReference('ldap://localhost'));
|
||||
}
|
||||
|
||||
res.end();
|
||||
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) {
|
||||
client.unbind(function() {
|
||||
server.on('close', function() {
|
||||
|
|
Loading…
Reference in New Issue