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 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];
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 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
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
Loading…
Reference in New Issue