Initial working client/server version
This commit is contained in:
commit
ca1443f102
|
@ -0,0 +1,3 @@
|
|||
node_modules
|
||||
*.log
|
||||
*.ldif
|
|
@ -0,0 +1,19 @@
|
|||
Copyright (c) 2011 Mark Cavage, All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE
|
|
@ -0,0 +1,13 @@
|
|||
node-ldapjs will blow your mind. Docs coming soon.
|
||||
|
||||
## Installation
|
||||
|
||||
npm install ldapjs
|
||||
|
||||
## License
|
||||
|
||||
MIT.
|
||||
|
||||
## Bugs
|
||||
|
||||
See <https://github.com/mcavage/node-ldapjs/issues>.
|
|
@ -0,0 +1,104 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var assert = require('assert');
|
||||
|
||||
var Protocol = require('./protocol');
|
||||
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
function Attribute(options) {
|
||||
if (options) {
|
||||
if (typeof(options) !== 'object')
|
||||
throw new TypeError('options must be an object');
|
||||
if (options.type && typeof(options.type) !== 'string')
|
||||
throw new TypeError('options.type must be a string');
|
||||
if (options.vals && !Array.isArray(options.vals))
|
||||
throw new TypeErrr('options.vals must be an array[string]');
|
||||
if (options.vals && options.vals.length) {
|
||||
options.vals.forEach(function(v) {
|
||||
if (typeof(v) !== 'string')
|
||||
throw new TypeErrr('options.vals must be an array[string]');
|
||||
});
|
||||
}
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
|
||||
this.type = options.type || '';
|
||||
this.vals = options.vals ? options.vals.slice(0) : [];
|
||||
|
||||
var self = this;
|
||||
this.__defineGetter__('json', function() {
|
||||
return {
|
||||
type: self.type,
|
||||
vals: self.vals
|
||||
};
|
||||
});
|
||||
}
|
||||
module.exports = Attribute;
|
||||
|
||||
|
||||
Attribute.prototype.addValue = function(val) {
|
||||
if (typeof(val) !== 'string')
|
||||
throw new TypeError('val (string) required');
|
||||
|
||||
this.vals.push(val);
|
||||
};
|
||||
|
||||
|
||||
Attribute.prototype.parse = function(ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
ber.readSequence();
|
||||
this.type = ber.readString();
|
||||
|
||||
|
||||
if (ber.readSequence(Protocol.LBER_SET)) {
|
||||
var end = ber.offset + ber.length;
|
||||
while (ber.offset < end) {
|
||||
var val = ber.readString();
|
||||
this.vals.push(val);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
Attribute.prototype.toBer = function(ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
ber.startSequence();
|
||||
ber.writeString(this.type);
|
||||
if (this.vals && this.vals.length) {
|
||||
ber.startSequence(Protocol.LBER_SET);
|
||||
ber.writeStringArray(this.vals);
|
||||
ber.endSequence();
|
||||
}
|
||||
ber.endSequence();
|
||||
|
||||
return ber;
|
||||
};
|
||||
|
||||
Attribute.toBer = function(attr, ber) {
|
||||
return Attribute.prototype.toBer.call(attr, ber);
|
||||
};
|
||||
|
||||
|
||||
Attribute.isAttribute = function(attr) {
|
||||
if (typeof(attr) !== 'object') return false;
|
||||
if (attr instanceof Attribute) return true;
|
||||
if (!attr.type || typeof(attr.type) !== 'string') return false;
|
||||
if (!attr.vals || !Array.isArray(attr.vals)) return false;
|
||||
for (var i = 0; i < attr.vals.length; i++)
|
||||
if (typeof(attr.vals[i]) !== 'string') return false;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
Attribute.prototype.toString = function() {
|
||||
return JSON.stringify(this.json);
|
||||
};
|
|
@ -0,0 +1,86 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var assert = require('assert');
|
||||
|
||||
var Attribute = require('./attribute');
|
||||
var Protocol = require('./protocol');
|
||||
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
function Change(options) {
|
||||
if (options) {
|
||||
if (typeof(options) !== 'object')
|
||||
throw new TypeError('options must be an object');
|
||||
if (options.operation && typeof(options.operation) !== 'string')
|
||||
throw new TypeError('options.operation must be a string');
|
||||
if (options.modification && !(options.modification instanceof Attribute))
|
||||
throw new TypeErrr('options.modification must be an Attribute');
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
|
||||
var self = this;
|
||||
this.__defineGetter__('operation', function() {
|
||||
switch (self._operation) {
|
||||
case 0x00: return 'Add';
|
||||
case 0x01: return 'Delete';
|
||||
case 0x02: return 'Replace';
|
||||
default: return 'Invalid';
|
||||
}
|
||||
});
|
||||
this.__defineSetter__('operation', function(val) {
|
||||
if (typeof(val) !== 'string')
|
||||
throw new TypeError('operation must be a string');
|
||||
|
||||
switch (val.toLowerCase()) {
|
||||
case 'add':
|
||||
self._operation = 0x00;
|
||||
break;
|
||||
case 'delete':
|
||||
self._operation = 0x01;
|
||||
break;
|
||||
case 'replace':
|
||||
self._operation = 0x02;
|
||||
break;
|
||||
default:
|
||||
throw new Error('Invalid operation type: 0x' + val.toString(16));
|
||||
}
|
||||
});
|
||||
|
||||
this.__defineGetter__('json', function() {
|
||||
return {
|
||||
operation: self.operation,
|
||||
modification: self.modification ? self.modification.json : {}
|
||||
};
|
||||
});
|
||||
|
||||
this.operation = options.operation || 'add';
|
||||
this.modification = options.modification || null;
|
||||
}
|
||||
module.exports = Change;
|
||||
|
||||
|
||||
Change.prototype.parse = function(ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
ber.readSequence();
|
||||
this._operation = ber.readEnumeration();
|
||||
this.modification = new Attribute();
|
||||
this.modification.parse(ber);
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
Change.prototype.toBer = function(ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
ber.startSequence();
|
||||
ber.writeEnumeration(this._operation);
|
||||
ber = this.modification.toBer(ber);
|
||||
ber.endSequence();
|
||||
|
||||
return ber;
|
||||
};
|
|
@ -0,0 +1,726 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var assert = require('assert');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var net = require('net');
|
||||
var tls = require('tls');
|
||||
var util = require('util');
|
||||
|
||||
var Attribute = require('./attribute');
|
||||
var Change = require('./change');
|
||||
var Control = require('./control');
|
||||
var Protocol = require('./protocol');
|
||||
var dn = require('./dn');
|
||||
var errors = require('./errors');
|
||||
var filters = require('./filters');
|
||||
var logStub = require('./log_stub');
|
||||
var messages = require('./messages');
|
||||
var url = require('./url');
|
||||
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var AddRequest = messages.AddRequest;
|
||||
var BindRequest = messages.BindRequest;
|
||||
var CompareRequest = messages.CompareRequest;
|
||||
var DeleteRequest = messages.DeleteRequest;
|
||||
var ExtendedRequest = messages.ExtendedRequest;
|
||||
var ModifyRequest = messages.ModifyRequest;
|
||||
var ModifyDNRequest = messages.ModifyDNRequest;
|
||||
var SearchRequest = messages.SearchRequest;
|
||||
var UnbindRequest = messages.UnbindRequest;
|
||||
|
||||
var LDAPResult = messages.LDAPResult;
|
||||
var SearchEntry = messages.SearchEntry;
|
||||
var SearchResponse = messages.SearchResponse;
|
||||
var Parser = messages.Parser;
|
||||
|
||||
|
||||
var Filter = filters.Filter;
|
||||
var PresenceFilter = filters.PresenceFilter;
|
||||
|
||||
|
||||
var MAX_MSGID = Math.pow(2, 31) - 1;
|
||||
|
||||
|
||||
|
||||
///--- Internal Helpers
|
||||
|
||||
function xor() {
|
||||
var b = false;
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
if (arguments[i] && !b) b = true;
|
||||
else if (arguments[i] && b) return false;
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
function validateControls(controls) {
|
||||
if (Array.isArray(controls)) {
|
||||
controls.forEach(function(c) {
|
||||
if (!(c instanceof Control))
|
||||
throw new TypeError('controls must be [Control]');
|
||||
});
|
||||
} else if (controls instanceof Control) {
|
||||
controls = [controls];
|
||||
} else {
|
||||
throw new TypeError('controls must be [Control]');
|
||||
}
|
||||
|
||||
return controls;
|
||||
}
|
||||
|
||||
|
||||
function DisconnectedError(message) {
|
||||
Error.call(this, message);
|
||||
|
||||
if (Error.captureStackTrace)
|
||||
Error.captureStackTrace(this, DisconnectedError);
|
||||
}
|
||||
util.inherits(DisconnectedError, Error);
|
||||
|
||||
///--- API
|
||||
|
||||
/**
|
||||
* Constructs a new client.
|
||||
*
|
||||
* The options object is required, and must contain either a URL (string) or
|
||||
* a socketPath (string); the socketPath is only if you want to talk to an LDAP
|
||||
* server over a Unix Domain Socket. Additionally, you can pass in a log4js
|
||||
* option that is the result of `require('log4js')`, presumably after you've
|
||||
* configured it.
|
||||
*
|
||||
* @param {Object} options must have either url or socketPath.
|
||||
* @throws {TypeError} on bad input.
|
||||
*/
|
||||
function Client(options) {
|
||||
if (!options || typeof(options) !== 'object')
|
||||
throw new TypeError('options (object) required');
|
||||
if (options.url && typeof(options.url) !== 'string')
|
||||
throw new TypeError('options.url (string) required');
|
||||
if (options.socketPath && typeof(options.socketPath) !== 'string')
|
||||
throw new TypeError('options.socketPath must be a string');
|
||||
if (options.log4js && typeof(options.log4js) !== 'object')
|
||||
throw new TypeError('options.log4s must be an object');
|
||||
if (options.numConnections && typeof(options.numConnections) !== 'number')
|
||||
throw new TypeError('options.numConnections must be a number');
|
||||
if (!xor(options.url, options.socketPath))
|
||||
throw new TypeError('options.url ^ options.socketPath required');
|
||||
|
||||
EventEmitter.call(this, options);
|
||||
|
||||
var self = this;
|
||||
this.secure = false;
|
||||
if (options.url) {
|
||||
this.url = url.parse(options.url);
|
||||
this.secure = this.url.secure;
|
||||
}
|
||||
|
||||
this.log4js = options.log4js || logStub;
|
||||
this.numConnections = Math.abs(options.numConnections) || 3;
|
||||
this.connections = [];
|
||||
this.currentConnection = 0;
|
||||
this.connectOptions = options.socketPath ? options.socketPath : {
|
||||
port: self.url.port,
|
||||
host: self.url.hostname
|
||||
};
|
||||
this.shutdown = false;
|
||||
|
||||
this.__defineGetter__('log', function() {
|
||||
if (!self._log)
|
||||
self._log = self.log4js.getLogger('LDAPClient');
|
||||
|
||||
return self._log;
|
||||
});
|
||||
|
||||
// Build the connection pool
|
||||
function newConnection() {
|
||||
var c;
|
||||
if (self.secure) {
|
||||
c = tls.createConnection(self.connectOptions);
|
||||
} else {
|
||||
c = net.createConnection(self.connectOptions);
|
||||
}
|
||||
assert.ok(c);
|
||||
|
||||
c.parser = new Parser({
|
||||
log4js: self.log4js
|
||||
});
|
||||
|
||||
// Wrap the events
|
||||
c.ldap = {
|
||||
id: options.socketPath || self.url.hostname,
|
||||
connected: true, // lie, but node queues for us
|
||||
messageID: 0,
|
||||
messages: {}
|
||||
};
|
||||
c.ldap.__defineGetter__('nextMessageID', function() {
|
||||
if (++c.ldap.messageID >= MAX_MSGID)
|
||||
c.ldap.messageID = 1;
|
||||
return c.ldap.messageID;
|
||||
});
|
||||
c.on('connect', function() {
|
||||
c.ldap.connected = true;
|
||||
c.ldap.id += ':' + (c.type !== 'unix' ? c.remotePort : c.fd);
|
||||
self.emit('connect', c.ldap.id);
|
||||
});
|
||||
c.on('end', function() {
|
||||
self.log.trace('%s end', c.ldap.id);
|
||||
c.ldap.connected = false;
|
||||
if (!self.shutdown)
|
||||
c.connect();
|
||||
});
|
||||
c.addListener('close', function(had_err) {
|
||||
self.log.trace('%s close; had_err=%j', c.ldap.id, had_err);
|
||||
c.ldap.connected = false;
|
||||
if (!self.shutdown)
|
||||
c.connect();
|
||||
});
|
||||
c.on('error', function(err) {
|
||||
self.log.warn('%s unexpected connection error %s', c.ldap.id, err);
|
||||
self.emit('error', err, c.ldap.id);
|
||||
c.ldap.connected = false;
|
||||
if (!self.shutdown) {
|
||||
c.end();
|
||||
c.connect();
|
||||
}
|
||||
});
|
||||
c.on('timeout', function() {
|
||||
self.log.trace('%s timed out', c.ldap.id);
|
||||
c.ldap.connected = false;
|
||||
if (!self.shutdown) {
|
||||
c.end();
|
||||
c.connect();
|
||||
}
|
||||
});
|
||||
c.on('data', function(data) {
|
||||
if (self.log.isTraceEnabled())
|
||||
self.log.trace('data on %s: %s', c.ldap.id, util.inspect(data));
|
||||
c.parser.write(data);
|
||||
});
|
||||
|
||||
// The "router"
|
||||
c.parser.on('message', function(message) {
|
||||
message.connection = c;
|
||||
|
||||
var callback = c.ldap.messages[message.messageID];
|
||||
if (!callback) {
|
||||
self.log.error('%s: received unsolicited message: %j',
|
||||
c.ldap.id, message.json);
|
||||
return;
|
||||
}
|
||||
|
||||
return callback(message);
|
||||
});
|
||||
return c;
|
||||
}
|
||||
|
||||
for (var i = 0; i < this.numConnections; i++) {
|
||||
self.connections.push(newConnection());
|
||||
}
|
||||
}
|
||||
util.inherits(Client, EventEmitter);
|
||||
module.exports = Client;
|
||||
|
||||
|
||||
/**
|
||||
* Performs a simple authentication against the server.
|
||||
*
|
||||
* @param {String} name the DN to bind as.
|
||||
* @param {String} credentials the userPassword associated with name.
|
||||
* @param {Control} controls (optional) either a Control or [Control].
|
||||
* @param {Function} callback of the form f(err, res).
|
||||
* @throws {TypeError} on invalid input.
|
||||
*/
|
||||
Client.prototype.bind = function(name, credentials, controls, callback) {
|
||||
if (typeof(name) !== 'string')
|
||||
throw new TypeError('name (string) required');
|
||||
if (typeof(credentials) !== 'string')
|
||||
throw new TypeError('credentials (string) required');
|
||||
if (typeof(controls) === 'function') {
|
||||
callback = controls;
|
||||
controls = [];
|
||||
} else {
|
||||
control = validateControls(controls);
|
||||
}
|
||||
if (typeof(callback) !== 'function')
|
||||
throw new TypeError('callback (function) required');
|
||||
|
||||
var self = this;
|
||||
|
||||
var req = new BindRequest({
|
||||
name: dn.parse(name),
|
||||
authentication: 'Simple',
|
||||
credentials: credentials,
|
||||
controls: controls
|
||||
});
|
||||
|
||||
var cbIssued = false;
|
||||
var finished = 0;
|
||||
function _callback(err, res) {
|
||||
if (err) {
|
||||
if (!cbIssued) {
|
||||
cbIssued = true;
|
||||
return callback(err);
|
||||
}
|
||||
}
|
||||
|
||||
if (++finished >= self.connections.length && !cbIssued) {
|
||||
cbIssued = true;
|
||||
return callback(null, res);
|
||||
}
|
||||
}
|
||||
|
||||
this.connections.forEach(function(c) {
|
||||
return self._send(req, [errors.LDAP_SUCCESS], _callback, c);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds an entry to the LDAP server.
|
||||
*
|
||||
* @param {String} name the DN of the entry to add.
|
||||
* @param {Array} attributes an array of Attributes to be added.
|
||||
* @param {Control} controls (optional) either a Control or [Control].
|
||||
* @param {Function} callback of the form f(err, res).
|
||||
* @throws {TypeError} on invalid input.
|
||||
*/
|
||||
Client.prototype.add = function(name, attributes, controls, callback) {
|
||||
if (typeof(name) !== 'string')
|
||||
throw new TypeError('name (string) required');
|
||||
if (!Array.isArray(attributes))
|
||||
throw new TypeError('attributes ([Attribute]) required');
|
||||
attributes.forEach(function(a) {
|
||||
if (!Attribute.isAttribute(a))
|
||||
throw new TypeError('attributes ([Attribute]) required');
|
||||
});
|
||||
if (typeof(controls) === 'function') {
|
||||
callback = controls;
|
||||
controls = [];
|
||||
} else {
|
||||
control = validateControls(controls);
|
||||
}
|
||||
if (typeof(callback) !== 'function')
|
||||
throw new TypeError('callback (function) required');
|
||||
|
||||
var req = new AddRequest({
|
||||
entry: dn.parse(name),
|
||||
attributes: attributes,
|
||||
controls: controls
|
||||
});
|
||||
|
||||
return this._send(req, [errors.LDAP_SUCCESS], callback);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Compares an attribute/value pair with an entry on the LDAP server.
|
||||
*
|
||||
* @param {String} name the DN of the entry to compare attributes with.
|
||||
* @param {String} attribute name of an attribute to check.
|
||||
* @param {String} value value of an attribute to check.
|
||||
* @param {Control} controls (optional) either a Control or [Control].
|
||||
* @param {Function} callback of the form f(err, boolean, res).
|
||||
* @throws {TypeError} on invalid input.
|
||||
*/
|
||||
Client.prototype.compare = function(name,
|
||||
attribute,
|
||||
value,
|
||||
controls,
|
||||
callback) {
|
||||
if (typeof(name) !== 'string')
|
||||
throw new TypeError('name (string) required');
|
||||
if (typeof(attribute) !== 'string')
|
||||
throw new TypeError('attribute (string) required');
|
||||
if (typeof(value) !== 'string')
|
||||
throw new TypeError('value (string) required');
|
||||
if (typeof(controls) === 'function') {
|
||||
callback = controls;
|
||||
controls = [];
|
||||
} else {
|
||||
control = validateControls(controls);
|
||||
}
|
||||
if (typeof(callback) !== 'function')
|
||||
throw new TypeError('callback (function) required');
|
||||
|
||||
var req = new CompareRequest({
|
||||
entry: dn.parse(name),
|
||||
attribute: attribute,
|
||||
value: value,
|
||||
controls: controls
|
||||
});
|
||||
|
||||
function _callback(err, res) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
return callback(null, (res.status === errors.LDAP_COMPARE_TRUE), res);
|
||||
}
|
||||
|
||||
return this._send(req,
|
||||
[errors.LDAP_COMPARE_TRUE, errors.LDAP_COMPARE_FALSE],
|
||||
_callback);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Deletes an entry from the LDAP server.
|
||||
*
|
||||
* @param {String} name the DN of the entry to delete.
|
||||
* @param {Control} controls (optional) either a Control or [Control].
|
||||
* @param {Function} callback of the form f(err, res).
|
||||
* @throws {TypeError} on invalid input.
|
||||
*/
|
||||
Client.prototype.del = function(name, controls, callback) {
|
||||
if (typeof(name) !== 'string')
|
||||
throw new TypeError('name (string) required');
|
||||
if (typeof(controls) === 'function') {
|
||||
callback = controls;
|
||||
controls = [];
|
||||
} else {
|
||||
control = validateControls(controls);
|
||||
}
|
||||
if (typeof(callback) !== 'function')
|
||||
throw new TypeError('callback (function) required');
|
||||
|
||||
var req = new DeleteRequest({
|
||||
entry: dn.parse(name),
|
||||
controls: controls
|
||||
});
|
||||
|
||||
return this._send(req, [errors.LDAP_SUCCESS], callback);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Performs an extended operation on the LDAP server.
|
||||
*
|
||||
* Pretty much none of the LDAP extended operations return an OID
|
||||
* (responseName), so I just don't bother giving it back in the callback.
|
||||
* It's on the third param in `res` if you need it.
|
||||
*
|
||||
* @param {String} name the OID of the extended operation to perform.
|
||||
* @param {String} value value to pass in for this operation.
|
||||
* @param {Control} controls (optional) either a Control or [Control].
|
||||
* @param {Function} callback of the form f(err, value, res).
|
||||
* @throws {TypeError} on invalid input.
|
||||
*/
|
||||
Client.prototype.exop = function(name, value, controls, callback) {
|
||||
if (typeof(name) !== 'string')
|
||||
throw new TypeError('name (string) required');
|
||||
if (typeof(value) === 'function') {
|
||||
callback = value;
|
||||
controls = [];
|
||||
value = '';
|
||||
}
|
||||
if (typeof(value) !== 'string')
|
||||
throw new TypeError('value (string) required');
|
||||
if (typeof(controls) === 'function') {
|
||||
callback = controls;
|
||||
controls = [];
|
||||
} else {
|
||||
control = validateControls(controls);
|
||||
}
|
||||
if (typeof(callback) !== 'function')
|
||||
throw new TypeError('callback (function) required');
|
||||
|
||||
var req = new ExtendedRequest({
|
||||
requestName: name,
|
||||
requestValue: value,
|
||||
controls: controls
|
||||
});
|
||||
|
||||
function _callback(err, res) {
|
||||
if (err)
|
||||
return callback(err);
|
||||
|
||||
return callback(null, res.responseValue || '', res);
|
||||
}
|
||||
|
||||
return this._send(req, [errors.LDAP_SUCCESS], _callback);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Performs an LDAP modify against the server.
|
||||
*
|
||||
* @param {String} name the DN of the entry to modify.
|
||||
* @param {Change} change update to perform (can be [Change]).
|
||||
* @param {Control} controls (optional) either a Control or [Control].
|
||||
* @param {Function} callback of the form f(err, res).
|
||||
* @throws {TypeError} on invalid input.
|
||||
*/
|
||||
Client.prototype.modify = function(name, change, controls, callback) {
|
||||
if (typeof(name) !== 'string')
|
||||
throw new TypeError('name (string) required');
|
||||
if (!Array.isArray(change) && !(change instanceof Change))
|
||||
throw new TypeError('change (Change) required');
|
||||
if (!Array.isArray(change)) {
|
||||
var save = change;
|
||||
change = [];
|
||||
change.push(save);
|
||||
}
|
||||
change.forEach(function(c) {
|
||||
if (!(c instanceof Change))
|
||||
throw new TypeError('change ([Change]) required');
|
||||
});
|
||||
if (typeof(controls) === 'function') {
|
||||
callback = controls;
|
||||
controls = [];
|
||||
} else {
|
||||
control = validateControls(controls);
|
||||
}
|
||||
if (typeof(callback) !== 'function')
|
||||
throw new TypeError('callback (function) required');
|
||||
|
||||
var req = new ModifyRequest({
|
||||
object: dn.parse(name),
|
||||
changes: change,
|
||||
controls: controls
|
||||
});
|
||||
|
||||
return this._send(req, [errors.LDAP_SUCCESS], callback);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Performs an LDAP modifyDN against the server.
|
||||
*
|
||||
* This does not allow you to keep the old DN, as while the LDAP protocol
|
||||
* has a facility for that, it's stupid. Just Search/Add.
|
||||
*
|
||||
* This will automatically deal with "new superior" logic.
|
||||
*
|
||||
* @param {String} name the DN of the entry to modify.
|
||||
* @param {String} newName the new DN to move this entry to.
|
||||
* @param {Control} controls (optional) either a Control or [Control].
|
||||
* @param {Function} callback of the form f(err, res).
|
||||
* @throws {TypeError} on invalid input.
|
||||
*/
|
||||
Client.prototype.modifyDN = function(name, newName, controls, callback) {
|
||||
if (typeof(name) !== 'string')
|
||||
throw new TypeError('name (string) required');
|
||||
if (typeof(newName) !== 'string')
|
||||
throw new TypeError('newName (string) required');
|
||||
if (typeof(controls) === 'function') {
|
||||
callback = controls;
|
||||
controls = [];
|
||||
} else {
|
||||
control = validateControls(controls);
|
||||
}
|
||||
if (typeof(callback) !== 'function')
|
||||
throw new TypeError('callback (function) required');
|
||||
|
||||
var DN = dn.parse(name);
|
||||
var newDN = dn.parse(newName);
|
||||
|
||||
var req = new ModifyDNRequest({
|
||||
entry: DN,
|
||||
deleteOldRdn: true,
|
||||
controls: controls
|
||||
});
|
||||
|
||||
if (newDN.length !== 1) {
|
||||
req.newRdn = dn.parse(newDN.rdns.shift().toString());
|
||||
req.newSuperior = newDN;
|
||||
} else {
|
||||
req.newRdn = newDN;
|
||||
}
|
||||
|
||||
return this._send(req, [errors.LDAP_SUCCESS], callback);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Performs an LDAP search against the server.
|
||||
*
|
||||
* Note that the defaults for options are a 'base' search, if that's what
|
||||
* you want you can just pass in a string for options and it will be treated
|
||||
* as the search filter. Also, you can either pass in programatic Filter
|
||||
* objects or a filter string as the filter option.
|
||||
*
|
||||
* Note that this method is 'special' in that the callback 'res' param will
|
||||
* have two important events on it, namely 'entry' and 'end' that you can hook
|
||||
* to. The former will emit a SearchEntry object for each record that comes
|
||||
* back, and the latter will emit a normal LDAPResult object.
|
||||
*
|
||||
* @param {String} base the DN in the tree to start searching at.
|
||||
* @param {Object} options parameters:
|
||||
* - {String} scope default of 'base'.
|
||||
* - {String} filter default of '(objectclass=*)'.
|
||||
* - {Array} attributes [string] to return.
|
||||
* - {Boolean} attrsOnly whether to return values.
|
||||
* @param {Control} controls (optional) either a Control or [Control].
|
||||
* @param {Function} callback of the form f(err, res).
|
||||
* @throws {TypeError} on invalid input.
|
||||
*/
|
||||
Client.prototype.search = function(base, options, controls, callback) {
|
||||
if (typeof(base) !== 'string')
|
||||
throw new TypeError('base (string) required');
|
||||
if (Array.isArray(options) || (options instanceof Control)) {
|
||||
controls = options;
|
||||
options = {};
|
||||
} else if (typeof(options) === 'function') {
|
||||
callback = options;
|
||||
controls = [];
|
||||
options = {
|
||||
filter: new PresenceFilter({attribute: 'objectclass'})
|
||||
};
|
||||
} else if (typeof(options) === 'string') {
|
||||
options = {filter: filters.parseString(options)};
|
||||
} else if (typeof(options) !== 'object') {
|
||||
throw new TypeError('options (object) required');
|
||||
}
|
||||
if (!(options.filter instanceof Filter))
|
||||
throw new TypeError('options.filter (Filter) required');
|
||||
if (typeof(controls) === 'function') {
|
||||
callback = controls;
|
||||
controls = [];
|
||||
} else {
|
||||
control = validateControls(controls);
|
||||
}
|
||||
if (typeof(callback) !== 'function')
|
||||
throw new TypeError('callback (function) required');
|
||||
|
||||
var req = new SearchRequest({
|
||||
baseObject: dn.parse(base),
|
||||
scope: options.scope || 'base',
|
||||
filter: options.filter,
|
||||
derefAliases: Protocol.NEVER_DEREF_ALIASES,
|
||||
sizeLimit: options.sizeLimit || 0,
|
||||
timeLimit: options.timeLimit || 10,
|
||||
typesOnly: options.typesOnly || false,
|
||||
attributes: options.attributes || []
|
||||
});
|
||||
|
||||
var res = new EventEmitter();
|
||||
this._send(req, [errors.LDAP_SUCCESS], res);
|
||||
return callback(null, res);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Unbinds this client from the LDAP server.
|
||||
*
|
||||
* Note that unbind does not have a response, so this callback is actually
|
||||
* optional; either way, the client is disconnected.
|
||||
*
|
||||
* @param {Function} callback of the form f(err).
|
||||
* @throws {TypeError} if you pass in callback as not a function.
|
||||
*/
|
||||
Client.prototype.unbind = function(callback) {
|
||||
if (callback && typeof(callback) !== 'function')
|
||||
throw new TypeError('callback must be a function');
|
||||
|
||||
var self = this;
|
||||
|
||||
if (!callback)
|
||||
callback = function defUnbindCb() { self.log.trace('disconnected'); };
|
||||
|
||||
this.shutdown = true;
|
||||
var req = new UnbindRequest();
|
||||
var finished = 0;
|
||||
var cbIssued = false;
|
||||
function _callback(err, res) {
|
||||
if (err) {
|
||||
if (!cbIssued) {
|
||||
cbIssued = true;
|
||||
return callback(err);
|
||||
}
|
||||
}
|
||||
|
||||
if (++finished >= self.connections.length && !cbIssued) {
|
||||
cbIssued = true;
|
||||
return callback(null);
|
||||
}
|
||||
}
|
||||
|
||||
this.connections.forEach(function(c) {
|
||||
return self._send(req, 'unbind', _callback, c);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
Client.prototype._send = function(message, expect, callback, conn) {
|
||||
assert.ok(message);
|
||||
assert.ok(expect);
|
||||
assert.ok(callback);
|
||||
|
||||
var self = this;
|
||||
|
||||
// First select a connection
|
||||
// Note bind and unbind are special in that they will pass in the
|
||||
// connection since they iterate over the whole pool
|
||||
if (!conn) {
|
||||
function nextConn() {
|
||||
if (++self.currentConnection >= self.connections.length)
|
||||
self.currentConnection = 0;
|
||||
|
||||
return self.connections[self.currentConnection];
|
||||
}
|
||||
|
||||
var save = this.currentConnection;
|
||||
while ((conn = nextConn()) && save !== this.currentConnection);
|
||||
|
||||
if (!conn) {
|
||||
self.emit('error', new DisconnectedError('No connections available'));
|
||||
return;
|
||||
}
|
||||
}
|
||||
assert.ok(conn);
|
||||
|
||||
// Now set up the callback in the messages table
|
||||
message.messageID = conn.ldap.nextMessageID;
|
||||
conn.ldap.messages[message.messageID] = function(res) {
|
||||
if (self.log.isDebugEnabled())
|
||||
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];
|
||||
|
||||
if (expect.indexOf(res.status) === -1) {
|
||||
err = errors.getError(res);
|
||||
if (typeof(callback) === 'function')
|
||||
return callback(err);
|
||||
|
||||
return callback.emit('error', err);
|
||||
}
|
||||
|
||||
if (typeof(callback) === 'function')
|
||||
return callback(null, res);
|
||||
|
||||
callback.emit('end', res);
|
||||
} else if (res instanceof SearchEntry) {
|
||||
assert.ok(callback instanceof EventEmitter);
|
||||
callback.emit('searchEntry', res);
|
||||
} else {
|
||||
delete conn.ldap.messages[message.messageID];
|
||||
|
||||
err = new errors.ProtocolError(res.type);
|
||||
if (typeof(callback) === 'function')
|
||||
return callback(err);
|
||||
|
||||
callback.emit('error', err);
|
||||
}
|
||||
};
|
||||
|
||||
// Finally send some data
|
||||
if (this.log.isDebugEnabled())
|
||||
this.log.debug('%s: sending request: %j', conn.ldap.id, message.json);
|
||||
|
||||
// Note if this was an unbind, we just go ahead and end, since there
|
||||
// will never be a response
|
||||
return conn.write(message.toBer(), (expect === 'unbind' ? function() {
|
||||
conn.on('end', function() {
|
||||
self.emit('unbind');
|
||||
return callback();
|
||||
});
|
||||
conn.end();
|
||||
} : null));
|
||||
};
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var assert = require('assert');
|
||||
var util = require('util');
|
||||
|
||||
var asn1 = require('asn1');
|
||||
|
||||
var Protocol = require('./protocol');
|
||||
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var Ber = asn1.Ber;
|
||||
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
function Control(options) {
|
||||
if (options) {
|
||||
if (typeof(options) !== 'object')
|
||||
throw new TypeError('options must be an object');
|
||||
if (options.type && typeof(options.type) !== 'string')
|
||||
throw new TypeError('options.type must be a string');
|
||||
if (options.criticality !== undefined &&
|
||||
typeof(options.criticality) !== 'boolean')
|
||||
throw new TypeError('options.criticality must be a boolean');
|
||||
if (options.value && typeof(options.value) !== 'string')
|
||||
throw new TypeError('options.value must be a string');
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
|
||||
this.type = options.type || '';
|
||||
this.criticality = options.criticality || false;
|
||||
this.value = options.value || undefined;
|
||||
|
||||
var self = this;
|
||||
this.__defineGetter__('json', function() {
|
||||
return {
|
||||
controlType: self.type,
|
||||
criticality: self.criticality,
|
||||
controlValue: self.value
|
||||
};
|
||||
});
|
||||
}
|
||||
module.exports = Control;
|
||||
|
||||
|
||||
Control.prototype.toString = function() {
|
||||
return this.json;
|
||||
};
|
||||
|
||||
|
||||
Control.prototype.parse = function(ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
if (ber.readSequence() === null)
|
||||
return false;
|
||||
|
||||
var end = ber.offset + ber.length;
|
||||
|
||||
if (ber.length) {
|
||||
this.type = ber.readString();
|
||||
if (ber.offset < end)
|
||||
this.criticality = ber.readBoolean();
|
||||
|
||||
if (ber.offset < end)
|
||||
this.value = ber.readString();
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
|
@ -0,0 +1,244 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
|
||||
|
||||
function invalidDN(name) {
|
||||
var e = new Error();
|
||||
e.name = 'InvalidDistinguishedNameError';
|
||||
e.message = name;
|
||||
return e;
|
||||
}
|
||||
|
||||
|
||||
function isAlphaNumeric(c) {
|
||||
var re = /[A-Za-z0-9]/;
|
||||
return re.test(c);
|
||||
}
|
||||
|
||||
|
||||
function isWhitespace(c) {
|
||||
var re = /\s/;
|
||||
return re.test(c);
|
||||
}
|
||||
|
||||
function RDN() {}
|
||||
RDN.prototype.toString = function() {
|
||||
var self = this;
|
||||
|
||||
var str = '';
|
||||
Object.keys(this).forEach(function(k) {
|
||||
if (str.length)
|
||||
str += '+';
|
||||
str += k + '=' + self[k];
|
||||
});
|
||||
|
||||
return str;
|
||||
};
|
||||
|
||||
function parse(name) {
|
||||
if (typeof(name) !== 'string')
|
||||
throw new TypeError('name (string) required');
|
||||
|
||||
var cur = 0;
|
||||
var len = name.length;
|
||||
|
||||
function parseRdn() {
|
||||
var rdn = new RDN();
|
||||
while (cur < len) {
|
||||
trim();
|
||||
var attr = parseAttrType();
|
||||
trim();
|
||||
if (cur >= len || name[cur++] !== '=')
|
||||
throw invalidDN(name);
|
||||
|
||||
trim();
|
||||
var value = parseAttrValue();
|
||||
trim();
|
||||
rdn[attr] = value;
|
||||
if (cur >= len || name[cur] !== '+')
|
||||
break;
|
||||
++cur;
|
||||
}
|
||||
|
||||
return rdn;
|
||||
}
|
||||
|
||||
|
||||
function trim() {
|
||||
while ((cur < len) && isWhitespace(name[cur]))
|
||||
++cur;
|
||||
}
|
||||
|
||||
function parseAttrType() {
|
||||
var beg = cur;
|
||||
while (cur < len) {
|
||||
var c = name[cur];
|
||||
if (isAlphaNumeric(c) ||
|
||||
c == '.' ||
|
||||
c == '-' ||
|
||||
c == ' ') {
|
||||
++cur;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Back out any trailing spaces.
|
||||
while ((cur > beg) && (name[cur - 1] == ' '))
|
||||
--cur;
|
||||
|
||||
if (beg == cur)
|
||||
throw invalidDN(name);
|
||||
|
||||
return name.slice(beg, cur);
|
||||
}
|
||||
|
||||
function parseAttrValue() {
|
||||
if (cur < len && name[cur] == '#') {
|
||||
return parseBinaryAttrValue();
|
||||
} else if (cur < len && name[cur] == '"') {
|
||||
return parseQuotedAttrValue();
|
||||
} else {
|
||||
return parseStringAttrValue();
|
||||
}
|
||||
}
|
||||
|
||||
function parseBinaryAttrValue() {
|
||||
var beg = cur++;
|
||||
while (cur < len && isAlphaNumeric(name[cur]))
|
||||
++cur;
|
||||
|
||||
return name.slice(beg, cur);
|
||||
}
|
||||
|
||||
function parseQuotedAttrValue() {
|
||||
var beg = cur++;
|
||||
|
||||
while ((cur < len) && name[cur] != '"') {
|
||||
if (name[cur] === '\\')
|
||||
++cur; // consume backslash, then what follows
|
||||
|
||||
++cur;
|
||||
}
|
||||
if (cur++ >= len) // no closing quote
|
||||
throw invalidDN(name);
|
||||
|
||||
return name.slice(beg, cur);
|
||||
}
|
||||
|
||||
function parseStringAttrValue() {
|
||||
var beg = cur;
|
||||
var esc = -1;
|
||||
|
||||
while ((cur < len) && !atTerminator()) {
|
||||
if (name[cur] === '\\') {
|
||||
++cur; // consume backslash, then what follows
|
||||
esc = cur;
|
||||
}
|
||||
++cur;
|
||||
}
|
||||
if (cur > len) // backslash followed by nothing
|
||||
throw invalidDN(name);
|
||||
|
||||
// Trim off (unescaped) trailing whitespace.
|
||||
var end;
|
||||
for (end = cur; end > beg; end--) {
|
||||
if (!isWhitespace(name[end - 1]) || (esc === (end - 1)))
|
||||
break;
|
||||
}
|
||||
return name.slice(beg, end);
|
||||
}
|
||||
|
||||
function atTerminator() {
|
||||
return (cur < len &&
|
||||
(name[cur] === ',' ||
|
||||
name[cur] === ';' ||
|
||||
name[cur] === '+'));
|
||||
}
|
||||
|
||||
var rdns = [];
|
||||
|
||||
rdns.push(parseRdn());
|
||||
while (cur < len) {
|
||||
if (name[cur] === ',' || name[cur] === ';') {
|
||||
++cur;
|
||||
rdns.push(parseRdn());
|
||||
} else {
|
||||
throw invalidDN(name);
|
||||
}
|
||||
}
|
||||
|
||||
return new DN(rdns);
|
||||
}
|
||||
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
|
||||
function DN(rdns) {
|
||||
if (!Array.isArray(rdns))
|
||||
throw new TypeError('rdns ([object]) required');
|
||||
rdns.forEach(function(rdn) {
|
||||
if (typeof(rdn) !== 'object')
|
||||
throw new TypeError('rdns ([object]) required');
|
||||
});
|
||||
|
||||
this.rdns = rdns.slice();
|
||||
|
||||
this.__defineGetter__('length', function() {
|
||||
return this.rdns.length;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
DN.prototype.toString = function() {
|
||||
var _dn = [];
|
||||
this.rdns.forEach(function(rdn) {
|
||||
_dn.push(rdn.toString());
|
||||
});
|
||||
return _dn.join(', ');
|
||||
};
|
||||
|
||||
|
||||
DN.prototype.childOf = function(dn) {
|
||||
if (!(dn instanceof DN))
|
||||
dn = parse(dn);
|
||||
|
||||
if (this.rdns.length < dn.rdns.length)
|
||||
return false;
|
||||
|
||||
var diff = this.rdns.length - dn.rdns.length;
|
||||
for (var i = dn.rdns.length - 1; i >= 0; i--) {
|
||||
var rdn = dn.rdns[i];
|
||||
for (var k in rdn) {
|
||||
if (rdn.hasOwnProperty(k)) {
|
||||
var ourRdn = this.rdns[i + diff];
|
||||
if (ourRdn[k] !== rdn[k])
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
DN.prototype.parentOf = function(dn) {
|
||||
if (!(dn instanceof DN))
|
||||
dn = parse(dn);
|
||||
|
||||
var parent = DN.prototype.childOf.call(dn, this);
|
||||
|
||||
return parent;
|
||||
};
|
||||
|
||||
|
||||
module.exports = {
|
||||
|
||||
parse: parse,
|
||||
|
||||
DN: DN,
|
||||
|
||||
RDN: RDN
|
||||
|
||||
};
|
|
@ -0,0 +1,140 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var util = require('util');
|
||||
|
||||
var LDAPResult = require('../messages').LDAPResult;
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var CODES = {
|
||||
LDAP_SUCCESS: 0,
|
||||
LDAP_OPERATIONS_ERROR: 1,
|
||||
LDAP_PROTOCOL_ERROR: 2,
|
||||
LDAP_TIME_LIMIT_EXCEEDED: 3,
|
||||
LDAP_SIZE_LIMIT_EXCEEDED: 4,
|
||||
LDAP_COMPARE_FALSE: 5,
|
||||
LDAP_COMPARE_TRUE: 6,
|
||||
LDAP_AUTH_METHOD_NOT_SUPPORTED: 7,
|
||||
LDAP_STRONG_AUTH_REQUIRED: 8,
|
||||
LDAP_REFERRAL: 10,
|
||||
LDAP_ADMIN_LIMIT_EXCEEDED: 11,
|
||||
LDAP_UNAVAILABLE_CRITICAL_EXTENSION: 12,
|
||||
LDAP_CONFIDENTIALITY_REQUIRED: 13,
|
||||
LDAP_SASL_BIND_IN_PROGRESS: 14,
|
||||
LDAP_NO_SUCH_ATTRIBUTE: 16,
|
||||
LDAP_UNDEFINED_ATTRIBUTE_TYPE: 17,
|
||||
LDAP_INAPPROPRIATE_MATCHING: 18,
|
||||
LDAP_CONSTRAINT_VIOLATION: 19,
|
||||
LDAP_ATTRIBUTE_OR_VALUE_EXISTS: 20,
|
||||
LDAP_INVALID_ATTRIUBTE_SYNTAX: 21,
|
||||
LDAP_NO_SUCH_OBJECT: 32,
|
||||
LDAP_ALIAS_PROBLEM: 33,
|
||||
LDAP_INVALID_DN_SYNTAX: 34,
|
||||
LDAP_ALIAS_DEREF_PROBLEM: 36,
|
||||
LDAP_INAPPROPRIATE_AUTHENTICATION: 48,
|
||||
LDAP_INVALID_CREDENTIALS: 49,
|
||||
LDAP_INSUFFICIENT_ACCESS_RIGHTS: 50,
|
||||
LDAP_BUSY: 51,
|
||||
LDAP_UNAVAILABLE: 52,
|
||||
LDAP_UNWILLING_TO_PERFORM: 53,
|
||||
LDAP_LOOP_DETECT: 54,
|
||||
LDAP_NAMING_VIOLATION: 64,
|
||||
LDAP_OBJECTCLASS_VIOLATION: 65,
|
||||
LDAP_NOT_ALLOWED_ON_NON_LEAF: 66,
|
||||
LDAP_NOT_ALLOWED_ON_RDN: 67,
|
||||
LDAP_ENTRY_ALREADY_EXISTS: 68,
|
||||
LDAP_OBJECTCLASS_MODS_PROHIBITED: 69,
|
||||
LDAP_AFFECTS_MULTIPLE_DSAS: 71,
|
||||
LDAP_OTHER: 80
|
||||
};
|
||||
|
||||
var ERRORS = [];
|
||||
|
||||
|
||||
|
||||
///--- Error Base class
|
||||
|
||||
function LDAPError(errorName, errorCode, msg, dn, caller) {
|
||||
if (Error.captureStackTrace)
|
||||
Error.captureStackTrace(this, caller || LDAPError);
|
||||
|
||||
this.__defineGetter__('dn', function() {
|
||||
return (dn ? (dn.toString() || '') : '');
|
||||
});
|
||||
this.__defineGetter__('code', function() {
|
||||
return errorCode;
|
||||
});
|
||||
this.__defineGetter__('name', function() {
|
||||
return errorName;
|
||||
});
|
||||
this.__defineGetter__('message', function() {
|
||||
return msg || errorName;
|
||||
});
|
||||
}
|
||||
util.inherits(LDAPError, Error);
|
||||
|
||||
|
||||
|
||||
///--- Exported API
|
||||
// Some whacky games here to make sure all the codes are exported
|
||||
|
||||
module.exports = {};
|
||||
|
||||
Object.keys(CODES).forEach(function(code) {
|
||||
module.exports[code] = CODES[code];
|
||||
if (code === 'LDAP_SUCCESS')
|
||||
return;
|
||||
|
||||
var err = '';
|
||||
var msg = '';
|
||||
var pieces = code.split('_').slice(1);
|
||||
for (var i = 0; i < pieces.length; i++) {
|
||||
var lc = pieces[i].toLowerCase();
|
||||
var key = lc.charAt(0).toUpperCase() + lc.slice(1);
|
||||
err += key;
|
||||
msg += key + ((i + 1) < pieces.length ? ' ' : '');
|
||||
}
|
||||
|
||||
if (!/\w+Error$/.test(err))
|
||||
err += 'Error';
|
||||
|
||||
// At this point LDAP_OPERATIONS_ERROR is now OperationsError in $err
|
||||
// and 'Operations Error' in $msg
|
||||
module.exports[err] = function(message, dn, caller) {
|
||||
LDAPError.call(this,
|
||||
err,
|
||||
CODES[code],
|
||||
message || msg,
|
||||
dn || null,
|
||||
caller || module.exports[err]);
|
||||
}
|
||||
module.exports[err].constructor = module.exports[err];
|
||||
util.inherits(module.exports[err], LDAPError);
|
||||
|
||||
ERRORS[CODES[code]] = {
|
||||
err: err,
|
||||
message: msg
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
module.exports.getError = function(res) {
|
||||
if (!(res instanceof LDAPResult))
|
||||
throw new TypeError('res (LDAPResult) required');
|
||||
|
||||
var errObj = ERRORS[res.status];
|
||||
var E = module.exports[errObj.err];
|
||||
return new E(res.errorMessage || errObj.message,
|
||||
res.matchedDN || null,
|
||||
module.exports.getError);
|
||||
};
|
||||
|
||||
|
||||
module.exports.getMessage = function(code) {
|
||||
if (typeof(code) !== 'number')
|
||||
throw new TypeError('code (number) required');
|
||||
|
||||
var errObj = ERRORS[res.status];
|
||||
return (errObj && errObj.message ? errObj.message : '');
|
||||
};
|
|
@ -0,0 +1,82 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var assert = require('assert');
|
||||
var util = require('util');
|
||||
|
||||
var Filter = require('./filter');
|
||||
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
function AndFilter(options) {
|
||||
if (typeof(options) === 'object') {
|
||||
if (!options.filters || !Array.isArray(options.filters))
|
||||
throw new TypeError('options.filters ([Filter]) required');
|
||||
this.filters = options.filters.slice();
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
|
||||
options.type = Protocol.FILTER_AND;
|
||||
Filter.call(this, options);
|
||||
|
||||
if (!this.filters)
|
||||
this.filters = [];
|
||||
|
||||
var self = this;
|
||||
this.__defineGetter__('json', function() {
|
||||
return {
|
||||
type: 'And',
|
||||
filters: self.filters || []
|
||||
};
|
||||
});
|
||||
}
|
||||
util.inherits(AndFilter, Filter);
|
||||
module.exports = AndFilter;
|
||||
|
||||
|
||||
AndFilter.prototype.toString = function() {
|
||||
var str = '(&';
|
||||
this.filters.forEach(function(f) {
|
||||
str += f.toString();
|
||||
});
|
||||
str += ')';
|
||||
|
||||
return str;
|
||||
};
|
||||
|
||||
|
||||
AndFilter.prototype.matches = function(target) {
|
||||
if (typeof(target) !== 'object')
|
||||
throw new TypeError('target (object) required');
|
||||
|
||||
var matches = this.filters.length ? true : false;
|
||||
|
||||
for (var i = 0; i < this.filters.length; i++)
|
||||
if (!this.filters[i].matches(target))
|
||||
return false;
|
||||
|
||||
return matches;
|
||||
};
|
||||
|
||||
|
||||
AndFilter.prototype.addFilter = function(filter) {
|
||||
if (!filter || typeof(filter) !== 'object')
|
||||
throw new TypeError('filter (object) required');
|
||||
|
||||
this.filters.push(filter);
|
||||
};
|
||||
|
||||
|
||||
AndFilter.prototype._toBer = function(ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
this.filters.forEach(function(f) {
|
||||
ber = f.toBer(ber);
|
||||
});
|
||||
|
||||
return ber;
|
||||
};
|
|
@ -0,0 +1,75 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var assert = require('assert');
|
||||
var util = require('util');
|
||||
|
||||
var Filter = require('./filter');
|
||||
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
function ApproximateFilter(options) {
|
||||
if (typeof(options) === 'object') {
|
||||
if (!options.attribute || typeof(options.attribute) !== 'string')
|
||||
throw new TypeError('options.attribute (string) required');
|
||||
if (!options.value || typeof(options.value) !== 'string')
|
||||
throw new TypeError('options.value (string) required');
|
||||
this.attribute = options.attribute;
|
||||
this.value = options.value;
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
options.type = Protocol.FILTER_APPROX;
|
||||
Filter.call(this, options);
|
||||
|
||||
var self = this;
|
||||
this.__defineGetter__('json', function() {
|
||||
return {
|
||||
type: 'ApproximateMatch',
|
||||
attribute: self.attribute || undefined,
|
||||
value: self.value || undefined
|
||||
};
|
||||
});
|
||||
}
|
||||
util.inherits(ApproximateFilter, Filter);
|
||||
module.exports = ApproximateFilter;
|
||||
|
||||
|
||||
ApproximateFilter.prototype.toString = function() {
|
||||
return '(' + this.attribute + '~=' + this.value + ')';
|
||||
};
|
||||
|
||||
|
||||
ApproximateFilter.prototype.matches = function(target) {
|
||||
if (typeof(target) !== 'object')
|
||||
throw new TypeError('target (object) required');
|
||||
|
||||
var matches = false;
|
||||
if (target.hasOwnProperty(this.attribute))
|
||||
matches = (this.value === target[this.attribute]);
|
||||
|
||||
return matches;
|
||||
};
|
||||
|
||||
|
||||
ApproximateFilter.prototype.parse = function(ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
this.attribute = ber.readString();
|
||||
this.value = ber.readString();
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
ApproximateFilter.prototype._toBer = function(ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
ber.writeString(this.attribute);
|
||||
ber.writeString(this.value);
|
||||
|
||||
return ber;
|
||||
};
|
|
@ -0,0 +1,75 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var assert = require('assert');
|
||||
var util = require('util');
|
||||
|
||||
var Filter = require('./filter');
|
||||
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
function EqualityFilter(options) {
|
||||
if (typeof(options) === 'object') {
|
||||
if (!options.attribute || typeof(options.attribute) !== 'string')
|
||||
throw new TypeError('options.attribute (string) required');
|
||||
if (!options.value || typeof(options.value) !== 'string')
|
||||
throw new TypeError('options.value (string) required');
|
||||
this.attribute = options.attribute;
|
||||
this.value = options.value;
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
options.type = Protocol.FILTER_EQUALITY;
|
||||
Filter.call(this, options);
|
||||
|
||||
var self = this;
|
||||
this.__defineGetter__('json', function() {
|
||||
return {
|
||||
type: 'EqualityMatch',
|
||||
attribute: self.attribute || undefined,
|
||||
value: self.value || undefined
|
||||
};
|
||||
});
|
||||
}
|
||||
util.inherits(EqualityFilter, Filter);
|
||||
module.exports = EqualityFilter;
|
||||
|
||||
|
||||
EqualityFilter.prototype.toString = function() {
|
||||
return '(' + this.attribute + '=' + this.value + ')';
|
||||
};
|
||||
|
||||
|
||||
EqualityFilter.prototype.matches = function(target) {
|
||||
if (typeof(target) !== 'object')
|
||||
throw new TypeError('target (object) required');
|
||||
|
||||
var matches = false;
|
||||
if (target.hasOwnProperty(this.attribute))
|
||||
matches = (this.value === target[this.attribute]);
|
||||
|
||||
return matches;
|
||||
};
|
||||
|
||||
|
||||
EqualityFilter.prototype.parse = function(ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
this.attribute = ber.readString();
|
||||
this.value = ber.readString();
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
EqualityFilter.prototype._toBer = function(ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
ber.writeString(this.attribute);
|
||||
ber.writeString(this.value);
|
||||
|
||||
return ber;
|
||||
};
|
|
@ -0,0 +1,38 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var assert = require('assert');
|
||||
|
||||
var asn1 = require('asn1');
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var BerWriter = asn1.BerWriter;
|
||||
|
||||
///--- API
|
||||
|
||||
function Filter(options) {
|
||||
if (!options || typeof(options) !== 'object')
|
||||
throw new TypeError('options (object) required');
|
||||
if (typeof(options.type) !== 'number')
|
||||
throw new TypeError('options.type (number) required');
|
||||
|
||||
this._type = options.type;
|
||||
|
||||
var self = this;
|
||||
this.__defineGetter__('type', function() {
|
||||
return '0x' + self._type.toString(16);
|
||||
});
|
||||
}
|
||||
module.exports = Filter;
|
||||
|
||||
|
||||
Filter.prototype.toBer = function(ber) {
|
||||
if (!ber || !(ber instanceof BerWriter))
|
||||
throw new TypeError('ber (BerWriter) required');
|
||||
|
||||
ber.startSequence(this._type);
|
||||
ber = this._toBer(ber);
|
||||
ber.endSequence();
|
||||
return ber;
|
||||
};
|
|
@ -0,0 +1,76 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var assert = require('assert');
|
||||
var util = require('util');
|
||||
|
||||
var Filter = require('./filter');
|
||||
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
function GreaterThanEqualsFilter(options) {
|
||||
if (typeof(options) === 'object') {
|
||||
if (!options.attribute || typeof(options.attribute) !== 'string')
|
||||
throw new TypeError('options.attribute (string) required');
|
||||
if (!options.value || typeof(options.value) !== 'string')
|
||||
throw new TypeError('options.value (string) required');
|
||||
this.attribute = options.attribute;
|
||||
this.value = options.value;
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
|
||||
options.type = Protocol.FILTER_GE;
|
||||
Filter.call(this, options);
|
||||
|
||||
var self = this;
|
||||
this.__defineGetter__('json', function() {
|
||||
return {
|
||||
type: 'GreaterThanEqualsMatch',
|
||||
attribute: self.attribute || undefined,
|
||||
value: self.value || undefined
|
||||
};
|
||||
});
|
||||
}
|
||||
util.inherits(GreaterThanEqualsFilter, Filter);
|
||||
module.exports = GreaterThanEqualsFilter;
|
||||
|
||||
|
||||
GreaterThanEqualsFilter.prototype.toString = function() {
|
||||
return '(' + this.attribute + '>=' + this.value + ')';
|
||||
};
|
||||
|
||||
|
||||
GreaterThanEqualsFilter.prototype.matches = function(target) {
|
||||
if (typeof(target) !== 'object')
|
||||
throw new TypeError('target (object) required');
|
||||
|
||||
var matches = false;
|
||||
if (target.hasOwnProperty(this.attribute))
|
||||
matches = (target[this.attribute] >= this.value);
|
||||
|
||||
return matches;
|
||||
};
|
||||
|
||||
|
||||
GreaterThanEqualsFilter.prototype.parse = function(ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
this.attribute = ber.readString();
|
||||
this.value = ber.readString();
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
GreaterThanEqualsFilter.prototype._toBer = function(ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
ber.writeString(this.attribute);
|
||||
ber.writeString(this.value);
|
||||
|
||||
return ber;
|
||||
};
|
|
@ -0,0 +1,320 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var assert = require('assert');
|
||||
|
||||
var asn1 = require('asn1');
|
||||
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
var Filter = require('./filter');
|
||||
var AndFilter = require('./and_filter');
|
||||
var ApproximateFilter = require('./approx_filter');
|
||||
var EqualityFilter = require('./equality_filter');
|
||||
var GreaterThanEqualsFilter = require('./ge_filter');
|
||||
var LessThanEqualsFilter = require('./le_filter');
|
||||
var NotFilter = require('./not_filter');
|
||||
var OrFilter = require('./or_filter');
|
||||
var PresenceFilter = require('./presence_filter');
|
||||
var SubstringFilter = require('./substr_filter');
|
||||
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var BerReader = asn1.BerReader;
|
||||
|
||||
|
||||
|
||||
///--- Internal Parsers
|
||||
|
||||
|
||||
/*
|
||||
* This is a pretty naive approach to parsing, but it's relatively short amount
|
||||
* of code. Basically, we just build a stack as we go.
|
||||
*/
|
||||
function _filterStringToStack(str) {
|
||||
assert.ok(str);
|
||||
|
||||
var tmp = '';
|
||||
var esc = false;
|
||||
var stack = [];
|
||||
var depth = -1;
|
||||
var open = false;
|
||||
for (var i = 0; i < str.length; i++) {
|
||||
var c = str[i];
|
||||
|
||||
if (esc) {
|
||||
esc = false;
|
||||
tmp += c;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (c) {
|
||||
case '(':
|
||||
open = true;
|
||||
tmp = '';
|
||||
stack[++depth] = '';
|
||||
break;
|
||||
case ')':
|
||||
if (open) {
|
||||
stack[depth].value = tmp;
|
||||
tmp = '';
|
||||
}
|
||||
open = false;
|
||||
break;
|
||||
case '&':
|
||||
case '|':
|
||||
case '!':
|
||||
stack[depth] = c;
|
||||
break;
|
||||
case '=':
|
||||
stack[depth] = { attribute: tmp, op: c };
|
||||
tmp = '';
|
||||
break;
|
||||
case '>':
|
||||
case '<':
|
||||
case '~':
|
||||
if (!(str[++i] === '='))
|
||||
throw new Error('Invalid filter: ' + tmp + c + str[i]);
|
||||
|
||||
stack[depth] = {attribute: tmp, op: c};
|
||||
tmp = '';
|
||||
break;
|
||||
case '\\':
|
||||
esc = true;
|
||||
default:
|
||||
tmp += c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (open)
|
||||
throw new Error('Invalid filter: ' + str);
|
||||
|
||||
return stack;
|
||||
}
|
||||
|
||||
|
||||
function _parseString(str) {
|
||||
assert.ok(str);
|
||||
|
||||
var stack = _filterStringToStack(str);
|
||||
|
||||
if (!stack || !stack.length)
|
||||
throw new Error('Invalid filter: ' + str);
|
||||
|
||||
debugger;
|
||||
var f;
|
||||
var filters = [];
|
||||
for (var i = stack.length - 1; i >= 0; i--) {
|
||||
if (stack[i] === '&') {
|
||||
filters.unshift(new AndFilter({
|
||||
filters: filters
|
||||
}));
|
||||
filters.length = 1;
|
||||
} else if (stack[i] === '|') {
|
||||
filters.unshift(new OrFilter({
|
||||
filters: filters
|
||||
}));
|
||||
filters.length = 1;
|
||||
} else if (stack[i] === '!') {
|
||||
filters.push(new NotFilter({
|
||||
filter: filters.pop()
|
||||
}));
|
||||
} else {
|
||||
switch (stack[i].op) {
|
||||
case '=': // could be presence, equality or substr
|
||||
if (stack[i].value === '*') {
|
||||
filters.push(new PresenceFilter(stack[i]));
|
||||
} else {
|
||||
var vals = [''];
|
||||
var ndx = 0;
|
||||
var esc = false;
|
||||
for (var j = 0; j < stack[i].value.length; j++) {
|
||||
var c = stack[i].value[j];
|
||||
if (c === '\\') {
|
||||
if (esc) {
|
||||
esc = true;
|
||||
} else {
|
||||
vals[ndx] += c;
|
||||
esc = false;
|
||||
}
|
||||
} else if (c === '*') {
|
||||
if (esc) {
|
||||
vals[ndx] = c;
|
||||
} else {
|
||||
vals[++ndx] = '';
|
||||
}
|
||||
} else {
|
||||
vals[ndx] += c;
|
||||
}
|
||||
}
|
||||
if (vals.length === 1) {
|
||||
filters.push(new EqualityFilter(stack[i]));
|
||||
} else {
|
||||
filters.push(new SubstringFilter({
|
||||
attribute: stack[i].attribute,
|
||||
initial: vals.shift(),
|
||||
'final': vals.pop(),
|
||||
any: vals
|
||||
}));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case '~':
|
||||
filters.push(new ApproximateFilter(stack[i]));
|
||||
break;
|
||||
case '>':
|
||||
filters.push(new GreaterThanEqualsFilter(stack[i]));
|
||||
break;
|
||||
case '<':
|
||||
filters.push(new LessThanEqualsFilter(stack[i]));
|
||||
break;
|
||||
default:
|
||||
throw new Error('Invalid filter (op=' + stack[i].op + '): ' + str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (filters.length !== 1)
|
||||
throw new Error('Invalid filter: ' + str);
|
||||
|
||||
return filters.pop();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* A filter looks like this coming in:
|
||||
* Filter ::= CHOICE {
|
||||
* and [0] SET OF Filter,
|
||||
* or [1] SET OF Filter,
|
||||
* not [2] Filter,
|
||||
* equalityMatch [3] AttributeValueAssertion,
|
||||
* substrings [4] SubstringFilter,
|
||||
* greaterOrEqual [5] AttributeValueAssertion,
|
||||
* lessOrEqual [6] AttributeValueAssertion,
|
||||
* present [7] AttributeType,
|
||||
* approxMatch [8] AttributeValueAssertion,
|
||||
* extensibleMatch [9] MatchingRuleAssertion --v3 only
|
||||
* }
|
||||
*
|
||||
* SubstringFilter ::= SEQUENCE {
|
||||
* type AttributeType,
|
||||
* SEQUENCE OF CHOICE {
|
||||
* initial [0] IA5String,
|
||||
* any [1] IA5String,
|
||||
* final [2] IA5String
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* The extensibleMatch was added in LDAPv3:
|
||||
*
|
||||
* MatchingRuleAssertion ::= SEQUENCE {
|
||||
* matchingRule [1] MatchingRuleID OPTIONAL,
|
||||
* type [2] AttributeDescription OPTIONAL,
|
||||
* matchValue [3] AssertionValue,
|
||||
* dnAttributes [4] BOOLEAN DEFAULT FALSE
|
||||
* }
|
||||
*/
|
||||
function _parse(ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
function parseSet(f) {
|
||||
var end = ber.offset + ber.length;
|
||||
while (ber.offset < end)
|
||||
f.addFilter(_parse(ber));
|
||||
}
|
||||
|
||||
var f;
|
||||
|
||||
var type = ber.readSequence();
|
||||
switch (type) {
|
||||
|
||||
case Protocol.FILTER_AND:
|
||||
f = new AndFilter();
|
||||
parseSet(f);
|
||||
break;
|
||||
|
||||
case Protocol.FILTER_APPROX:
|
||||
f = new ApproximateFilter();
|
||||
f.parse(ber);
|
||||
break;
|
||||
|
||||
case Protocol.FILTER_EQUALITY:
|
||||
f = new EqualityFilter();
|
||||
f.parse(ber);
|
||||
return f;
|
||||
|
||||
case Protocol.FILTER_GE:
|
||||
f = new GreaterThanEqualsFilter();
|
||||
f.parse(ber);
|
||||
return f;
|
||||
|
||||
case Protocol.FILTER_LE:
|
||||
f = new LessThanEqualsFilter();
|
||||
f.parse(ber);
|
||||
return f;
|
||||
|
||||
case Protocol.FILTER_NOT:
|
||||
var _f = _parse(ber);
|
||||
f = new NotFilter({
|
||||
filter: _f
|
||||
});
|
||||
break;
|
||||
|
||||
case Protocol.FILTER_OR:
|
||||
f = new OrFilter();
|
||||
parseSet(f);
|
||||
break;
|
||||
|
||||
case Protocol.FILTER_PRESENT:
|
||||
f = new PresenceFilter();
|
||||
f.parse(ber);
|
||||
break;
|
||||
|
||||
case Protocol.FILTER_SUBSTRINGS:
|
||||
f = new SubstringFilter();
|
||||
f.parse(ber);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error('Invalid search filter type: 0x' + type.toString(16));
|
||||
}
|
||||
|
||||
|
||||
assert.ok(f);
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
module.exports = {
|
||||
|
||||
parse: function(ber) {
|
||||
if (!ber || !(ber instanceof BerReader))
|
||||
throw new TypeError('ber (BerReader) required');
|
||||
|
||||
return _parse(ber);
|
||||
},
|
||||
|
||||
parseString: function(filter) {
|
||||
if (!filter || typeof(filter) !== 'string')
|
||||
throw new TypeError('filter (string) required');
|
||||
|
||||
return _parseString(filter);
|
||||
},
|
||||
|
||||
AndFilter: AndFilter,
|
||||
ApproximateFilter: ApproximateFilter,
|
||||
EqualityFilter: EqualityFilter,
|
||||
GreaterThanEqualsFilter: GreaterThanEqualsFilter,
|
||||
LessThanEqualsFilter: LessThanEqualsFilter,
|
||||
NotFilter: NotFilter,
|
||||
OrFilter: OrFilter,
|
||||
PresenceFilter: PresenceFilter,
|
||||
SubstringFilter: SubstringFilter,
|
||||
Filter: Filter
|
||||
};
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var assert = require('assert');
|
||||
var util = require('util');
|
||||
|
||||
var Filter = require('./filter');
|
||||
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
function LessThanEqualsFilter(options) {
|
||||
if (typeof(options) === 'object') {
|
||||
if (!options.attribute || typeof(options.attribute) !== 'string')
|
||||
throw new TypeError('options.attribute (string) required');
|
||||
if (!options.value || typeof(options.value) !== 'string')
|
||||
throw new TypeError('options.value (string) required');
|
||||
this.attribute = options.attribute;
|
||||
this.value = options.value;
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
|
||||
options.type = Protocol.FILTER_LE;
|
||||
Filter.call(this, options);
|
||||
|
||||
var self = this;
|
||||
this.__defineGetter__('json', function() {
|
||||
return {
|
||||
type: 'LessThanEqualsMatch',
|
||||
attribute: self.attribute || undefined,
|
||||
value: self.value || undefined
|
||||
};
|
||||
});
|
||||
}
|
||||
util.inherits(LessThanEqualsFilter, Filter);
|
||||
module.exports = LessThanEqualsFilter;
|
||||
|
||||
|
||||
LessThanEqualsFilter.prototype.toString = function() {
|
||||
return '(' + this.attribute + '<=' + this.value + ')';
|
||||
};
|
||||
|
||||
|
||||
LessThanEqualsFilter.prototype.matches = function(target) {
|
||||
if (typeof(target) !== 'object')
|
||||
throw new TypeError('target (object) required');
|
||||
|
||||
var matches = false;
|
||||
if (target.hasOwnProperty(this.attribute))
|
||||
matches = (target[this.attribute] <= this.value);
|
||||
|
||||
return matches;
|
||||
};
|
||||
|
||||
|
||||
LessThanEqualsFilter.prototype.parse = function(ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
this.attribute = ber.readString();
|
||||
this.value = ber.readString();
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
LessThanEqualsFilter.prototype._toBer = function(ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
ber.writeString(this.attribute);
|
||||
ber.writeString(this.value);
|
||||
|
||||
return ber;
|
||||
};
|
|
@ -0,0 +1,51 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var assert = require('assert');
|
||||
var util = require('util');
|
||||
|
||||
var Filter = require('./filter');
|
||||
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
function NotFilter(options) {
|
||||
if (typeof(options) !== 'object')
|
||||
throw new TypeError('options (object) required');
|
||||
if (!options.filter || !(options.filter instanceof Filter))
|
||||
throw new TypeError('options.filter (Filter) required');
|
||||
|
||||
options.type = Protocol.FILTER_NOT;
|
||||
Filter.call(this, options);
|
||||
|
||||
this.filter = options.filter;
|
||||
|
||||
var self = this;
|
||||
this.__defineGetter__('json', function() {
|
||||
return {
|
||||
type: 'Not',
|
||||
filter: self.filter
|
||||
};
|
||||
});
|
||||
}
|
||||
util.inherits(NotFilter, Filter);
|
||||
module.exports = NotFilter;
|
||||
|
||||
|
||||
NotFilter.prototype.toString = function() {
|
||||
return '(!' + this.filter.toString() + ')';
|
||||
};
|
||||
|
||||
|
||||
NotFilter.prototype.matches = function(target) {
|
||||
return !this.filter.matches(target);
|
||||
};
|
||||
|
||||
|
||||
NotFilter.prototype._toBer = function(ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
return this.filter.toBer(ber);
|
||||
};
|
|
@ -0,0 +1,80 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var assert = require('assert');
|
||||
var util = require('util');
|
||||
|
||||
var Filter = require('./filter');
|
||||
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
function OrFilter(options) {
|
||||
if (typeof(options) === 'object') {
|
||||
if (!options.filters || !Array.isArray(options.filters))
|
||||
throw new TypeError('options.filters ([Filter]) required');
|
||||
this.filters = options.filters.slice();
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
|
||||
options.type = Protocol.FILTER_OR;
|
||||
Filter.call(this, options);
|
||||
|
||||
if (!this.filters)
|
||||
this.filters = [];
|
||||
|
||||
var self = this;
|
||||
this.__defineGetter__('json', function() {
|
||||
return {
|
||||
type: 'Or',
|
||||
filters: self.filters || []
|
||||
};
|
||||
});
|
||||
}
|
||||
util.inherits(OrFilter, Filter);
|
||||
module.exports = OrFilter;
|
||||
|
||||
|
||||
OrFilter.prototype.toString = function() {
|
||||
var str = '(|';
|
||||
this.filters.forEach(function(f) {
|
||||
str += f.toString();
|
||||
});
|
||||
str += ')';
|
||||
|
||||
return str;
|
||||
};
|
||||
|
||||
|
||||
OrFilter.prototype.matches = function(target) {
|
||||
if (typeof(target) !== 'object')
|
||||
throw new TypeError('target (object) required');
|
||||
|
||||
for (var i = 0; i < this.filters.length; i++)
|
||||
if (this.filters[i].matches(target))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
OrFilter.prototype.addFilter = function(filter) {
|
||||
if (!filter || typeof(filter) !== 'object')
|
||||
throw new TypeError('filter (object) required');
|
||||
|
||||
this.filters.push(filter);
|
||||
};
|
||||
|
||||
|
||||
OrFilter.prototype._toBer = function(ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
this.filters.forEach(function(f) {
|
||||
ber = f.toBer(ber);
|
||||
});
|
||||
|
||||
return ber;
|
||||
};
|
|
@ -0,0 +1,67 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var assert = require('assert');
|
||||
var util = require('util');
|
||||
|
||||
var Filter = require('./filter');
|
||||
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
function PresenceFilter(options) {
|
||||
if (typeof(options) === 'object') {
|
||||
if (!options.attribute || typeof(options.attribute) !== 'string')
|
||||
throw new TypeError('options.attribute (string) required');
|
||||
this.attribute = options.attribute;
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
options.type = Protocol.FILTER_PRESENT;
|
||||
Filter.call(this, options);
|
||||
|
||||
var self = this;
|
||||
this.__defineGetter__('json', function() {
|
||||
return {
|
||||
type: 'PresenceMatch',
|
||||
attribute: self.attribute || undefined
|
||||
};
|
||||
});
|
||||
}
|
||||
util.inherits(PresenceFilter, Filter);
|
||||
module.exports = PresenceFilter;
|
||||
|
||||
|
||||
PresenceFilter.prototype.toString = function() {
|
||||
return '(' + this.attribute + '=*)';
|
||||
};
|
||||
|
||||
|
||||
PresenceFilter.prototype.matches = function(target) {
|
||||
if (typeof(target) !== 'object')
|
||||
throw new TypeError('target (object) required');
|
||||
|
||||
var matches = false;
|
||||
if (target.hasOwnProperty(this.attribute))
|
||||
matches = true;
|
||||
|
||||
return matches;
|
||||
};
|
||||
|
||||
|
||||
PresenceFilter.prototype.parse = function(ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
this.attribute = ber.readString();
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
PresenceFilter.prototype._toBer = function(ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
ber.writeString(this.attribute);
|
||||
|
||||
return ber;
|
||||
};
|
|
@ -0,0 +1,133 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var assert = require('assert');
|
||||
var util = require('util');
|
||||
|
||||
var Filter = require('./filter');
|
||||
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
function SubstringFilter(options) {
|
||||
if (typeof(options) === 'object') {
|
||||
if (!options.attribute || typeof(options.attribute) !== 'string')
|
||||
throw new TypeError('options.attribute (string) required');
|
||||
this.attribute = options.attribute;
|
||||
this.initial = options.initial;
|
||||
this.any = options.any ? options.any.slice(0) : [];
|
||||
this['final'] = options['final'];
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
|
||||
if (!this.any)
|
||||
this.any = [];
|
||||
|
||||
options.type = Protocol.FILTER_SUBSTRINGS;
|
||||
Filter.call(this, options);
|
||||
|
||||
var self = this;
|
||||
this.__defineGetter__('json', function() {
|
||||
return {
|
||||
type: 'SubstringMatch',
|
||||
initial: self.initial || undefined,
|
||||
any: self.any || undefined,
|
||||
'final': self['final'] || undefined
|
||||
};
|
||||
});
|
||||
}
|
||||
util.inherits(SubstringFilter, Filter);
|
||||
module.exports = SubstringFilter;
|
||||
|
||||
|
||||
SubstringFilter.prototype.toString = function() {
|
||||
var str = '(' + this.attribute + '=';
|
||||
if (this.initial)
|
||||
str += this.initial + '*';
|
||||
this.any.forEach(function(s) {
|
||||
str += s + '*';
|
||||
});
|
||||
if (this['final'])
|
||||
str += this['final'];
|
||||
str += ')';
|
||||
|
||||
return str;
|
||||
};
|
||||
|
||||
|
||||
SubstringFilter.prototype.matches = function(target) {
|
||||
if (typeof(target) !== 'object')
|
||||
throw new TypeError('target (object) required');
|
||||
|
||||
if (target.hasOwnProperty(this.attribute)) {
|
||||
var re = '';
|
||||
if (this.initial)
|
||||
re += '^' + this.initial + '.*';
|
||||
|
||||
this.any.forEach(function(s) {
|
||||
re += s + '.*';
|
||||
});
|
||||
|
||||
if (this['final'])
|
||||
re += this['final'] + '$';
|
||||
|
||||
var matcher = new RegExp(re);
|
||||
return matcher.test(target[this.attribute]);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
SubstringFilter.prototype.parse = function(ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
this.attribute = ber.readString();
|
||||
ber.readSequence();
|
||||
var end = ber.offset + ber.length;
|
||||
|
||||
while (ber.offset < end) {
|
||||
var tag = ber.peek();
|
||||
switch (tag) {
|
||||
case 0x80: // Initial
|
||||
this.initial = ber.readString(tag);
|
||||
break;
|
||||
case 0x81: // Any
|
||||
this.any.push(ber.readString(tag));
|
||||
break;
|
||||
case 0x82: // Final
|
||||
this['final'] = ber.readString(tag);
|
||||
break;
|
||||
default:
|
||||
throw new Error('Invalid substrings filter type: 0x' + tag.toString(16));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
SubstringFilter.prototype._toBer = function(ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
ber.writeString(this.attribute);
|
||||
ber.startSequence();
|
||||
|
||||
if (this.initial)
|
||||
ber.writeString(this.initial, 0x80);
|
||||
|
||||
if (this.any && this.any.length)
|
||||
this.any.forEach(function(s) {
|
||||
ber.writeString(s, 0x81);
|
||||
});
|
||||
|
||||
if (this['final'])
|
||||
ber.writeString(this['final'], 0x82);
|
||||
|
||||
ber.endSequence();
|
||||
|
||||
return ber;
|
||||
};
|
|
@ -0,0 +1,93 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var Client = require('./client');
|
||||
var dn = require('./dn');
|
||||
var errors = require('./errors');
|
||||
var filters = require('./filters');
|
||||
var messages = require('./messages');
|
||||
var server = require('./server');
|
||||
var logStub = require('./log_stub');
|
||||
var url = require('./url');
|
||||
|
||||
var Attribute = require('./attribute');
|
||||
var Change = require('./change');
|
||||
var Control = require('./control');
|
||||
var Protocol = require('./protocol');
|
||||
|
||||
|
||||
/// Hack a few things we need (i.e., "monkey patch" the prototype)
|
||||
|
||||
if (!String.prototype.startsWith) {
|
||||
String.prototype.startsWith = function(str) {
|
||||
var re = new RegExp('^' + str);
|
||||
return re.test(this);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
if (!String.prototype.endsWith) {
|
||||
String.prototype.endsWith = function(str) {
|
||||
var re = new RegExp(str + '$');
|
||||
return re.test(this);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
module.exports = {
|
||||
|
||||
Client: Client,
|
||||
createClient: function(options) {
|
||||
if (typeof(options) !== 'object')
|
||||
throw new TypeError('options (object) required');
|
||||
|
||||
return new Client(options);
|
||||
},
|
||||
|
||||
createServer: server.createServer,
|
||||
|
||||
dn: dn,
|
||||
DN: dn.DN,
|
||||
DN: dn.RDN,
|
||||
parseDN: dn.parse,
|
||||
|
||||
filters: filters,
|
||||
parseFilter: filters.parseString,
|
||||
|
||||
Attribute: Attribute,
|
||||
Change: Change,
|
||||
Control: Control,
|
||||
|
||||
log4js: logStub,
|
||||
url: url
|
||||
};
|
||||
|
||||
|
||||
///--- Export all the childrenz
|
||||
|
||||
var k;
|
||||
|
||||
for (k in Protocol) {
|
||||
if (Protocol.hasOwnProperty(k))
|
||||
module.exports[k] = Protocol[k];
|
||||
}
|
||||
|
||||
for (k in messages) {
|
||||
if (messages.hasOwnProperty(k))
|
||||
module.exports[k] = messages[k];
|
||||
}
|
||||
|
||||
for (k in filters) {
|
||||
if (filters.hasOwnProperty(k)) {
|
||||
if (k !== 'parse' && k !== 'parseString')
|
||||
module.exports[k] = filters[k];
|
||||
}
|
||||
}
|
||||
|
||||
for (k in errors) {
|
||||
if (errors.hasOwnProperty(k)) {
|
||||
module.exports[k] = errors[k];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var FMT_STR = '%d-%s-%s %s:%s:%sZ %s - %s: ';
|
||||
|
||||
var _i = 0;
|
||||
var LEVELS = {
|
||||
Trace: _i++,
|
||||
Debug: _i++,
|
||||
Info: _i++,
|
||||
Warn: _i++,
|
||||
Error: _i++,
|
||||
Fatal: _i++
|
||||
};
|
||||
|
||||
var level = 'Info';
|
||||
|
||||
|
||||
|
||||
// --- Helpers
|
||||
|
||||
function pad(val) {
|
||||
if (parseInt(val, 10) < 10) {
|
||||
val = '0' + val;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
function format(level, name, args) {
|
||||
var d = new Date();
|
||||
var fmtStr = args.shift();
|
||||
var fmtArgs = [
|
||||
d.getUTCFullYear(),
|
||||
pad(d.getUTCMonth()),
|
||||
pad(d.getUTCDate()),
|
||||
pad(d.getUTCHours()),
|
||||
pad(d.getUTCMinutes()),
|
||||
pad(d.getUTCSeconds()),
|
||||
level,
|
||||
name
|
||||
];
|
||||
|
||||
args = fmtArgs.concat(args);
|
||||
|
||||
var output = (FMT_STR + fmtStr).replace(/%[sdj]/g, function(match) {
|
||||
switch (match) {
|
||||
case '%s': return new String(args.shift());
|
||||
case '%d': return new Number(args.shift());
|
||||
case '%j': return JSON.stringify(args.shift());
|
||||
default:
|
||||
return match;
|
||||
}
|
||||
});
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
function Log(name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
Log.prototype._write = function(level, args) {
|
||||
var data = format(level, this.name, args);
|
||||
console.error(data);
|
||||
};
|
||||
|
||||
Log.prototype.isTraceEnabled = function() {
|
||||
return (LEVELS.Trace >= LEVELS[level]);
|
||||
};
|
||||
|
||||
Log.prototype.trace = function() {
|
||||
if (this.isTraceEnabled())
|
||||
this._write('TRACE', Array.prototype.slice.call(arguments));
|
||||
};
|
||||
|
||||
Log.prototype.isDebugEnabled = function() {
|
||||
return (LEVELS.Debug >= LEVELS[level]);
|
||||
};
|
||||
|
||||
Log.prototype.debug = function() {
|
||||
if (this.isDebugEnabled())
|
||||
this._write('DEBUG', Array.prototype.slice.call(arguments));
|
||||
};
|
||||
|
||||
Log.prototype.isInfoEnabled = function() {
|
||||
return (LEVELS.Info >= LEVELS[level]);
|
||||
};
|
||||
|
||||
Log.prototype.info = function() {
|
||||
if (this.isInfoEnabled())
|
||||
this._write('INFO', Array.prototype.slice.call(arguments));
|
||||
};
|
||||
|
||||
Log.prototype.isWarnEnabled = function() {
|
||||
return (LEVELS.Warn >= LEVELS[level]);
|
||||
};
|
||||
|
||||
Log.prototype.warn = function() {
|
||||
if (this.isWarnEnabled())
|
||||
this._write('WARN', Array.prototype.slice.call(arguments));
|
||||
};
|
||||
|
||||
Log.prototype.isErrorEnabled = function() {
|
||||
return (LEVELS.Error >= LEVELS[level]);
|
||||
};
|
||||
|
||||
Log.prototype.error = function() {
|
||||
if (this.isErrorEnabled())
|
||||
this._write('ERROR', Array.prototype.slice.call(arguments));
|
||||
};
|
||||
|
||||
Log.prototype.isFatalEnabled = function() {
|
||||
return (LEVELS.Fatal >= LEVELS[level]);
|
||||
};
|
||||
|
||||
Log.prototype.fatal = function() {
|
||||
if (this.isFatalEnabled())
|
||||
this._write('FATAL', Array.prototype.slice.call(arguments));
|
||||
};
|
||||
|
||||
|
||||
module.exports = {
|
||||
|
||||
setLevel: function(l) {
|
||||
if (LEVELS[l] !== undefined)
|
||||
level = l;
|
||||
|
||||
return level;
|
||||
},
|
||||
|
||||
getLogger: function(name) {
|
||||
if (!name || typeof(name) !== 'string')
|
||||
throw new TypeError('name (string) required');
|
||||
|
||||
return new Log(name);
|
||||
}
|
||||
|
||||
};
|
|
@ -0,0 +1,98 @@
|
|||
// 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 LDAPResult = require('./result');
|
||||
|
||||
var dn = require('../dn');
|
||||
var Attribute = require('../attribute');
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
///--- Globals
|
||||
|
||||
var Ber = asn1.Ber;
|
||||
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
function AddRequest(options) {
|
||||
if (options) {
|
||||
if (typeof(options) !== 'object')
|
||||
throw new TypeError('options must be an object');
|
||||
if (options.entry && !(options.entry instanceof dn.DN))
|
||||
throw new TypeError('options.entry must be a DN');
|
||||
if (options.attributes) {
|
||||
if (!Array.isArray(options.attributes))
|
||||
throw new TypeError('options.attributes must be [Attribute]');
|
||||
options.attributes.forEach(function(a) {
|
||||
if (!Attribute.isAttribute(a))
|
||||
throw new TypeError('options.attributes must be [Attribute]');
|
||||
});
|
||||
}
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
|
||||
options.protocolOp = Protocol.LDAP_REQ_ADD;
|
||||
LDAPMessage.call(this, options);
|
||||
|
||||
this.entry = options.entry || null;
|
||||
this.attributes = options.attributes ? options.attributes.slice(0) : [];
|
||||
|
||||
var self = this;
|
||||
this.__defineGetter__('type', function() { return 'AddRequest'; });
|
||||
this.__defineGetter__('_dn', function() { return self.entry; });
|
||||
}
|
||||
util.inherits(AddRequest, LDAPMessage);
|
||||
module.exports = AddRequest;
|
||||
|
||||
|
||||
AddRequest.prototype._parse = function(ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
this.entry = dn.parse(ber.readString());
|
||||
|
||||
ber.readSequence();
|
||||
|
||||
var end = ber.offset + ber.length;
|
||||
while (ber.offset < end) {
|
||||
var a = new Attribute();
|
||||
a.parse(ber);
|
||||
this.attributes.push(a);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
AddRequest.prototype._toBer = function(ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
ber.writeString(this.entry.toString());
|
||||
ber.startSequence();
|
||||
this.attributes.forEach(function(a) {
|
||||
a.toBer(ber);
|
||||
});
|
||||
ber.endSequence();
|
||||
|
||||
return ber;
|
||||
};
|
||||
|
||||
|
||||
AddRequest.prototype._json = function(j) {
|
||||
assert.ok(j);
|
||||
|
||||
j.entry = this.entry.toString();
|
||||
j.attributes = [];
|
||||
|
||||
this.attributes.forEach(function(a) {
|
||||
j.attributes.push(a.json);
|
||||
});
|
||||
|
||||
return j;
|
||||
};
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var util = require('util');
|
||||
|
||||
var LDAPResult = require('./result');
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
function AddResponse(options) {
|
||||
if (options) {
|
||||
if (typeof(options) !== 'object')
|
||||
throw new TypeError('options must be an object');
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
|
||||
options.protocolOp = Protocol.LDAP_REP_ADD;
|
||||
LDAPResult.call(this, options);
|
||||
}
|
||||
util.inherits(AddResponse, LDAPResult);
|
||||
module.exports = AddResponse;
|
|
@ -0,0 +1,93 @@
|
|||
// 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 LDAPResult = require('./result');
|
||||
|
||||
|
||||
var dn = require('../dn');
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
///--- Globals
|
||||
|
||||
var Ber = asn1.Ber;
|
||||
|
||||
var LDAP_BIND_SIMPLE = 'Simple';
|
||||
var LDAP_BIND_SASL = 'Sasl';
|
||||
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
function BindRequest(options) {
|
||||
if (options) {
|
||||
if (typeof(options) !== 'object')
|
||||
throw new TypeError('options must be an object');
|
||||
if (options.name && !(options.name instanceof dn.DN))
|
||||
throw new TypeError('options.entry must be a DN');
|
||||
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
|
||||
options.protocolOp = Protocol.LDAP_REQ_BIND;
|
||||
LDAPMessage.call(this, options);
|
||||
|
||||
this.version = options.version || 0x03;
|
||||
this.name = options.name || null;
|
||||
this.authentication = options.authentication || LDAP_BIND_SIMPLE;
|
||||
this.credentials = options.credentials || '';
|
||||
|
||||
var self = this;
|
||||
this.__defineGetter__('type', function() { return 'BindRequest'; });
|
||||
this.__defineGetter__('_dn', function() { return self.name.toString(); });
|
||||
}
|
||||
util.inherits(BindRequest, LDAPMessage);
|
||||
module.exports = BindRequest;
|
||||
|
||||
|
||||
BindRequest.prototype._parse = function(ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
this.version = ber.readInt();
|
||||
this.name = dn.parse(ber.readString());
|
||||
|
||||
var t = ber.peek();
|
||||
|
||||
// TODO add support for SASL et al
|
||||
if (t !== Ber.Context)
|
||||
throw new Error('authentication 0x' + t.toString(16) + ' not supported');
|
||||
|
||||
this.authentication = LDAP_BIND_SIMPLE;
|
||||
this.credentials = ber.readString(Ber.Context);
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
BindRequest.prototype._toBer = function(ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
ber.writeInt(this.version);
|
||||
ber.writeString(this.name.toString());
|
||||
// TODO add support for SASL et al
|
||||
ber.writeString(this.credentials, Ber.Context);
|
||||
|
||||
return ber;
|
||||
};
|
||||
|
||||
|
||||
BindRequest.prototype._json = function(j) {
|
||||
assert.ok(j);
|
||||
|
||||
j.version = this.version;
|
||||
j.name = this.name;
|
||||
j.authenticationType = this.authentication;
|
||||
j.credentials = this.credentials;
|
||||
|
||||
return j;
|
||||
};
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var util = require('util');
|
||||
|
||||
var LDAPResult = require('./result');
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
function BindResponse(options) {
|
||||
if (options) {
|
||||
if (typeof(options) !== 'object')
|
||||
throw new TypeError('options must be an object');
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
|
||||
options.protocolOp = Protocol.LDAP_REP_BIND;
|
||||
LDAPResult.call(this, options);
|
||||
}
|
||||
util.inherits(BindResponse, LDAPResult);
|
||||
module.exports = BindResponse;
|
|
@ -0,0 +1,82 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var assert = require('assert');
|
||||
var util = require('util');
|
||||
|
||||
var LDAPMessage = require('./message');
|
||||
var LDAPResult = require('./result');
|
||||
|
||||
var dn = require('../dn');
|
||||
var Attribute = require('../attribute');
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
function CompareRequest(options) {
|
||||
if (options) {
|
||||
if (typeof(options) !== 'object')
|
||||
throw new TypeError('options must be an object');
|
||||
if (options.entry && !(options.entry instanceof dn.DN))
|
||||
throw new TypeError('options.entry must be a DN');
|
||||
if (options.attribute && typeof(options.attribute) !== 'string')
|
||||
throw new TypeError('options.attribute must be a string');
|
||||
if (options.value && typeof(options.value) !== 'string')
|
||||
throw new TypeError('options.value must be a string');
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
|
||||
options.protocolOp = Protocol.LDAP_REQ_COMPARE;
|
||||
LDAPMessage.call(this, options);
|
||||
|
||||
this.entry = options.entry || null;
|
||||
this.attribute = options.attribute || '';
|
||||
this.value = options.value || '';
|
||||
|
||||
var self = this;
|
||||
this.__defineGetter__('type', function() { return 'CompareRequest'; });
|
||||
this.__defineGetter__('_dn', function() {
|
||||
return self.entry ? self.entry.toString() : '';
|
||||
});
|
||||
}
|
||||
util.inherits(CompareRequest, LDAPMessage);
|
||||
module.exports = CompareRequest;
|
||||
|
||||
|
||||
CompareRequest.prototype._parse = function(ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
this.entry = dn.parse(ber.readString());
|
||||
|
||||
ber.readSequence();
|
||||
this.attribute = ber.readString();
|
||||
this.value = ber.readString();
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
CompareRequest.prototype._toBer = function(ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
ber.writeString(this.entry.toString());
|
||||
ber.startSequence();
|
||||
ber.writeString(this.attribute);
|
||||
ber.writeString(this.value);
|
||||
ber.endSequence();
|
||||
|
||||
return ber;
|
||||
};
|
||||
|
||||
|
||||
CompareRequest.prototype._json = function(j) {
|
||||
assert.ok(j);
|
||||
|
||||
j.entry = this.entry.toString();
|
||||
j.attribute = this.attribute;
|
||||
j.value = this.value;
|
||||
|
||||
return j;
|
||||
};
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var util = require('util');
|
||||
|
||||
var LDAPResult = require('./result');
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
function CompareResponse(options) {
|
||||
if (options) {
|
||||
if (typeof(options) !== 'object')
|
||||
throw new TypeError('options must be an object');
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
|
||||
options.protocolOp = Protocol.LDAP_REP_COMPARE;
|
||||
LDAPResult.call(this, options);
|
||||
}
|
||||
util.inherits(CompareResponse, LDAPResult);
|
||||
module.exports = CompareResponse;
|
|
@ -0,0 +1,78 @@
|
|||
// 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 LDAPResult = require('./result');
|
||||
|
||||
var dn = require('../dn');
|
||||
var Attribute = require('../attribute');
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
///--- Globals
|
||||
|
||||
var Ber = asn1.Ber;
|
||||
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
function DeleteRequest(options) {
|
||||
if (options) {
|
||||
if (typeof(options) !== 'object')
|
||||
throw new TypeError('options must be an object');
|
||||
if (options.entry && !(options.entry instanceof dn.DN))
|
||||
throw new TypeError('options.entry must be a DN');
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
|
||||
options.protocolOp = Protocol.LDAP_REQ_DELETE;
|
||||
LDAPMessage.call(this, options);
|
||||
|
||||
this.entry = options.entry || null;
|
||||
|
||||
var self = this;
|
||||
this.__defineGetter__('type', function() { return 'DeleteRequest'; });
|
||||
this.__defineGetter__('_dn', function() {
|
||||
return self.entry ? self.entry.toString() : '';
|
||||
});
|
||||
}
|
||||
util.inherits(DeleteRequest, LDAPMessage);
|
||||
module.exports = DeleteRequest;
|
||||
|
||||
|
||||
DeleteRequest.prototype._parse = function(ber, length) {
|
||||
assert.ok(ber);
|
||||
|
||||
// What a hack; LDAP is so annoying with its decisions of what to
|
||||
// shortcut, so this is totally a hack to work around the way the delete
|
||||
// message is structured
|
||||
this.entry = dn.parse(ber.buffer.slice(0, length).toString());
|
||||
ber._offset += length;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
DeleteRequest.prototype._toBer = function(ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
var buf = new Buffer(this.entry.toString());
|
||||
for (var i = 0; i < buf.length; i++)
|
||||
ber.writeByte(buf[i]);
|
||||
|
||||
return ber;
|
||||
};
|
||||
|
||||
|
||||
DeleteRequest.prototype._json = function(j) {
|
||||
assert.ok(j);
|
||||
|
||||
j.entry = this.entry;
|
||||
|
||||
return j;
|
||||
};
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var util = require('util');
|
||||
|
||||
var LDAPResult = require('./result');
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
function DeleteResponse(options) {
|
||||
if (options) {
|
||||
if (typeof(options) !== 'object')
|
||||
throw new TypeError('options must be an object');
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
|
||||
options.protocolOp = Protocol.LDAP_REP_DELETE;
|
||||
LDAPResult.call(this, options);
|
||||
}
|
||||
util.inherits(DeleteResponse, LDAPResult);
|
||||
module.exports = DeleteResponse;
|
|
@ -0,0 +1,97 @@
|
|||
// 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 LDAPResult = require('./result');
|
||||
|
||||
var dn = require('../dn');
|
||||
var Attribute = require('../attribute');
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var Ber = asn1.Ber;
|
||||
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
function ExtendedRequest(options) {
|
||||
if (options) {
|
||||
if (typeof(options) !== 'object')
|
||||
throw new TypeError('options must be an object');
|
||||
if (options.requestName && typeof(options.requestName) !== 'string')
|
||||
throw new TypeError('options.requestName must be a string');
|
||||
if (options.requestValue && typeof(options.requestValue) !== 'string')
|
||||
throw new TypeError('options.requestValue must be a string');
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
|
||||
options.protocolOp = Protocol.LDAP_REQ_EXTENSION;
|
||||
LDAPMessage.call(this, options);
|
||||
|
||||
this.requestName = options.requestName || '';
|
||||
this.requestValue = options.requestValue || undefined;
|
||||
|
||||
this.__defineGetter__('type', function() { return 'ExtendedRequest'; });
|
||||
this.__defineGetter__('_dn', function() { return this.requestName; });
|
||||
this.__defineGetter__('name', function() {
|
||||
return this.requestName;
|
||||
});
|
||||
this.__defineGetter__('value', function() {
|
||||
return this.requestValue;
|
||||
});
|
||||
this.__defineSetter__('name', function(name) {
|
||||
if (typeof(name) !== 'string')
|
||||
throw new TypeError('name must be a string');
|
||||
|
||||
this.requestName = name;
|
||||
});
|
||||
this.__defineSetter__('value', function(val) {
|
||||
if (typeof(val) !== 'string')
|
||||
throw new TypeError('value must be a string');
|
||||
|
||||
this.requestValue = val;
|
||||
});
|
||||
}
|
||||
util.inherits(ExtendedRequest, LDAPMessage);
|
||||
module.exports = ExtendedRequest;
|
||||
|
||||
|
||||
ExtendedRequest.prototype._parse = function(ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
this.requestName = ber.readString(0x80);
|
||||
if (ber.peek() === 0x81)
|
||||
this.requestValue = ber.readString(0x81);
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
ExtendedRequest.prototype._toBer = function(ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
ber.writeString(this.requestName, 0x80);
|
||||
if (this.requestValue)
|
||||
ber.writeString(this.requestValue, 0x81);
|
||||
|
||||
return ber;
|
||||
};
|
||||
|
||||
|
||||
ExtendedRequest.prototype._json = function(j) {
|
||||
assert.ok(j);
|
||||
|
||||
j.requestName = this.requestName;
|
||||
j.requestValue = this.requestValue;
|
||||
|
||||
return j;
|
||||
};
|
|
@ -0,0 +1,92 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var assert = require('assert');
|
||||
var util = require('util');
|
||||
|
||||
var LDAPResult = require('./result');
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
function ExtendedResponse(options) {
|
||||
if (options) {
|
||||
if (typeof(options) !== 'object')
|
||||
throw new TypeError('options must be an object');
|
||||
if (options.responseName && typeof(options.responseName) !== 'string')
|
||||
throw new TypeError('options.responseName must be a string');
|
||||
if (options.responseValue && typeof(options.responseValue) !== 'string')
|
||||
throw new TypeError('options.responseValue must be a string');
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
|
||||
this.responseName = options.responseName || undefined;
|
||||
this.responseValue = options.responseValue || undefined;
|
||||
|
||||
options.protocolOp = Protocol.LDAP_REP_EXTENSION;
|
||||
LDAPResult.call(this, options);
|
||||
|
||||
this.__defineGetter__('name', function() {
|
||||
return this.responseName;
|
||||
});
|
||||
this.__defineGetter__('value', function() {
|
||||
return this.responseValue;
|
||||
});
|
||||
this.__defineSetter__('name', function(name) {
|
||||
if (typeof(name) !== 'string')
|
||||
throw new TypeError('name must be a string');
|
||||
|
||||
this.responseName = name;
|
||||
});
|
||||
this.__defineSetter__('value', function(val) {
|
||||
if (typeof(val) !== 'string')
|
||||
throw new TypeError('value must be a string');
|
||||
|
||||
this.responseValue = val;
|
||||
});
|
||||
}
|
||||
util.inherits(ExtendedResponse, LDAPResult);
|
||||
module.exports = ExtendedResponse;
|
||||
|
||||
|
||||
ExtendedResponse.prototype._parse = function(ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
if (!LDAPResult.prototype._parse.call(this, ber))
|
||||
return false;
|
||||
|
||||
if (ber.peek() === 0x8a)
|
||||
this.responseName = ber.readString(0x8a);
|
||||
if (ber.peek() === 0x8b)
|
||||
this.responseValue = ber.readString(0x8b);
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
ExtendedResponse.prototype._toBer = function(ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
if (!LDAPResult.prototype._toBer.call(this, ber))
|
||||
return false;
|
||||
|
||||
if (this.responseName)
|
||||
ber.writeString(this.responseName, 0x8a);
|
||||
if (this.responseValue)
|
||||
ber.writeString(this.responseValue, 0x8b);
|
||||
|
||||
return ber;
|
||||
};
|
||||
|
||||
|
||||
ExtendedResponse.prototype._json = function(j) {
|
||||
assert.ok(j);
|
||||
|
||||
j = LDAPResult.prototype._json.call(this, j);
|
||||
|
||||
j.responseName = this.responseName;
|
||||
j.responseValue = this.responseValue;
|
||||
|
||||
return j;
|
||||
};
|
|
@ -0,0 +1,57 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var LDAPMessage = require('./message');
|
||||
var LDAPResult = require('./result');
|
||||
var Parser = require('./parser');
|
||||
|
||||
var AddRequest = require('./add_request');
|
||||
var AddResponse = require('./add_response');
|
||||
var BindRequest = require('./bind_request');
|
||||
var BindResponse = require('./bind_response');
|
||||
var CompareRequest = require('./compare_request');
|
||||
var CompareResponse = require('./compare_response');
|
||||
var DeleteRequest = require('./del_request');
|
||||
var DeleteResponse = require('./del_response');
|
||||
var ExtendedRequest = require('./ext_request');
|
||||
var ExtendedResponse = require('./ext_response');
|
||||
var ModifyRequest = require('./modify_request');
|
||||
var ModifyResponse = require('./modify_response');
|
||||
var ModifyDNRequest = require('./moddn_request');
|
||||
var ModifyDNResponse = require('./moddn_response');
|
||||
var SearchRequest = require('./search_request');
|
||||
var SearchEntry = require('./search_entry');
|
||||
var SearchResponse = require('./search_response');
|
||||
var UnbindRequest = require('./unbind_request');
|
||||
var UnbindResponse = require('./unbind_response');
|
||||
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
module.exports = {
|
||||
|
||||
LDAPMessage: LDAPMessage,
|
||||
LDAPResult: LDAPResult,
|
||||
Parser: Parser,
|
||||
|
||||
AddRequest: AddRequest,
|
||||
AddResponse: AddResponse,
|
||||
BindRequest: BindRequest,
|
||||
BindResponse: BindResponse,
|
||||
CompareRequest: CompareRequest,
|
||||
CompareResponse: CompareResponse,
|
||||
DeleteRequest: DeleteRequest,
|
||||
DeleteResponse: DeleteResponse,
|
||||
ExtendedRequest: ExtendedRequest,
|
||||
ExtendedResponse: ExtendedResponse,
|
||||
ModifyRequest: ModifyRequest,
|
||||
ModifyResponse: ModifyResponse,
|
||||
ModifyDNRequest: ModifyDNRequest,
|
||||
ModifyDNResponse: ModifyDNResponse,
|
||||
SearchRequest: SearchRequest,
|
||||
SearchEntry: SearchEntry,
|
||||
SearchResponse: SearchResponse,
|
||||
UnbindRequest: UnbindRequest,
|
||||
UnbindResponse: UnbindResponse
|
||||
|
||||
};
|
|
@ -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 Control = require('../control');
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
var logStub = require('../log_stub');
|
||||
|
||||
///--- Globals
|
||||
|
||||
var Ber = asn1.Ber;
|
||||
var BerReader = asn1.BerReader;
|
||||
var BerWriter = asn1.BerWriter;
|
||||
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
/**
|
||||
* LDAPMessage structure.
|
||||
*
|
||||
* @param {Object} options stuff.
|
||||
*/
|
||||
function LDAPMessage(options) {
|
||||
if (!options || typeof(options) !== 'object')
|
||||
throw new TypeError('options (object) required');
|
||||
|
||||
this.messageID = options.messageID || 0;
|
||||
this.protocolOp = options.protocolOp || undefined;
|
||||
this.controls = options.controls ? options.controls.slice(0) : [];
|
||||
|
||||
this.log4js = options.log4js || logStub;
|
||||
|
||||
var self = this;
|
||||
this.__defineGetter__('id', function() { return self.messageID; });
|
||||
this.__defineGetter__('dn', function() { return self._dn || ''; });
|
||||
this.__defineGetter__('type', function() { return 'LDAPMessage'; });
|
||||
this.__defineGetter__('json', function() {
|
||||
var j = {
|
||||
messageID: self.messageID,
|
||||
protocolOp: self.type
|
||||
};
|
||||
j = self._json(j);
|
||||
j.controls = self.controls;
|
||||
return j;
|
||||
});
|
||||
this.__defineGetter__('log', function() {
|
||||
if (!self._log)
|
||||
self._log = self.log4js.getLogger(self.type);
|
||||
return self._log;
|
||||
});
|
||||
}
|
||||
module.exports = LDAPMessage;
|
||||
|
||||
|
||||
LDAPMessage.prototype.toString = function() {
|
||||
return JSON.stringify(this.json);
|
||||
};
|
||||
|
||||
|
||||
LDAPMessage.prototype.parse = function(data, length) {
|
||||
if (!data || !Buffer.isBuffer(data))
|
||||
throw new TypeError('data (buffer) required');
|
||||
|
||||
if (this.log.isTraceEnabled())
|
||||
this.log.trace('parse: data=%s, len=%d', util.inspect(data), length);
|
||||
|
||||
var ber = new BerReader(data);
|
||||
|
||||
// Delegate off to the specific type to parse
|
||||
this._parse(ber, length);
|
||||
|
||||
// Look for controls
|
||||
if (ber.peek === Protocol.LDAP_CONTROLS && ber.offset < length) {
|
||||
ber.readSequence();
|
||||
var end = ber.offset + ber.length;
|
||||
while (ber.offset < end) {
|
||||
var c = new Control();
|
||||
if (c.parse(ber))
|
||||
this.controls.push(c);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.log.isTraceEnabled())
|
||||
this.log.trace('Parsing done: %j', this.json);
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
LDAPMessage.prototype.toBer = function() {
|
||||
var writer = new BerWriter();
|
||||
writer.startSequence();
|
||||
writer.writeInt(this.messageID);
|
||||
writer.startSequence(this.protocolOp);
|
||||
|
||||
if (this._toBer)
|
||||
writer = this._toBer(writer);
|
||||
|
||||
writer.endSequence();
|
||||
writer.endSequence();
|
||||
return writer.buffer;
|
||||
};
|
|
@ -0,0 +1,95 @@
|
|||
// 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 LDAPResult = require('./result');
|
||||
|
||||
var dn = require('../dn');
|
||||
var Attribute = require('../attribute');
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var Ber = asn1.Ber;
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
function ModifyDNRequest(options) {
|
||||
if (options) {
|
||||
if (typeof(options) !== 'object')
|
||||
throw new TypeError('options must be an object');
|
||||
if (options.entry && !(options.entry instanceof dn.DN))
|
||||
throw new TypeError('options.entry must be a DN');
|
||||
if (options.newRdn && !(options.newRdn instanceof dn.DN))
|
||||
throw new TypeError('options.newRdn must be a DN');
|
||||
if (options.deleteOldRdn !== undefined &&
|
||||
typeof(options.deleteOldRdn) !== 'boolean')
|
||||
throw new TypeError('options.deleteOldRdn must be a boolean');
|
||||
if (options.newSuperior && !(options.newSuperior instanceof dn.DN))
|
||||
throw new TypeError('options.newSuperior must be a DN');
|
||||
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
|
||||
options.protocolOp = Protocol.LDAP_REQ_MODRDN;
|
||||
LDAPMessage.call(this, options);
|
||||
|
||||
this.entry = options.entry || null;
|
||||
this.newRdn = options.newRdn || null;
|
||||
this.deleteOldRdn = options.deleteOldRdn || false;
|
||||
this.newSuperior = options.newSuperior || null;
|
||||
|
||||
var self = this;
|
||||
this.__defineGetter__('type', function() { return 'ModifyDNRequest'; });
|
||||
this.__defineGetter__('_dn', function() {
|
||||
return self.entry ? self.entry.toString() : '';
|
||||
});
|
||||
}
|
||||
util.inherits(ModifyDNRequest, LDAPMessage);
|
||||
module.exports = ModifyDNRequest;
|
||||
|
||||
|
||||
ModifyDNRequest.prototype._parse = function(ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
this.entry = dn.parse(ber.readString());
|
||||
this.newRdn = dn.parse(ber.readString());
|
||||
this.deleteOldRdn = ber.readBoolean();
|
||||
if (ber.peek() === Ber.OctetString)
|
||||
this.newSuperior = ber.readString();
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
ModifyDNRequest.prototype._toBer = function(ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
ber.writeString(this.entry.toString());
|
||||
ber.writeString(this.newRdn.toString());
|
||||
ber.writeBoolean(this.deleteOldRdn);
|
||||
if (this.newSuperior)
|
||||
ber.writeString(this.newSuperior.toString());
|
||||
|
||||
return ber;
|
||||
};
|
||||
|
||||
|
||||
ModifyDNRequest.prototype._json = function(j) {
|
||||
assert.ok(j);
|
||||
|
||||
j.entry = this.entry.toString();
|
||||
j.newRdn = this.newRdn.toString();
|
||||
j.deleteOldRdn = this.deleteOldRdn;
|
||||
j.newSuperior = this.newSuperior ? this.newSuperior.toString() : '';
|
||||
|
||||
return j;
|
||||
};
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var util = require('util');
|
||||
|
||||
var LDAPResult = require('./result');
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
function ModifyDNResponse(options) {
|
||||
if (options) {
|
||||
if (typeof(options) !== 'object')
|
||||
throw new TypeError('options must be an object');
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
|
||||
options.protocolOp = Protocol.LDAP_REP_MODRDN;
|
||||
LDAPResult.call(this, options);
|
||||
}
|
||||
util.inherits(ModifyDNResponse, LDAPResult);
|
||||
module.exports = ModifyDNResponse;
|
|
@ -0,0 +1,93 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var assert = require('assert');
|
||||
var util = require('util');
|
||||
|
||||
var LDAPMessage = require('./message');
|
||||
var LDAPResult = require('./result');
|
||||
|
||||
var dn = require('../dn');
|
||||
var Change = require('../change');
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
function ModifyRequest(options) {
|
||||
if (options) {
|
||||
if (typeof(options) !== 'object')
|
||||
throw new TypeError('options must be an object');
|
||||
if (options.object && !(options.object instanceof dn.DN))
|
||||
throw new TypeError('options.object must be a DN');
|
||||
if (options.attributes) {
|
||||
if (!Array.isArray(options.attributes))
|
||||
throw new TypeError('options.attributes must be [Attribute]');
|
||||
options.attributes.forEach(function(a) {
|
||||
if (!(a instanceof Attribute))
|
||||
throw new TypeError('options.attributes must be [Attribute]');
|
||||
});
|
||||
}
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
|
||||
options.protocolOp = Protocol.LDAP_REQ_MODIFY;
|
||||
LDAPMessage.call(this, options);
|
||||
|
||||
this.object = options.object || null;
|
||||
this.changes = options.changes ? options.changes.slice(0) : [];
|
||||
|
||||
var self = this;
|
||||
this.__defineGetter__('type', function() { return 'ModifyRequest'; });
|
||||
this.__defineGetter__('_dn', function() {
|
||||
return self.object ? self.object.toString() : '';
|
||||
});
|
||||
}
|
||||
util.inherits(ModifyRequest, LDAPMessage);
|
||||
module.exports = ModifyRequest;
|
||||
|
||||
|
||||
ModifyRequest.prototype._parse = function(ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
this.object = dn.parse(ber.readString());
|
||||
|
||||
ber.readSequence();
|
||||
var end = ber.offset + ber.length;
|
||||
while (ber.offset < end) {
|
||||
var c = new Change();
|
||||
c.parse(ber);
|
||||
this.changes.push(c);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
ModifyRequest.prototype._toBer = function(ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
ber.writeString(this.object.toString());
|
||||
ber.startSequence();
|
||||
this.changes.forEach(function(c) {
|
||||
c.toBer(ber);
|
||||
});
|
||||
ber.endSequence();
|
||||
|
||||
return ber;
|
||||
};
|
||||
|
||||
|
||||
ModifyRequest.prototype._json = function(j) {
|
||||
assert.ok(j);
|
||||
|
||||
j.object = this.object;
|
||||
j.changes = [];
|
||||
|
||||
this.changes.forEach(function(c) {
|
||||
j.changes.push(c.json);
|
||||
});
|
||||
|
||||
return j;
|
||||
};
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var util = require('util');
|
||||
|
||||
var LDAPResult = require('./result');
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
function ModifyResponse(options) {
|
||||
if (options) {
|
||||
if (typeof(options) !== 'object')
|
||||
throw new TypeError('options must be an object');
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
|
||||
options.protocolOp = Protocol.LDAP_REP_MODIFY;
|
||||
LDAPResult.call(this, options);
|
||||
}
|
||||
util.inherits(ModifyResponse, LDAPResult);
|
||||
module.exports = ModifyResponse;
|
|
@ -0,0 +1,247 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var assert = require('assert');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var util = require('util');
|
||||
|
||||
var asn1 = require('asn1');
|
||||
|
||||
var AddRequest = require('./add_request');
|
||||
var AddResponse = require('./add_response');
|
||||
var BindRequest = require('./bind_request');
|
||||
var BindResponse = require('./bind_response');
|
||||
var CompareRequest = require('./compare_request');
|
||||
var CompareResponse = require('./compare_response');
|
||||
var DeleteRequest = require('./del_request');
|
||||
var DeleteResponse = require('./del_response');
|
||||
var ExtendedRequest = require('./ext_request');
|
||||
var ExtendedResponse = require('./ext_response');
|
||||
var ModifyRequest = require('./modify_request');
|
||||
var ModifyResponse = require('./modify_response');
|
||||
var ModifyDNRequest = require('./moddn_request');
|
||||
var ModifyDNResponse = require('./moddn_response');
|
||||
var SearchRequest = require('./search_request');
|
||||
var SearchEntry = require('./search_entry');
|
||||
var SearchResponse = require('./search_response');
|
||||
var UnbindRequest = require('./unbind_request');
|
||||
var UnbindResponse = require('./unbind_response');
|
||||
var Message = require('./message');
|
||||
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
// Just make sure this adds to the prototype
|
||||
require('buffertools');
|
||||
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var Ber = asn1.Ber;
|
||||
var BerReader = asn1.BerReader;
|
||||
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
function Parser(options) {
|
||||
if (!options || typeof(options) !== 'object')
|
||||
throw new TypeError('options (object) required');
|
||||
if (!options.log4js || typeof(options.log4js) !== 'object')
|
||||
throw new TypeError('options.log4js (object) required');
|
||||
|
||||
EventEmitter.call(this);
|
||||
|
||||
this._reset();
|
||||
|
||||
var self = this;
|
||||
this.log4js = options.log4js;
|
||||
this.log = this.log4js.getLogger('Parser');
|
||||
}
|
||||
util.inherits(Parser, EventEmitter);
|
||||
module.exports = Parser;
|
||||
|
||||
|
||||
Parser.prototype.write = function(data) {
|
||||
if (!data || !Buffer.isBuffer(data))
|
||||
throw new TypeError('data (buffer) required');
|
||||
|
||||
var self = this;
|
||||
|
||||
if (this._buffer)
|
||||
data = this._buffer.concat(data);
|
||||
|
||||
if (this.log.isTraceEnabled())
|
||||
this.log.trace('Processing buffer (concat\'d): ' + util.inspect(data));
|
||||
|
||||
// If there's more than one message in this buffer
|
||||
var extra;
|
||||
|
||||
try {
|
||||
if (this._message === null) {
|
||||
var ber = new BerReader(data);
|
||||
if (!this._newMessage(ber))
|
||||
return false;
|
||||
|
||||
data = data.slice(ber.offset);
|
||||
}
|
||||
|
||||
if (data.length > this._messageLength) {
|
||||
extra = data.slice(ber.length);
|
||||
data = data.slice(0, ber.length);
|
||||
}
|
||||
|
||||
if (!this._message.parse(data, ber.length))
|
||||
this.emit('protocolError', new Error('TODO'));
|
||||
|
||||
var message = this._message;
|
||||
this._reset();
|
||||
this.emit('message', message);
|
||||
|
||||
} catch (e) {
|
||||
if (e.name === 'InvalidAsn1Error') {
|
||||
self.emit('protocolError', e, self._message);
|
||||
} else {
|
||||
self.emit('error', e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Another message is already there
|
||||
if (extra) {
|
||||
if (this.log.isTraceEnabled())
|
||||
this.log.trace('parsing extra bytes: ' + util.inspect(extra));
|
||||
|
||||
return this.write(extra);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
Parser.prototype._newMessage = function(ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
if (this._messageLength === null) {
|
||||
if (ber.readSequence() === null) { // not enough data for the length?
|
||||
this._buffer = ber.buffer;
|
||||
if (this.log.isTraceEnabled())
|
||||
this.log.trace('Not enough data for the message header');
|
||||
|
||||
return false;
|
||||
}
|
||||
this._messageLength = ber.length;
|
||||
|
||||
}
|
||||
|
||||
if (ber.remain < this._messageLength) {
|
||||
if (this.log.isTraceEnabled())
|
||||
this.log.trace('Not enough data for the message');
|
||||
|
||||
this._buffer = ber.buffer;
|
||||
return false;
|
||||
}
|
||||
|
||||
var messageID = ber.readInt();
|
||||
var type = ber.readSequence();
|
||||
|
||||
if (this.log.isTraceEnabled())
|
||||
this.log.trace('message id=%d, type=0x%s', messageID, type.toString(16));
|
||||
|
||||
var Message;
|
||||
switch (type) {
|
||||
|
||||
case Protocol.LDAP_REQ_ADD:
|
||||
Message = AddRequest;
|
||||
break;
|
||||
|
||||
case Protocol.LDAP_REP_ADD:
|
||||
Message = AddResponse;
|
||||
break;
|
||||
|
||||
case Protocol.LDAP_REQ_BIND:
|
||||
Message = BindRequest;
|
||||
break;
|
||||
|
||||
case Protocol.LDAP_REP_BIND:
|
||||
Message = BindResponse;
|
||||
break;
|
||||
|
||||
case Protocol.LDAP_REQ_COMPARE:
|
||||
Message = CompareRequest;
|
||||
break;
|
||||
|
||||
case Protocol.LDAP_REP_COMPARE:
|
||||
Message = CompareResponse;
|
||||
break;
|
||||
|
||||
case Protocol.LDAP_REQ_DELETE:
|
||||
Message = DeleteRequest;
|
||||
break;
|
||||
|
||||
case Protocol.LDAP_REP_DELETE:
|
||||
Message = DeleteResponse;
|
||||
break;
|
||||
|
||||
case Protocol.LDAP_REQ_EXTENSION:
|
||||
Message = ExtendedRequest;
|
||||
break;
|
||||
|
||||
case Protocol.LDAP_REP_EXTENSION:
|
||||
Message = ExtendedResponse;
|
||||
break;
|
||||
|
||||
case Protocol.LDAP_REQ_MODIFY:
|
||||
Message = ModifyRequest;
|
||||
break;
|
||||
|
||||
case Protocol.LDAP_REP_MODIFY:
|
||||
Message = ModifyResponse;
|
||||
break;
|
||||
|
||||
case Protocol.LDAP_REQ_MODRDN:
|
||||
Message = ModifyDNRequest;
|
||||
break;
|
||||
|
||||
case Protocol.LDAP_REP_MODRDN:
|
||||
Message = ModifyDNResponse;
|
||||
break;
|
||||
|
||||
case Protocol.LDAP_REQ_SEARCH:
|
||||
Message = SearchRequest;
|
||||
break;
|
||||
|
||||
case Protocol.LDAP_REP_SEARCH_ENTRY:
|
||||
Message = SearchEntry;
|
||||
break;
|
||||
|
||||
case Protocol.LDAP_REP_SEARCH:
|
||||
Message = SearchResponse;
|
||||
break;
|
||||
|
||||
case Protocol.LDAP_REQ_UNBIND:
|
||||
Message = UnbindRequest;
|
||||
break;
|
||||
|
||||
default:
|
||||
var e = new Error('protocolOp 0x' + type.toString(16) + ' not supported');
|
||||
this.emit('protocolError', e, messageID);
|
||||
this._reset();
|
||||
return false;
|
||||
}
|
||||
assert.ok(Message);
|
||||
|
||||
var self = this;
|
||||
this._message = new Message({
|
||||
messageID: messageID,
|
||||
log4js: self.log4js
|
||||
});
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
Parser.prototype._reset = function() {
|
||||
this._message = null;
|
||||
this._messageLength = null;
|
||||
this._buffer = null;
|
||||
};
|
|
@ -0,0 +1,117 @@
|
|||
// 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');
|
||||
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var Ber = asn1.Ber;
|
||||
var BerWriter = asn1.BerWriter;
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
function LDAPResult(options) {
|
||||
if (options) {
|
||||
if (typeof(options) !== 'object')
|
||||
throw new TypeError('options (object) required');
|
||||
if (options.status && typeof(options.status) !== 'number')
|
||||
throw new TypeError('options.status must be a number');
|
||||
if (options.matchedDN && typeof(options.matchedDN) !== 'string')
|
||||
throw new TypeError('options.matchedDN must be a string');
|
||||
if (options.errorMessage && typeof(options.errorMessage) !== 'string')
|
||||
throw new TypeError('options.errorMessage must be a string');
|
||||
|
||||
if (options.referrals) {
|
||||
if (!(options.referrals instanceof Array))
|
||||
throw new TypeError('options.referrrals must be an array[string]');
|
||||
options.referrals.forEach(function(r) {
|
||||
if (typeof(r) !== 'string')
|
||||
throw new TypeError('options.referrals must be an array[string]');
|
||||
});
|
||||
}
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
|
||||
LDAPMessage.call(this, options);
|
||||
|
||||
this.status = options.status || 0; // LDAP SUCCESS
|
||||
this.matchedDN = options.matchedDN || '';
|
||||
this.errorMessage = options.errorMessage || '';
|
||||
this.referrals = options.referrals || [];
|
||||
|
||||
this.__defineGetter__('type', function() { return 'LDAPResult'; });
|
||||
}
|
||||
util.inherits(LDAPResult, LDAPMessage);
|
||||
module.exports = LDAPResult;
|
||||
|
||||
|
||||
LDAPResult.prototype.end = function(status) {
|
||||
assert.ok(this.connection);
|
||||
|
||||
if (typeof(status) === 'number')
|
||||
this.status = status;
|
||||
|
||||
var ber = this.toBer();
|
||||
if (this.log.isDebugEnabled())
|
||||
this.log.debug('%s: sending: %j', this.connection.ldap.id, this.json);
|
||||
|
||||
this.connection.write(ber);
|
||||
};
|
||||
|
||||
|
||||
LDAPResult.prototype._parse = function(ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
this.status = ber.readEnumeration();
|
||||
this.matchedDN = ber.readString();
|
||||
this.errorMessage = ber.readString();
|
||||
|
||||
var t = ber.peek();
|
||||
|
||||
if (t === Protocol.LDAP_REP_REFERRAL) {
|
||||
var end = ber.offset + ber.length;
|
||||
while (ber.offset < end)
|
||||
this.referrals.push(ber.readString());
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
LDAPResult.prototype._toBer = function(ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
ber.writeEnumeration(this.status);
|
||||
ber.writeString(this.matchedDN || '');
|
||||
ber.writeString(this.errorMessage || '');
|
||||
|
||||
if (this.referrals.length) {
|
||||
ber.startSequence(Protocol.LDAP_REP_REFERRAL);
|
||||
ber.writeStringArray(this.referrals);
|
||||
ber.endSequence();
|
||||
}
|
||||
|
||||
return ber;
|
||||
};
|
||||
|
||||
|
||||
LDAPResult.prototype._json = function(j) {
|
||||
assert.ok(j);
|
||||
|
||||
j.status = this.status;
|
||||
j.matchedDN = this.matchedDN;
|
||||
j.errorMessage = this.errorMessage;
|
||||
j.referrals = this.referrals;
|
||||
|
||||
return j;
|
||||
};
|
|
@ -0,0 +1,110 @@
|
|||
// 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 Attribute = require('../attribute');
|
||||
var dn = require('../dn');
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var BerWriter = asn1.BerWriter;
|
||||
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
function SearchEntry(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');
|
||||
if (options.attributes && !Array.isArray(options.attributes))
|
||||
throw new TypeError('options.attributes must be an array[Attribute]');
|
||||
if (options.attributes && options.attributes.length) {
|
||||
options.attributes.forEach(function(a) {
|
||||
if (!(a instanceof Attribute))
|
||||
throw new TypeError('options.attributes must be an array[Attribute]');
|
||||
});
|
||||
}
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
|
||||
options.protocolOp = Protocol.LDAP_REP_SEARCH_ENTRY;
|
||||
LDAPMessage.call(this, options);
|
||||
|
||||
this.objectName = options.objectName || null;
|
||||
this.attributes = options.attributes ? options.attributes.slice(0) : [];
|
||||
|
||||
var self = this;
|
||||
this.__defineGetter__('type', function() { return 'SearchEntry'; });
|
||||
this.__defineGetter__('_dn', function() {
|
||||
return self.objectName.toString();
|
||||
});
|
||||
}
|
||||
util.inherits(SearchEntry, LDAPMessage);
|
||||
module.exports = SearchEntry;
|
||||
|
||||
|
||||
SearchEntry.prototype.addAttribute = function(attr) {
|
||||
if (!attr || typeof(attr) !== 'object')
|
||||
throw new TypeError('attr (attribute) required');
|
||||
|
||||
this.attributes.push(attr);
|
||||
};
|
||||
|
||||
|
||||
SearchEntry.prototype._json = function(j) {
|
||||
assert.ok(j);
|
||||
|
||||
j.objectName = this.objectName.toString();
|
||||
j.attributes = [];
|
||||
this.attributes.forEach(function(a) {
|
||||
j.attributes.push(a.json);
|
||||
});
|
||||
|
||||
return j;
|
||||
};
|
||||
|
||||
|
||||
SearchEntry.prototype._parse = function(ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
this.objectName = ber.readString();
|
||||
assert.ok(ber.readSequence());
|
||||
var end = ber.offset + ber.length;
|
||||
|
||||
while (ber.offset < end) {
|
||||
var a = new Attribute();
|
||||
a.parse(ber);
|
||||
this.attributes.push(a);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
SearchEntry.prototype._toBer = function(ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
ber.writeString(this.objectName.toString());
|
||||
ber.startSequence();
|
||||
this.attributes.forEach(function(a) {
|
||||
// This may or may not be an attribute
|
||||
ber = Attribute.toBer(a, ber);
|
||||
});
|
||||
ber.endSequence();
|
||||
|
||||
return ber;
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,154 @@
|
|||
// 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 LDAPResult = require('./result');
|
||||
|
||||
var dn = require('../dn');
|
||||
var filters = require('../filters');
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var Ber = asn1.Ber;
|
||||
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
function SearchRequest(options) {
|
||||
if (options) {
|
||||
if (typeof(options) !== 'object')
|
||||
throw new TypeError('options must be an object');
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
|
||||
options.protocolOp = Protocol.LDAP_REQ_SEARCH;
|
||||
LDAPMessage.call(this, options);
|
||||
|
||||
var self = this;
|
||||
this.__defineGetter__('type', function() { return 'SearchRequest'; });
|
||||
this.__defineGetter__('_dn', function() {
|
||||
return self.baseObject;
|
||||
});
|
||||
this.__defineGetter__('scope', function() {
|
||||
switch (self._scope) {
|
||||
case Protocol.SCOPE_BASE_OBJECT: return 'base';
|
||||
case Protocol.SCOPE_ONE_LEVEL: return 'one';
|
||||
case Protocol.SCOPE_SUBTREE: return 'sub';
|
||||
default:
|
||||
throw new Error(self._scope + ' is an invalid search scope');
|
||||
}
|
||||
});
|
||||
this.__defineSetter__('scope', function(s) {
|
||||
if (typeof(s) === 'string') {
|
||||
switch (s) {
|
||||
case 'base':
|
||||
self._scope = Protocol.SCOPE_BASE_OBJECT;
|
||||
break;
|
||||
case 'one':
|
||||
self._scope = Protocol.SCOPE_ONE_LEVEL;
|
||||
break;
|
||||
case 'sub':
|
||||
self._scope = Protocol.SCOPE_SUBTREE;
|
||||
break;
|
||||
default:
|
||||
throw new Error(s + ' is an invalid search scope');
|
||||
}
|
||||
} else {
|
||||
self._scope = s;
|
||||
}
|
||||
});
|
||||
|
||||
this.baseObject = options.baseObject || new dn.DN([{}]);
|
||||
this.scope = options.scope || 'base';
|
||||
this.derefAliases = options.derefAliases || Protocol.NEVER_DEREF_ALIASES;
|
||||
this.sizeLimit = options.sizeLimit || 0;
|
||||
this.timeLimit = options.timeLimit || 00;
|
||||
this.typesOnly = options.typesOnly || false;
|
||||
this.filter = options.filter || null;
|
||||
this.attributes = options.attributes ? options.attributes.slice(0) : [];
|
||||
}
|
||||
util.inherits(SearchRequest, LDAPMessage);
|
||||
module.exports = SearchRequest;
|
||||
|
||||
|
||||
SearchRequest.prototype.newResult = function() {
|
||||
var self = this;
|
||||
|
||||
return new LDAPResult({
|
||||
messageID: self.messageID,
|
||||
protocolOp: Protocol.LDAP_REP_SEARCH
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
SearchRequest.prototype._parse = function(ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
this.baseObject = dn.parse(ber.readString());
|
||||
this.scope = ber.readEnumeration();
|
||||
this.derefAliases = ber.readEnumeration();
|
||||
this.sizeLimit = ber.readInt();
|
||||
this.timeLimit = ber.readInt();
|
||||
this.typesOnly = ber.readBoolean();
|
||||
|
||||
this.filter = filters.parse(ber);
|
||||
|
||||
// look for attributes
|
||||
if (ber.readSequence() === (Ber.Sequence | Ber.Constructor)) {
|
||||
var end = ber.offset + ber.length;
|
||||
while (ber.offset < end)
|
||||
this.attributes.push(ber.readString().toLowerCase());
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
SearchRequest.prototype._toBer = function(ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
ber.writeString(this.baseObject.toString());
|
||||
ber.writeEnumeration(this._scope);
|
||||
ber.writeEnumeration(this.derefAliases);
|
||||
ber.writeInt(this.sizeLimit);
|
||||
ber.writeInt(this.timeLimit);
|
||||
ber.writeBoolean(this.typesOnly);
|
||||
|
||||
var f = this.filter || new filters.PresenceFilter({attribute: 'objectclass'});
|
||||
ber = f.toBer(ber);
|
||||
|
||||
if (this.attributes && this.attributes.length) {
|
||||
ber.startSequence(Ber.Sequence | Ber.Constructor);
|
||||
this.attributes.forEach(function(a) {
|
||||
ber.writeString(a);
|
||||
});
|
||||
ber.endSequence();
|
||||
}
|
||||
|
||||
return ber;
|
||||
};
|
||||
|
||||
|
||||
SearchRequest.prototype._json = function(j) {
|
||||
assert.ok(j);
|
||||
|
||||
j.baseObject = this.baseObject;
|
||||
j.scope = this.scope;
|
||||
j.derefAliases = this.derefAliases;
|
||||
j.sizeLimit = this.sizeLimit;
|
||||
j.timeLimit = this.timeLimit;
|
||||
j.typesOnly = this.typesOnly;
|
||||
j.filter = this.filter.toString();
|
||||
j.attributes = this.attributes;
|
||||
|
||||
return j;
|
||||
};
|
|
@ -0,0 +1,60 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var assert = require('assert');
|
||||
var util = require('util');
|
||||
|
||||
var LDAPResult = require('./result');
|
||||
var SearchEntry = require('./search_entry');
|
||||
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
function SearchResponse(options) {
|
||||
if (!options)
|
||||
options = {};
|
||||
if (typeof(options) !== 'object')
|
||||
throw new TypeError('options must be an object');
|
||||
|
||||
options.protocolOp = Protocol.LDAP_REP_SEARCH;
|
||||
LDAPResult.call(this, options);
|
||||
}
|
||||
util.inherits(SearchResponse, LDAPResult);
|
||||
module.exports = SearchResponse;
|
||||
|
||||
|
||||
/**
|
||||
* Allows you to send a SearchEntry back to the client.
|
||||
*
|
||||
* @param {Object} entry an instance of SearchEntry.
|
||||
*/
|
||||
SearchResponse.prototype.send = function(entry) {
|
||||
if (!entry || !(entry instanceof SearchEntry))
|
||||
throw new TypeError('entry (SearchEntry) required');
|
||||
if (entry.messageID !== this.messageID)
|
||||
throw new Error('SearchEntry messageID mismatch');
|
||||
|
||||
assert.ok(this.connection);
|
||||
|
||||
if (this.log.isDebugEnabled())
|
||||
this.log.debug('%s: sending: %j', this.connection.ldap.id, entry.json);
|
||||
|
||||
this.connection.write(entry.toBer());
|
||||
};
|
||||
|
||||
|
||||
SearchResponse.prototype.createSearchEntry = function(options) {
|
||||
if (options) {
|
||||
if (typeof(options) !== 'object')
|
||||
throw new TypeError('options must be an object');
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
|
||||
options.messageID = this.messageID;
|
||||
options.log4js = this.log4js;
|
||||
|
||||
return new SearchEntry(options);
|
||||
};
|
|
@ -0,0 +1,83 @@
|
|||
// 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 LDAPResult = require('./result');
|
||||
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var Ber = asn1.Ber;
|
||||
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
function UnbindRequest(options) {
|
||||
if (options) {
|
||||
if (typeof(options) !== 'object')
|
||||
throw new TypeError('options must be an object');
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
|
||||
options.protocolOp = Protocol.LDAP_REQ_UNBIND;
|
||||
LDAPMessage.call(this, options);
|
||||
|
||||
this.__defineGetter__('type', function() { return 'UnbindRequest'; });
|
||||
this.__defineGetter__('_dn', function() { return ''; });
|
||||
}
|
||||
util.inherits(UnbindRequest, LDAPMessage);
|
||||
module.exports = UnbindRequest;
|
||||
|
||||
|
||||
UnbindRequest.prototype.newResult = function() {
|
||||
var self = this;
|
||||
|
||||
// This one is special, so just hack up the result object
|
||||
function UnbindResponse(options) {
|
||||
LDAPMessage.call(this, options);
|
||||
this.__defineGetter__('type', function() { return 'UnbindResponse'; });
|
||||
}
|
||||
util.inherits(UnbindResponse, LDAPMessage);
|
||||
UnbindResponse.prototype.end = function(status) {
|
||||
if (this.log.isTraceEnabled())
|
||||
log.trace('%s: unbinding!', this.connection.ldap.id);
|
||||
this.connection.end();
|
||||
};
|
||||
UnbindResponse.prototype._json = function(j) { return j; };
|
||||
|
||||
return new UnbindResponse({
|
||||
messageID: 0,
|
||||
protocolOp: 0,
|
||||
status: 0 // Success
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
UnbindRequest.prototype._parse = function(ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
UnbindRequest.prototype._toBer = function(ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
return ber;
|
||||
};
|
||||
|
||||
|
||||
UnbindRequest.prototype._json = function(j) {
|
||||
assert.ok(j);
|
||||
|
||||
return j;
|
||||
};
|
|
@ -0,0 +1,46 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var assert = require('assert');
|
||||
var util = require('util');
|
||||
|
||||
var LDAPMessage = require('./result');
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
|
||||
///--- API
|
||||
// Ok, so there's really no such thing as an unbind 'response', but to make
|
||||
// the framework not suck, I just made this up, and have it stubbed so it's
|
||||
// not such a one-off.
|
||||
|
||||
function UnbindResponse(options) {
|
||||
if (!options)
|
||||
options = {};
|
||||
if (typeof(options) !== 'object')
|
||||
throw new TypeError('options must be an object');
|
||||
|
||||
options.protocolOp = 0;
|
||||
LDAPMessage.call(this, options);
|
||||
this.__defineGetter__('type', function() { return 'UnbindResponse'; });
|
||||
}
|
||||
util.inherits(UnbindResponse, LDAPMessage);
|
||||
module.exports = UnbindResponse;
|
||||
|
||||
|
||||
/**
|
||||
* Special override that just ends the connection, if present.
|
||||
*
|
||||
* @param {Number} status completely ignored.
|
||||
*/
|
||||
UnbindResponse.prototype.end = function(status) {
|
||||
assert.ok(this.connection);
|
||||
|
||||
if (this.log.isTraceEnabled())
|
||||
this.log.trace('%s: unbinding!', this.connection.ldap.id);
|
||||
|
||||
this.connection.end();
|
||||
};
|
||||
|
||||
|
||||
UnbindResponse.prototype._json = function(j) {
|
||||
return j;
|
||||
};
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
|
||||
module.exports = {
|
||||
|
||||
// Misc
|
||||
LDAP_VERSION_3: 0x03,
|
||||
LBER_SET: 0x31,
|
||||
LDAP_CONTROLS: 0xa0,
|
||||
|
||||
// Search
|
||||
SCOPE_BASE_OBJECT: 0,
|
||||
SCOPE_ONE_LEVEL: 1,
|
||||
SCOPE_SUBTREE: 2,
|
||||
|
||||
NEVER_DEREF_ALIASES: 0,
|
||||
DEREF_IN_SEARCHING: 1,
|
||||
DEREF_BASE_OBJECT: 2,
|
||||
DEREF_ALWAYS: 3,
|
||||
|
||||
FILTER_AND: 0xa0,
|
||||
FILTER_OR: 0xa1,
|
||||
FILTER_NOT: 0xa2,
|
||||
FILTER_EQUALITY: 0xa3,
|
||||
FILTER_SUBSTRINGS: 0xa4,
|
||||
FILTER_GE: 0xa5,
|
||||
FILTER_LE: 0xa6,
|
||||
FILTER_PRESENT: 0x87,
|
||||
FILTER_APPROX: 0xa8,
|
||||
FILTER_EXT: 0xa9,
|
||||
|
||||
// Protocol Operations
|
||||
LDAP_REQ_BIND: 0x60,
|
||||
LDAP_REQ_UNBIND: 0x42,
|
||||
LDAP_REQ_SEARCH: 0x63,
|
||||
LDAP_REQ_MODIFY: 0x66,
|
||||
LDAP_REQ_ADD: 0x68,
|
||||
LDAP_REQ_DELETE: 0x4a,
|
||||
LDAP_REQ_MODRDN: 0x6c,
|
||||
LDAP_REQ_COMPARE: 0x6e,
|
||||
LDAP_REQ_ABANDON: 0x50,
|
||||
LDAP_REQ_EXTENSION: 0x77,
|
||||
|
||||
LDAP_REP_BIND: 0x61,
|
||||
LDAP_REP_SEARCH_ENTRY: 0x64,
|
||||
LDAP_REP_SEARCH_REF: 0x73,
|
||||
LDAP_REP_SEARCH: 0x65,
|
||||
LDAP_REP_MODIFY: 0x67,
|
||||
LDAP_REP_ADD: 0x69,
|
||||
LDAP_REP_DELETE: 0x6b,
|
||||
LDAP_REP_MODRDN: 0x6d,
|
||||
LDAP_REP_COMPARE: 0x6f,
|
||||
LDAP_REP_EXTENSION: 0x78
|
||||
};
|
|
@ -0,0 +1,509 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var assert = require('assert');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var net = require('net');
|
||||
var tls = require('tls');
|
||||
var util = require('util');
|
||||
|
||||
var asn1 = require('asn1');
|
||||
var sprintf = require('sprintf').sprintf;
|
||||
|
||||
var dn = require('./dn');
|
||||
var errors = require('./errors');
|
||||
var Protocol = require('./protocol');
|
||||
var logStub = require('./log_stub');
|
||||
|
||||
var Parser = require('./messages').Parser;
|
||||
var AddResponse = require('./messages/add_response');
|
||||
var BindResponse = require('./messages/bind_response');
|
||||
var CompareResponse = require('./messages/compare_response');
|
||||
var DeleteResponse = require('./messages/del_response');
|
||||
var ExtendedResponse = require('./messages/ext_response');
|
||||
var ModifyResponse = require('./messages/modify_response');
|
||||
var ModifyDNResponse = require('./messages/moddn_response');
|
||||
var SearchResponse = require('./messages/search_response');
|
||||
var UnbindResponse = require('./messages/unbind_response');
|
||||
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var Ber = asn1.Ber;
|
||||
var BerReader = asn1.BerReader;
|
||||
|
||||
|
||||
|
||||
///--- Helpers
|
||||
|
||||
function setupConnection(server, c, config) {
|
||||
assert.ok(server);
|
||||
assert.ok(c);
|
||||
assert.ok(config);
|
||||
|
||||
c.ldap = {
|
||||
id: c.remoteAddress + ':' + c.remotePort,
|
||||
config: config
|
||||
};
|
||||
|
||||
c.addListener('timeout', function() {
|
||||
server.log.trace('%s timed out', c.ldap.id);
|
||||
c.destroy();
|
||||
});
|
||||
c.addListener('end', function() {
|
||||
server.log.trace('%s shutdown', c.ldap.id);
|
||||
});
|
||||
c.addListener('error', function(err) {
|
||||
server.log.warn('%s unexpected connection error', c.ldap.id, err);
|
||||
c.destroy();
|
||||
});
|
||||
c.addListener('close', function(had_err) {
|
||||
server.log.trace('%s close; had_err=%j', c.ldap.id, had_err);
|
||||
c.destroy();
|
||||
});
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
function getResponse(req) {
|
||||
assert.ok(req);
|
||||
|
||||
var Response;
|
||||
|
||||
switch (req.protocolOp) {
|
||||
case Protocol.LDAP_REQ_BIND:
|
||||
Response = BindResponse;
|
||||
break;
|
||||
case Protocol.LDAP_REQ_ABANDON:
|
||||
return; // Noop
|
||||
case Protocol.LDAP_REQ_ADD:
|
||||
Response = AddResponse;
|
||||
break;
|
||||
case Protocol.LDAP_REQ_COMPARE:
|
||||
Response = CompareResponse;
|
||||
break;
|
||||
case Protocol.LDAP_REQ_DELETE:
|
||||
Response = DeleteResponse;
|
||||
break;
|
||||
case Protocol.LDAP_REQ_EXTENSION:
|
||||
Response = ExtendedResponse;
|
||||
break;
|
||||
case Protocol.LDAP_REQ_MODIFY:
|
||||
Response = ModifyResponse;
|
||||
break;
|
||||
case Protocol.LDAP_REQ_MODRDN:
|
||||
Response = ModifyDNResponse;
|
||||
break;
|
||||
case Protocol.LDAP_REQ_SEARCH:
|
||||
Response = SearchResponse;
|
||||
break;
|
||||
case Protocol.LDAP_REQ_UNBIND:
|
||||
Response = UnbindResponse;
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
assert.ok(Response);
|
||||
|
||||
var res = new Response({
|
||||
messageID: req.messageID,
|
||||
log4js: req.log4js
|
||||
});
|
||||
res.connection = req.connection;
|
||||
res.logId = req.logId;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
function defaultHandler(req, res, next) {
|
||||
assert.ok(req);
|
||||
assert.ok(res);
|
||||
assert.ok(next);
|
||||
|
||||
res.matchedDN = req.dn.toString();
|
||||
res.errorMessage = 'Server method not implemented';
|
||||
res.end(errors.LDAP_OTHER);
|
||||
return next();
|
||||
}
|
||||
|
||||
|
||||
function noSuffixHandler(req, res, next) {
|
||||
assert.ok(req);
|
||||
assert.ok(res);
|
||||
assert.ok(next);
|
||||
|
||||
res.errorMessage = 'No tree found for: ' + req.dn.toString();
|
||||
res.end(errors.LDAP_NO_SUCH_OBJECT);
|
||||
return next();
|
||||
}
|
||||
|
||||
|
||||
function noExOpHandler(req, res, next) {
|
||||
assert.ok(req);
|
||||
assert.ok(res);
|
||||
assert.ok(next);
|
||||
|
||||
res.errorMessage = req.requestName + ' not supported';
|
||||
res.end(errors.LDAP_PROTOCOL_ERROR);
|
||||
return next();
|
||||
}
|
||||
|
||||
|
||||
function getHandlerChain(server, req) {
|
||||
assert.ok(server);
|
||||
assert.ok(req);
|
||||
|
||||
var backend;
|
||||
var handlers;
|
||||
var matched = false;
|
||||
for (var r in server.routes) {
|
||||
if (server.routes.hasOwnProperty(r)) {
|
||||
|
||||
if (req.protocolOp === Protocol.LDAP_REQ_EXTENSION) {
|
||||
if (r === req.requestName)
|
||||
matched = true;
|
||||
} else if (req.protocolOp === Protocol.LDAP_REQ_UNBIND) {
|
||||
matched = true;
|
||||
} else {
|
||||
if (req.dn) {
|
||||
if (r === req.dn.toString()) {
|
||||
matched = true;
|
||||
} else if (server.routes[r]._dn &&
|
||||
server.routes[r]._dn.parentOf(req.dn)) {
|
||||
matched = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!matched)
|
||||
continue;
|
||||
|
||||
switch (req.protocolOp) {
|
||||
case Protocol.LDAP_REQ_BIND:
|
||||
handlers = server.routes[r]._bind;
|
||||
break;
|
||||
|
||||
case Protocol.LDAP_REQ_ABANDON:
|
||||
return; // Noop
|
||||
|
||||
case Protocol.LDAP_REQ_ADD:
|
||||
handlers = server.routes[r]._add;
|
||||
break;
|
||||
|
||||
case Protocol.LDAP_REQ_COMPARE:
|
||||
handlers = server.routes[r]._compare;
|
||||
break;
|
||||
|
||||
case Protocol.LDAP_REQ_DELETE:
|
||||
handlers = server.routes[r]._del;
|
||||
break;
|
||||
|
||||
case Protocol.LDAP_REQ_EXTENSION:
|
||||
handlers = server.routes[r]._exop;
|
||||
break;
|
||||
|
||||
case Protocol.LDAP_REQ_MODIFY:
|
||||
handlers = server.routes[r]._modify;
|
||||
break;
|
||||
|
||||
case Protocol.LDAP_REQ_MODRDN:
|
||||
handlers = server.routes[r]._modifyDN;
|
||||
break;
|
||||
|
||||
case Protocol.LDAP_REQ_SEARCH:
|
||||
handlers = server.routes[r]._search;
|
||||
break;
|
||||
|
||||
case Protocol.LDAP_REQ_UNBIND:
|
||||
if (server.routes['unbind'])
|
||||
handlers = server.routes['unbind']._unbind;
|
||||
break;
|
||||
|
||||
default:
|
||||
server.log.warn('Unimplemented server method: %s', req.type);
|
||||
return c.destroy();
|
||||
}
|
||||
}
|
||||
if (handlers) {
|
||||
backend = server.routes[r]._backend;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!handlers) {
|
||||
backend = server;
|
||||
if (matched) {
|
||||
server.log.warn('No handler registered for %s:%s, running default',
|
||||
req.type, req.dn.toString());
|
||||
handlers = [defaultHandler];
|
||||
} else {
|
||||
server.log.trace('%s does not map to a known suffix/oid',
|
||||
req.dn.toString());
|
||||
handlers = [req.protocolOp !== Protocol.LDAP_REQ_EXTENSION ?
|
||||
noSuffixHandler : noExOpHandler];
|
||||
}
|
||||
}
|
||||
|
||||
assert.ok(backend);
|
||||
assert.ok(handlers);
|
||||
assert.ok(handlers instanceof Array);
|
||||
assert.ok(handlers.length);
|
||||
|
||||
return {
|
||||
backend: backend,
|
||||
handlers: handlers
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
function addHandlers(server) {
|
||||
assert.ok(server);
|
||||
assert.ok(server.log);
|
||||
|
||||
var log = server.log;
|
||||
|
||||
var ops = [ // We don't support abandon.
|
||||
'add',
|
||||
'bind',
|
||||
'compare',
|
||||
'del',
|
||||
'exop',
|
||||
'modify',
|
||||
'modifyDN',
|
||||
'search',
|
||||
'unbind'
|
||||
];
|
||||
|
||||
function processHandlerChain(chain) {
|
||||
if (!chain)
|
||||
return [defaultHandler];
|
||||
|
||||
if (chain instanceof Array) {
|
||||
if (!chain.length)
|
||||
return [defaultHandler];
|
||||
|
||||
chain.forEach(function(f) {
|
||||
if (typeof(f) !== 'function')
|
||||
throw new TypeError('[function(req, res, next)] required');
|
||||
});
|
||||
|
||||
return chain;
|
||||
} else if (typeof(chain) === 'function') {
|
||||
return [chain];
|
||||
}
|
||||
|
||||
throw new TypeError('[function(req, res, next)] required');
|
||||
}
|
||||
|
||||
server.routes = {};
|
||||
|
||||
ops.forEach(function(o) {
|
||||
var op = '_' + o;
|
||||
server[o] = function(name, handler) {
|
||||
if (o === 'unbind') {
|
||||
if (typeof(name === 'function')) {
|
||||
handler = name;
|
||||
name = 'unbind';
|
||||
}
|
||||
}
|
||||
if (!name || typeof(name) !== 'string')
|
||||
throw new TypeError('name (string) required');
|
||||
if (!handler || typeof(handler) !== 'function')
|
||||
throw new TypeError('[function(req, res, next)] required');
|
||||
|
||||
// Do this first so it will throw
|
||||
var _dn = null;
|
||||
if (o !== 'exop' && o !== 'unbind') {
|
||||
_dn = dn.parse(name);
|
||||
name = _dn.toString();
|
||||
}
|
||||
|
||||
if (!server.routes[name])
|
||||
server.routes[name] = {};
|
||||
if (!server.routes[name]._backend)
|
||||
server.routes[name]._backend = server;
|
||||
|
||||
server.routes[name][op] = processHandlerChain(handler);
|
||||
server.routes[name]._dn = _dn;
|
||||
if (log.isTraceEnabled()) {
|
||||
var _names = [];
|
||||
server.routes[name][op].forEach(function(f) {
|
||||
_names.push(f.name || 'Anonymous Function');
|
||||
});
|
||||
log.trace('%s(%s) -> %s', o, name, _names);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
server.mount = function(name, backend) {
|
||||
if (!name || typeof(name) !== 'string')
|
||||
throw new TypeError('name (string) required');
|
||||
if (!backend || typeof(backend) !== 'object')
|
||||
throw new TypeError('backend (object) required');
|
||||
if (!backend.name)
|
||||
throw new TypeError('backend is not a valid LDAP Backend');
|
||||
if (!backend.register || typeof(backend.register) !== 'function')
|
||||
throw new TypeError('backend is not a valid LDAP Backend');
|
||||
|
||||
var _dn = null;
|
||||
// Do this first so it will throw
|
||||
_dn = dn.parse(name);
|
||||
name = _dn.toString();
|
||||
|
||||
ops.forEach(function(o) {
|
||||
if (o === 'exop' || o === 'unbind')
|
||||
return;
|
||||
|
||||
var op = '_' + o;
|
||||
|
||||
if (!server.routes[name])
|
||||
server.routes[name] = {};
|
||||
if (!server.routes[name]._backend)
|
||||
server.routes[name]._backend = backend;
|
||||
|
||||
server.routes[name][op] = processHandlerChain(backend.register(o));
|
||||
if (log.isTraceEnabled()) {
|
||||
var _names = [];
|
||||
server.routes[name][op].forEach(function(f) { _names.push(f.name); });
|
||||
log.trace('%s(%s) -> %s', o, name, _names);
|
||||
}
|
||||
});
|
||||
|
||||
server.routes[name]._dn = _dn;
|
||||
|
||||
log.info('%s mounted at %s', server.routes[name]._backend.toString(), dn);
|
||||
|
||||
return server;
|
||||
};
|
||||
|
||||
return server;
|
||||
}
|
||||
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
module.exports = {
|
||||
|
||||
createServer: function(options) {
|
||||
if (options) {
|
||||
if (typeof(options) !== 'object')
|
||||
throw new TypeError('options (object) required');
|
||||
if (options.log4js && typeof(options.log4js) !== 'object')
|
||||
throw new TypeError('options.log4s must be an object');
|
||||
|
||||
if (options.certificate || options.key) {
|
||||
if (!(options.certificate && options.key) ||
|
||||
typeof(options.certificate) !== 'string' ||
|
||||
typeof(options.key) !== 'string') {
|
||||
throw new TypeError('options.certificate and options.key (string) ' +
|
||||
'are both required for TLS');
|
||||
}
|
||||
}
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
|
||||
var server;
|
||||
|
||||
function newConnection(c) {
|
||||
assert.ok(c);
|
||||
|
||||
if (c.type === 'unix' && server.type === 'unix') {
|
||||
c.remoteAddress = server.path;
|
||||
c.remotePort = c.fd;
|
||||
}
|
||||
|
||||
assert.ok(c.remoteAddress);
|
||||
assert.ok(c.remotePort);
|
||||
|
||||
setupConnection(server, c, options);
|
||||
if (server.log.isTraceEnabled())
|
||||
server.log.trace('new connection from %s', c.ldap.id);
|
||||
|
||||
c.parser = new Parser({
|
||||
log4js: server.log4js
|
||||
});
|
||||
c.parser.on('message', function(req) {
|
||||
assert.ok(req);
|
||||
|
||||
req.connection = c;
|
||||
req.logId = c.remoteAddress + '::' + req.messageID;
|
||||
|
||||
if (server.log.isDebugEnabled())
|
||||
server.log.debug('%s: message received: req=%j', c.ldap.id, req.json);
|
||||
|
||||
var res = getResponse(req);
|
||||
if (!res) {
|
||||
server.log.warn('Unimplemented server method: %s', req.type);
|
||||
c.destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
var chain = getHandlerChain(server, req);
|
||||
|
||||
var i = 0;
|
||||
return function(err) {
|
||||
if (err) {
|
||||
res.status = err.code || errors.LDAP_OPERATIONS_ERROR;
|
||||
res.matchedDN = err.dn ? err.dn.toString() : req.dn.toString();
|
||||
res.errorMessage = err.message || '';
|
||||
return res.end();
|
||||
}
|
||||
|
||||
var next = arguments.callee;
|
||||
if (chain.handlers[i])
|
||||
return chain.handlers[i++].call(chain.backend, req, res, next);
|
||||
}();
|
||||
});
|
||||
|
||||
c.parser.on('protocolError', function(err, messageID) {
|
||||
server.log.warn('%s sent invalid protocol message', c.ldap.id, err);
|
||||
// TODO (mcavage) deal with this
|
||||
// send an unsolicited notification
|
||||
c.destroy();
|
||||
});
|
||||
c.parser.on('error', function(err) {
|
||||
server.log.error('Exception happened parsing for %s: %s',
|
||||
c.ldap.id, err.stack);
|
||||
c.destroy();
|
||||
});
|
||||
c.on('data', function(data) {
|
||||
assert.ok(data);
|
||||
if (server.log.isTraceEnabled())
|
||||
server.log.trace('data on %s: %s', c.ldap.id, util.inspect(data));
|
||||
c.parser.write(data);
|
||||
});
|
||||
|
||||
}; // end newConnection
|
||||
|
||||
var secure = options.certificate && options.key;
|
||||
|
||||
if (secure) {
|
||||
server = tls.createServer(options, newConnection);
|
||||
} else {
|
||||
server = net.createServer(newConnection);
|
||||
}
|
||||
|
||||
server.log4js = options.log4js || logStub;
|
||||
|
||||
server.ldap = {
|
||||
config: options
|
||||
};
|
||||
|
||||
server.__defineGetter__('log', function() {
|
||||
if (!server._log)
|
||||
server._log = server.log4js.getLogger('LDAPServer');
|
||||
|
||||
return server._log;
|
||||
});
|
||||
|
||||
addHandlers(server);
|
||||
|
||||
return server;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var querystring = require('querystring');
|
||||
var url = require('url');
|
||||
var util = require('util');
|
||||
|
||||
var dn = require('./dn');
|
||||
|
||||
|
||||
|
||||
module.exports = {
|
||||
|
||||
parse: function(urlStr, parseDN) {
|
||||
var u = url.parse(urlStr);
|
||||
if (!u.protocol || !(u.protocol === 'ldap:' || u.protocol === 'ldaps:'))
|
||||
throw new TypeError(urlStr + ' is an invalid LDAP url (protocol)');
|
||||
|
||||
if (!u.port) {
|
||||
u.port = 389;
|
||||
} else {
|
||||
u.port = parseInt(u.port, 10);
|
||||
}
|
||||
|
||||
if (!u.hostname)
|
||||
u.hostname = 'localhost';
|
||||
|
||||
u.secure = (u.protocol === 'ldaps:');
|
||||
|
||||
if (u.pathname) {
|
||||
u.pathname = querystring.unescape(u.pathname.substr(1));
|
||||
u.DN = parseDN ? dn.parse(u.pathname) : u.pathname;
|
||||
}
|
||||
|
||||
if (u.search) {
|
||||
u.attributes = [];
|
||||
var tmp = u.search.substr(1).split('?');
|
||||
if (tmp && tmp.length) {
|
||||
if (tmp[0]) {
|
||||
tmp[0].split(',').forEach(function(a) {
|
||||
u.attributes.push(querystring.unescape(a.trim()));
|
||||
});
|
||||
}
|
||||
}
|
||||
if (tmp[1]) {
|
||||
if (tmp[1] !== 'base' && tmp[1] !== 'one' && tmp[1] !== 'sub')
|
||||
throw new TypeError(urlStr + ' is an invalid LDAP url (scope)');
|
||||
u.scope = tmp[1];
|
||||
}
|
||||
if (tmp[2]) {
|
||||
u.filter = querystring.unescape(tmp[2]);
|
||||
}
|
||||
if (tmp[3]) {
|
||||
u.extensions = querystring.unescape(tmp[3]);
|
||||
}
|
||||
if (!u.scope)
|
||||
u.scope = 'base';
|
||||
if (!u.filter)
|
||||
u.filter = '(objectclass=*)';
|
||||
}
|
||||
|
||||
return u;
|
||||
}
|
||||
|
||||
};
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"author": "Mark Cavage <mcavage@gmail.com>",
|
||||
"name": "ldapjs",
|
||||
"description": "LDAP client and server APIs",
|
||||
"version": "0.0.1",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/mcavage/node-ldapjs.git"
|
||||
},
|
||||
"main": "lib/index.js",
|
||||
"engines": {
|
||||
"node": ">=0.4.10"
|
||||
},
|
||||
"dependencies": {
|
||||
"asn1": "~0.1.5",
|
||||
"buffertools": "~1.0.3",
|
||||
"sprintf": "~0.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"tap": "~0.0.9",
|
||||
"node-uuid": "~1.2.0"
|
||||
},
|
||||
"scripts": {
|
||||
"pretest": "which gjslint; if [[ \"$?\" = 0 ]] ; then gjslint --nojsdoc -r lib -r tst; else echo \"Missing gjslint. Skipping lint\"; fi",
|
||||
"test": "./node_modules/.bin/tap ./tst"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var test = require('tap').test;
|
||||
|
||||
var asn1 = require('asn1');
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var BerReader = asn1.BerReader;
|
||||
var BerWriter = asn1.BerWriter;
|
||||
var Attribute;
|
||||
|
||||
|
||||
///--- Tests
|
||||
|
||||
test('load library', function(t) {
|
||||
Attribute = require('../lib/index').Attribute;
|
||||
t.ok(Attribute);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('new no args', function(t) {
|
||||
t.ok(new Attribute());
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('new with args', function(t) {
|
||||
var attr = new Attribute({
|
||||
type: 'cn',
|
||||
vals: ['foo', 'bar']
|
||||
});
|
||||
t.ok(attr);
|
||||
attr.addValue('baz');
|
||||
t.equal(attr.type, 'cn');
|
||||
t.equal(attr.vals.length, 3);
|
||||
t.equal(attr.vals[0], 'foo');
|
||||
t.equal(attr.vals[1], 'bar');
|
||||
t.equal(attr.vals[2], 'baz');
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('toBer', function(t) {
|
||||
var attr = new Attribute({
|
||||
type: 'cn',
|
||||
vals: ['foo', 'bar']
|
||||
});
|
||||
t.ok(attr);
|
||||
var ber = new BerWriter();
|
||||
attr.toBer(ber);
|
||||
var reader = new BerReader(ber.buffer);
|
||||
t.ok(reader.readSequence());
|
||||
t.equal(reader.readString(), 'cn');
|
||||
t.equal(reader.readSequence(), 0x31); // lber set
|
||||
t.equal(reader.readString(), 'foo');
|
||||
t.equal(reader.readString(), 'bar');
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('parse', function(t) {
|
||||
var ber = new BerWriter();
|
||||
ber.startSequence();
|
||||
ber.writeString('cn');
|
||||
ber.startSequence(0x31);
|
||||
ber.writeStringArray(['foo', 'bar']);
|
||||
ber.endSequence();
|
||||
ber.endSequence();
|
||||
|
||||
var attr = new Attribute();
|
||||
t.ok(attr);
|
||||
t.ok(attr.parse(new BerReader(ber.buffer)));
|
||||
|
||||
t.equal(attr.type, 'cn');
|
||||
t.equal(attr.vals.length, 2);
|
||||
t.equal(attr.vals[0], 'foo');
|
||||
t.equal(attr.vals[1], 'bar');
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,100 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var test = require('tap').test;
|
||||
|
||||
var asn1 = require('asn1');
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var BerReader = asn1.BerReader;
|
||||
var BerWriter = asn1.BerWriter;
|
||||
var Attribute;
|
||||
var Change;
|
||||
|
||||
|
||||
///--- Tests
|
||||
|
||||
test('load library', function(t) {
|
||||
Attribute = require('../lib/index').Attribute;
|
||||
Change = require('../lib/index').Change;
|
||||
t.ok(Attribute);
|
||||
t.ok(Change);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('new no args', function(t) {
|
||||
t.ok(new Change());
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('new with args', function(t) {
|
||||
var change = new Change({
|
||||
operation: 0x00,
|
||||
modification: new Attribute({
|
||||
type: 'cn',
|
||||
vals: ['foo', 'bar']
|
||||
})
|
||||
});
|
||||
t.ok(change);
|
||||
|
||||
t.equal(change.operation, 'Add');
|
||||
t.equal(change.modification.type, 'cn');
|
||||
t.equal(change.modification.vals.length, 2);
|
||||
t.equal(change.modification.vals[0], 'foo');
|
||||
t.equal(change.modification.vals[1], 'bar');
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('toBer', function(t) {
|
||||
var change = new Change({
|
||||
operation: 'Add',
|
||||
modification: new Attribute({
|
||||
type: 'cn',
|
||||
vals: ['foo', 'bar']
|
||||
})
|
||||
});
|
||||
t.ok(change);
|
||||
|
||||
var ber = new BerWriter();
|
||||
change.toBer(ber);
|
||||
var reader = new BerReader(ber.buffer);
|
||||
t.ok(reader.readSequence());
|
||||
t.equal(reader.readEnumeration(), 0x00);
|
||||
t.ok(reader.readSequence());
|
||||
t.equal(reader.readString(), 'cn');
|
||||
t.equal(reader.readSequence(), 0x31); // lber set
|
||||
t.equal(reader.readString(), 'foo');
|
||||
t.equal(reader.readString(), 'bar');
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('parse', function(t) {
|
||||
var ber = new BerWriter();
|
||||
ber.startSequence();
|
||||
ber.writeEnumeration(0x00);
|
||||
ber.startSequence();
|
||||
ber.writeString('cn');
|
||||
ber.startSequence(0x31);
|
||||
ber.writeStringArray(['foo', 'bar']);
|
||||
ber.endSequence();
|
||||
ber.endSequence();
|
||||
ber.endSequence();
|
||||
|
||||
var change = new Change();
|
||||
t.ok(change);
|
||||
t.ok(change.parse(new BerReader(ber.buffer)));
|
||||
|
||||
t.equal(change.operation, 'Add');
|
||||
t.equal(change.modification.type, 'cn');
|
||||
t.equal(change.modification.vals.length, 2);
|
||||
t.equal(change.modification.vals[0], 'foo');
|
||||
t.equal(change.modification.vals[1], 'bar');
|
||||
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,320 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var test = require('tap').test;
|
||||
var uuid = require('node-uuid');
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var BIND_DN = 'cn=root';
|
||||
var BIND_PW = 'secret';
|
||||
var SOCKET = '/tmp/.' + uuid();
|
||||
|
||||
var SUFFIX = 'dc=test';
|
||||
|
||||
var ldap;
|
||||
var Attribute;
|
||||
var Change;
|
||||
var client;
|
||||
var server;
|
||||
|
||||
|
||||
///--- Tests
|
||||
|
||||
test('setup', function(t) {
|
||||
ldap = require('../lib/index');
|
||||
t.ok(ldap);
|
||||
t.ok(ldap.createClient);
|
||||
t.ok(ldap.createServer);
|
||||
t.ok(ldap.Attribute);
|
||||
t.ok(ldap.Change);
|
||||
|
||||
Attribute = ldap.Attribute;
|
||||
Change = ldap.Change;
|
||||
|
||||
server = ldap.createServer();
|
||||
t.ok(server);
|
||||
|
||||
server.bind(BIND_DN, function(req, res, next) {
|
||||
if (req.credentials !== BIND_PW)
|
||||
return next(new ldap.InvalidCredentialsError('Invalid password'));
|
||||
|
||||
res.end();
|
||||
return next();
|
||||
});
|
||||
|
||||
server.add(SUFFIX, function(req, res, next) {
|
||||
res.end();
|
||||
return next();
|
||||
});
|
||||
|
||||
server.compare(SUFFIX, function(req, res, next) {
|
||||
if (req.value !== 'test')
|
||||
return next(new ldap.CompareFalseError('value was test'));
|
||||
|
||||
res.end(ldap.LDAP_COMPARE_TRUE);
|
||||
return next();
|
||||
});
|
||||
|
||||
server.del(SUFFIX, function(req, res, next) {
|
||||
res.end();
|
||||
return next();
|
||||
});
|
||||
|
||||
// LDAP whoami
|
||||
server.exop('1.3.6.1.4.1.4203.1.11.3', function(req, res, next) {
|
||||
res.value = 'u:xxyyz@EXAMPLE.NET';
|
||||
res.end();
|
||||
return next();
|
||||
});
|
||||
|
||||
server.modify(SUFFIX, function(req, res, next) {
|
||||
res.end();
|
||||
return next();
|
||||
});
|
||||
|
||||
server.modifyDN(SUFFIX, function(req, res, next) {
|
||||
res.end();
|
||||
return next();
|
||||
});
|
||||
|
||||
server.search(SUFFIX, function(req, res, next) {
|
||||
var e = res.createSearchEntry({
|
||||
objectName: req.dn,
|
||||
attributes: [
|
||||
new Attribute({
|
||||
type: 'cn',
|
||||
vals: ['test']
|
||||
})
|
||||
]
|
||||
});
|
||||
res.send(e);
|
||||
res.send(e);
|
||||
res.end();
|
||||
return next();
|
||||
});
|
||||
|
||||
|
||||
server.unbind(function(req, res, next) {
|
||||
res.end();
|
||||
return next();
|
||||
});
|
||||
|
||||
server.listen(SOCKET, function() {
|
||||
client = ldap.createClient({
|
||||
socketPath: SOCKET
|
||||
});
|
||||
t.ok(client);
|
||||
// client.log4js.setLevel('Debug');
|
||||
t.end();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
test('simple bind success', function(t) {
|
||||
client.bind(BIND_DN, BIND_PW, function(err, res) {
|
||||
t.ifError(err);
|
||||
t.ok(res);
|
||||
t.equal(res.status, 0);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
test('simple bind failure', function(t) {
|
||||
client.bind(BIND_DN, uuid(), function(err, res) {
|
||||
t.ok(err);
|
||||
t.notOk(res);
|
||||
|
||||
t.ok(err instanceof ldap.InvalidCredentialsError);
|
||||
t.ok(err instanceof Error);
|
||||
t.ok(err.dn);
|
||||
t.ok(err.message);
|
||||
t.ok(err.stack);
|
||||
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
test('add success', function(t) {
|
||||
var attrs = [
|
||||
new Attribute({
|
||||
type: 'cn',
|
||||
vals: ['test']
|
||||
})
|
||||
];
|
||||
client.add('cn=add, ' + SUFFIX, attrs, function(err, res) {
|
||||
t.ifError(err);
|
||||
t.ok(res);
|
||||
t.equal(res.status, 0);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
test('compare success', function(t) {
|
||||
client.compare('cn=compare, ' + SUFFIX, 'cn', 'test', function(err,
|
||||
matched,
|
||||
res) {
|
||||
t.ifError(err);
|
||||
t.ok(matched);
|
||||
t.ok(res);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
test('compare false', function(t) {
|
||||
client.compare('cn=compare, ' + SUFFIX, 'cn', 'foo', function(err,
|
||||
matched,
|
||||
res) {
|
||||
t.ifError(err);
|
||||
t.notOk(matched);
|
||||
t.ok(res);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
test('compare bad suffix', function(t) {
|
||||
client.compare('cn=' + uuid(), 'cn', 'foo', function(err,
|
||||
matched,
|
||||
res) {
|
||||
t.ok(err);
|
||||
t.ok(err instanceof ldap.NoSuchObjectError);
|
||||
t.notOk(matched);
|
||||
t.notOk(res);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
test('delete success', function(t) {
|
||||
client.del('cn=delete, ' + SUFFIX, function(err, res) {
|
||||
t.ifError(err);
|
||||
t.ok(res);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
test('exop success', function(t) {
|
||||
client.exop('1.3.6.1.4.1.4203.1.11.3', function(err, value, res) {
|
||||
t.ifError(err);
|
||||
t.ok(value);
|
||||
t.ok(res);
|
||||
t.equal(value, 'u:xxyyz@EXAMPLE.NET');
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
test('exop invalid', function(t) {
|
||||
client.exop('1.2.3.4', function(err, res) {
|
||||
t.ok(err);
|
||||
t.ok(err instanceof ldap.ProtocolError);
|
||||
t.notOk(res);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
test('modify success', function(t) {
|
||||
var change = new Change({
|
||||
type: 'Replace',
|
||||
modification: new Attribute({
|
||||
type: 'cn',
|
||||
vals: ['test']
|
||||
})
|
||||
});
|
||||
client.modify('cn=modify, ' + SUFFIX, change, function(err, res) {
|
||||
t.ifError(err);
|
||||
t.ok(res);
|
||||
t.equal(res.status, 0);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
test('modify array success', function(t) {
|
||||
var changes = [
|
||||
new Change({
|
||||
operation: 'Replace',
|
||||
modification: new Attribute({
|
||||
type: 'cn',
|
||||
vals: ['test']
|
||||
})
|
||||
}),
|
||||
new Change({
|
||||
operation: 'Delete',
|
||||
modification: new Attribute({
|
||||
type: 'sn'
|
||||
})
|
||||
})
|
||||
];
|
||||
client.modify('cn=modify, ' + SUFFIX, changes, function(err, res) {
|
||||
t.ifError(err);
|
||||
t.ok(res);
|
||||
t.equal(res.status, 0);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
test('modify DN new RDN only', function(t) {
|
||||
client.modifyDN('cn=old, ' + SUFFIX, 'cn=new', function(err, res) {
|
||||
t.ifError(err);
|
||||
t.ok(res);
|
||||
t.equal(res.status, 0);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
test('modify DN new superior', function(t) {
|
||||
client.modifyDN('cn=old, ' + SUFFIX, 'cn=new, dc=foo', function(err, res) {
|
||||
t.ifError(err);
|
||||
t.ok(res);
|
||||
t.equal(res.status, 0);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
test('search basic', function(t) {
|
||||
client.search('cn=test, ' + SUFFIX, '(objectclass=*)', function(err, res) {
|
||||
t.ifError(err);
|
||||
t.ok(res);
|
||||
var gotEntry = 0;
|
||||
res.on('searchEntry', function(entry) {
|
||||
t.ok(entry);
|
||||
t.ok(entry instanceof ldap.SearchEntry);
|
||||
t.equal(entry.dn.toString(), 'cn=test, ' + SUFFIX);
|
||||
t.ok(entry.attributes);
|
||||
t.ok(entry.attributes.length);
|
||||
gotEntry++;
|
||||
});
|
||||
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, 2);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
test('shutdown', function(t) {
|
||||
client.unbind(function() {
|
||||
server.on('close', function() {
|
||||
t.end();
|
||||
});
|
||||
server.close();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,59 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var test = require('tap').test;
|
||||
|
||||
var asn1 = require('asn1');
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var BerReader = asn1.BerReader;
|
||||
var BerWriter = asn1.BerWriter;
|
||||
var Control;
|
||||
|
||||
|
||||
///--- Tests
|
||||
|
||||
test('load library', function(t) {
|
||||
Control = require('../lib/index').Control;
|
||||
t.ok(Control);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('new no args', function(t) {
|
||||
t.ok(new Control());
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('new with args', function(t) {
|
||||
var c = new Control({
|
||||
type: '2.16.840.1.113730.3.4.2',
|
||||
criticality: true
|
||||
});
|
||||
t.ok(c);
|
||||
t.equal(c.type, '2.16.840.1.113730.3.4.2');
|
||||
t.ok(c.criticality);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('parse', function(t) {
|
||||
var ber = new BerWriter();
|
||||
ber.startSequence();
|
||||
ber.writeString('2.16.840.1.113730.3.4.2');
|
||||
ber.writeBoolean(true);
|
||||
ber.writeString('foo');
|
||||
ber.endSequence();
|
||||
|
||||
var c = new Control();
|
||||
t.ok(c);
|
||||
t.ok(c.parse(new BerReader(ber.buffer)));
|
||||
|
||||
t.ok(c);
|
||||
t.equal(c.type, '2.16.840.1.113730.3.4.2');
|
||||
t.ok(c.criticality);
|
||||
t.equal(c.value, 'foo');
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,79 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var test = require('tap').test;
|
||||
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var dn;
|
||||
|
||||
|
||||
|
||||
///--- Tests
|
||||
|
||||
test('load library', function(t) {
|
||||
dn = require('../lib/index').dn;
|
||||
t.ok(dn);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('parse basic', function(t) {
|
||||
var DN_STR = 'cn=mark, ou=people, o=joyent';
|
||||
var name = dn.parse(DN_STR);
|
||||
t.ok(name);
|
||||
t.ok(name.rdns);
|
||||
t.ok(Array.isArray(name.rdns));
|
||||
t.equal(3, name.rdns.length);
|
||||
name.rdns.forEach(function(rdn) {
|
||||
t.equal('object', typeof(rdn));
|
||||
});
|
||||
t.equal(name.toString(), DN_STR);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('parse escaped', function(t) {
|
||||
var DN_STR = 'cn=m\\,ark, ou=people, o=joyent';
|
||||
var name = dn.parse(DN_STR);
|
||||
t.ok(name);
|
||||
t.ok(name.rdns);
|
||||
t.ok(Array.isArray(name.rdns));
|
||||
t.equal(3, name.rdns.length);
|
||||
name.rdns.forEach(function(rdn) {
|
||||
t.equal('object', typeof(rdn));
|
||||
});
|
||||
t.equal(name.toString(), DN_STR);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('parse compound', function(t) {
|
||||
var DN_STR = 'cn=mark+sn=cavage, ou=people, o=joyent';
|
||||
var name = dn.parse(DN_STR);
|
||||
t.ok(name);
|
||||
t.ok(name.rdns);
|
||||
t.ok(Array.isArray(name.rdns));
|
||||
t.equal(3, name.rdns.length);
|
||||
name.rdns.forEach(function(rdn) {
|
||||
t.equal('object', typeof(rdn));
|
||||
});
|
||||
t.equal(name.toString(), DN_STR);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('parse quoted', function(t) {
|
||||
var DN_STR = 'cn="mark+sn=cavage", ou=people, o=joyent';
|
||||
var name = dn.parse(DN_STR);
|
||||
t.ok(name);
|
||||
t.ok(name.rdns);
|
||||
t.ok(Array.isArray(name.rdns));
|
||||
t.equal(3, name.rdns.length);
|
||||
name.rdns.forEach(function(rdn) {
|
||||
t.equal('object', typeof(rdn));
|
||||
});
|
||||
t.equal(name.toString(), DN_STR);
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,81 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var test = require('tap').test;
|
||||
|
||||
var asn1 = require('asn1');
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var EqualityFilter;
|
||||
var AndFilter;
|
||||
|
||||
|
||||
|
||||
///--- Tests
|
||||
|
||||
test('load library', function(t) {
|
||||
var filters = require('../../lib/index').filters;
|
||||
t.ok(filters);
|
||||
EqualityFilter = filters.EqualityFilter;
|
||||
AndFilter = filters.AndFilter;
|
||||
t.ok(EqualityFilter);
|
||||
t.ok(AndFilter);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('Construct no args', function(t) {
|
||||
t.ok(new AndFilter());
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('Construct args', function(t) {
|
||||
var f = new AndFilter();
|
||||
f.addFilter(new EqualityFilter({
|
||||
attribute: 'foo',
|
||||
value: 'bar'
|
||||
}));
|
||||
f.addFilter(new EqualityFilter({
|
||||
attribute: 'zig',
|
||||
value: 'zag'
|
||||
}));
|
||||
t.ok(f);
|
||||
t.equal(f.toString(), '(&(foo=bar)(zig=zag))');
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('match true', function(t) {
|
||||
var f = new AndFilter();
|
||||
f.addFilter(new EqualityFilter({
|
||||
attribute: 'foo',
|
||||
value: 'bar'
|
||||
}));
|
||||
f.addFilter(new EqualityFilter({
|
||||
attribute: 'zig',
|
||||
value: 'zag'
|
||||
}));
|
||||
t.ok(f);
|
||||
t.ok(f.matches({ foo: 'bar', zig: 'zag' }));
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('match false', function(t) {
|
||||
var f = new AndFilter();
|
||||
f.addFilter(new EqualityFilter({
|
||||
attribute: 'foo',
|
||||
value: 'bar'
|
||||
}));
|
||||
f.addFilter(new EqualityFilter({
|
||||
attribute: 'zig',
|
||||
value: 'zag'
|
||||
}));
|
||||
t.ok(f);
|
||||
t.ok(!f.matches({ foo: 'bar', zig: 'zonk' }));
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var test = require('tap').test;
|
||||
|
||||
var asn1 = require('asn1');
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var ApproximateFilter;
|
||||
var BerReader = asn1.BerReader;
|
||||
var BerWriter = asn1.BerWriter;
|
||||
|
||||
|
||||
|
||||
///--- Tests
|
||||
|
||||
test('load library', function(t) {
|
||||
var filters = require('../../lib/index').filters;
|
||||
t.ok(filters);
|
||||
ApproximateFilter = filters.ApproximateFilter;
|
||||
t.ok(ApproximateFilter);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('Construct no args', function(t) {
|
||||
var f = new ApproximateFilter();
|
||||
t.ok(f);
|
||||
t.ok(!f.attribute);
|
||||
t.ok(!f.value);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('Construct args', function(t) {
|
||||
var f = new ApproximateFilter({
|
||||
attribute: 'foo',
|
||||
value: 'bar'
|
||||
});
|
||||
t.ok(f);
|
||||
t.equal(f.attribute, 'foo');
|
||||
t.equal(f.value, 'bar');
|
||||
t.equal(f.toString(), '(foo~=bar)');
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('match true', function(t) {
|
||||
var f = new ApproximateFilter({
|
||||
attribute: 'foo',
|
||||
value: 'bar'
|
||||
});
|
||||
t.ok(f);
|
||||
t.ok(f.matches({ foo: 'bar' }));
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('match false', function(t) {
|
||||
var f = new ApproximateFilter({
|
||||
attribute: 'foo',
|
||||
value: 'bar'
|
||||
});
|
||||
t.ok(f);
|
||||
t.ok(!f.matches({ foo: 'baz' }));
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('parse ok', function(t) {
|
||||
var writer = new BerWriter();
|
||||
writer.writeString('foo');
|
||||
writer.writeString('bar');
|
||||
|
||||
var f = new ApproximateFilter();
|
||||
t.ok(f);
|
||||
t.ok(f.parse(new BerReader(writer.buffer)));
|
||||
t.ok(f.matches({ foo: 'bar' }));
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('parse bad', function(t) {
|
||||
var writer = new BerWriter();
|
||||
writer.writeString('foo');
|
||||
writer.writeInt(20);
|
||||
|
||||
var f = new ApproximateFilter();
|
||||
t.ok(f);
|
||||
try {
|
||||
f.parse(new BerReader(writer.buffer));
|
||||
t.fail('Should have thrown InvalidAsn1Error');
|
||||
} catch (e) {
|
||||
t.equal(e.name, 'InvalidAsn1Error');
|
||||
}
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,98 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var test = require('tap').test;
|
||||
|
||||
var asn1 = require('asn1');
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var EqualityFilter;
|
||||
var BerReader = asn1.BerReader;
|
||||
var BerWriter = asn1.BerWriter;
|
||||
|
||||
|
||||
|
||||
///--- Tests
|
||||
|
||||
test('load library', function(t) {
|
||||
var filters = require('../../lib/index').filters;
|
||||
t.ok(filters);
|
||||
EqualityFilter = filters.EqualityFilter;
|
||||
t.ok(EqualityFilter);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('Construct no args', function(t) {
|
||||
var f = new EqualityFilter();
|
||||
t.ok(f);
|
||||
t.ok(!f.attribute);
|
||||
t.ok(!f.value);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('Construct args', function(t) {
|
||||
var f = new EqualityFilter({
|
||||
attribute: 'foo',
|
||||
value: 'bar'
|
||||
});
|
||||
t.ok(f);
|
||||
t.equal(f.attribute, 'foo');
|
||||
t.equal(f.value, 'bar');
|
||||
t.equal(f.toString(), '(foo=bar)');
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('match true', function(t) {
|
||||
var f = new EqualityFilter({
|
||||
attribute: 'foo',
|
||||
value: 'bar'
|
||||
});
|
||||
t.ok(f);
|
||||
t.ok(f.matches({ foo: 'bar' }));
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('match false', function(t) {
|
||||
var f = new EqualityFilter({
|
||||
attribute: 'foo',
|
||||
value: 'bar'
|
||||
});
|
||||
t.ok(f);
|
||||
t.ok(!f.matches({ foo: 'baz' }));
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('parse ok', function(t) {
|
||||
var writer = new BerWriter();
|
||||
writer.writeString('foo');
|
||||
writer.writeString('bar');
|
||||
|
||||
var f = new EqualityFilter();
|
||||
t.ok(f);
|
||||
t.ok(f.parse(new BerReader(writer.buffer)));
|
||||
t.ok(f.matches({ foo: 'bar' }));
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('parse bad', function(t) {
|
||||
var writer = new BerWriter();
|
||||
writer.writeString('foo');
|
||||
writer.writeInt(20);
|
||||
|
||||
var f = new EqualityFilter();
|
||||
t.ok(f);
|
||||
try {
|
||||
f.parse(new BerReader(writer.buffer));
|
||||
t.fail('Should have thrown InvalidAsn1Error');
|
||||
} catch (e) {
|
||||
t.equal(e.name, 'InvalidAsn1Error');
|
||||
}
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,98 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var test = require('tap').test;
|
||||
|
||||
var asn1 = require('asn1');
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var GreaterThanEqualsFilter;
|
||||
var BerReader = asn1.BerReader;
|
||||
var BerWriter = asn1.BerWriter;
|
||||
|
||||
|
||||
|
||||
///--- Tests
|
||||
|
||||
test('load library', function(t) {
|
||||
var filters = require('../../lib/index').filters;
|
||||
t.ok(filters);
|
||||
GreaterThanEqualsFilter = filters.GreaterThanEqualsFilter;
|
||||
t.ok(GreaterThanEqualsFilter);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('Construct no args', function(t) {
|
||||
var f = new GreaterThanEqualsFilter();
|
||||
t.ok(f);
|
||||
t.ok(!f.attribute);
|
||||
t.ok(!f.value);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('Construct args', function(t) {
|
||||
var f = new GreaterThanEqualsFilter({
|
||||
attribute: 'foo',
|
||||
value: 'bar'
|
||||
});
|
||||
t.ok(f);
|
||||
t.equal(f.attribute, 'foo');
|
||||
t.equal(f.value, 'bar');
|
||||
t.equal(f.toString(), '(foo>=bar)');
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('match true', function(t) {
|
||||
var f = new GreaterThanEqualsFilter({
|
||||
attribute: 'foo',
|
||||
value: 'bar'
|
||||
});
|
||||
t.ok(f);
|
||||
t.ok(f.matches({ foo: 'baz' }));
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('match false', function(t) {
|
||||
var f = new GreaterThanEqualsFilter({
|
||||
attribute: 'foo',
|
||||
value: 'bar'
|
||||
});
|
||||
t.ok(f);
|
||||
t.ok(!f.matches({ foo: 'abc' }));
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('parse ok', function(t) {
|
||||
var writer = new BerWriter();
|
||||
writer.writeString('foo');
|
||||
writer.writeString('bar');
|
||||
|
||||
var f = new GreaterThanEqualsFilter();
|
||||
t.ok(f);
|
||||
t.ok(f.parse(new BerReader(writer.buffer)));
|
||||
t.ok(f.matches({ foo: 'bar' }));
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('parse bad', function(t) {
|
||||
var writer = new BerWriter();
|
||||
writer.writeString('foo');
|
||||
writer.writeInt(20);
|
||||
|
||||
var f = new GreaterThanEqualsFilter();
|
||||
t.ok(f);
|
||||
try {
|
||||
f.parse(new BerReader(writer.buffer));
|
||||
t.fail('Should have thrown InvalidAsn1Error');
|
||||
} catch (e) {
|
||||
t.equal(e.name, 'InvalidAsn1Error');
|
||||
}
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,98 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var test = require('tap').test;
|
||||
|
||||
var asn1 = require('asn1');
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var LessThanEqualsFilter;
|
||||
var BerReader = asn1.BerReader;
|
||||
var BerWriter = asn1.BerWriter;
|
||||
|
||||
|
||||
|
||||
///--- Tests
|
||||
|
||||
test('load library', function(t) {
|
||||
var filters = require('../../lib/index').filters;
|
||||
t.ok(filters);
|
||||
LessThanEqualsFilter = filters.LessThanEqualsFilter;
|
||||
t.ok(LessThanEqualsFilter);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('Construct no args', function(t) {
|
||||
var f = new LessThanEqualsFilter();
|
||||
t.ok(f);
|
||||
t.ok(!f.attribute);
|
||||
t.ok(!f.value);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('Construct args', function(t) {
|
||||
var f = new LessThanEqualsFilter({
|
||||
attribute: 'foo',
|
||||
value: 'bar'
|
||||
});
|
||||
t.ok(f);
|
||||
t.equal(f.attribute, 'foo');
|
||||
t.equal(f.value, 'bar');
|
||||
t.equal(f.toString(), '(foo<=bar)');
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('match true', function(t) {
|
||||
var f = new LessThanEqualsFilter({
|
||||
attribute: 'foo',
|
||||
value: 'bar'
|
||||
});
|
||||
t.ok(f);
|
||||
t.ok(f.matches({ foo: 'abc' }));
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('match false', function(t) {
|
||||
var f = new LessThanEqualsFilter({
|
||||
attribute: 'foo',
|
||||
value: 'bar'
|
||||
});
|
||||
t.ok(f);
|
||||
t.ok(!f.matches({ foo: 'baz' }));
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('parse ok', function(t) {
|
||||
var writer = new BerWriter();
|
||||
writer.writeString('foo');
|
||||
writer.writeString('bar');
|
||||
|
||||
var f = new LessThanEqualsFilter();
|
||||
t.ok(f);
|
||||
t.ok(f.parse(new BerReader(writer.buffer)));
|
||||
t.ok(f.matches({ foo: 'bar' }));
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('parse bad', function(t) {
|
||||
var writer = new BerWriter();
|
||||
writer.writeString('foo');
|
||||
writer.writeInt(20);
|
||||
|
||||
var f = new LessThanEqualsFilter();
|
||||
t.ok(f);
|
||||
try {
|
||||
f.parse(new BerReader(writer.buffer));
|
||||
t.fail('Should have thrown InvalidAsn1Error');
|
||||
} catch (e) {
|
||||
t.equal(e.name, 'InvalidAsn1Error');
|
||||
}
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,77 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var test = require('tap').test;
|
||||
|
||||
var asn1 = require('asn1');
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var EqualityFilter;
|
||||
var NotFilter;
|
||||
|
||||
|
||||
|
||||
///--- Tests
|
||||
|
||||
test('load library', function(t) {
|
||||
var filters = require('../../lib/index').filters;
|
||||
t.ok(filters);
|
||||
EqualityFilter = filters.EqualityFilter;
|
||||
NotFilter = filters.NotFilter;
|
||||
t.ok(EqualityFilter);
|
||||
t.ok(NotFilter);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('Construct no args', function(t) {
|
||||
try {
|
||||
new NotFilter();
|
||||
t.fail('should have thrown');
|
||||
} catch (e) {
|
||||
t.ok(e instanceof TypeError);
|
||||
}
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('Construct args', function(t) {
|
||||
var f = new NotFilter({
|
||||
filter: new EqualityFilter({
|
||||
attribute: 'foo',
|
||||
value: 'bar'
|
||||
})
|
||||
});
|
||||
t.ok(f);
|
||||
t.equal(f.toString(), '(!(foo=bar))');
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('match true', function(t) {
|
||||
var f = new NotFilter({
|
||||
filter: new EqualityFilter({
|
||||
attribute: 'foo',
|
||||
value: 'bar'
|
||||
})
|
||||
});
|
||||
t.ok(f);
|
||||
t.ok(f.matches({ foo: 'baz' }));
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('match false', function(t) {
|
||||
var f = new NotFilter({
|
||||
filter: new EqualityFilter({
|
||||
attribute: 'foo',
|
||||
value: 'bar'
|
||||
})
|
||||
});
|
||||
t.ok(f);
|
||||
t.ok(!f.matches({ foo: 'bar' }));
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var test = require('tap').test;
|
||||
|
||||
var asn1 = require('asn1');
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var EqualityFilter;
|
||||
var OrFilter;
|
||||
|
||||
|
||||
|
||||
///--- Tests
|
||||
|
||||
test('load library', function(t) {
|
||||
var filters = require('../../lib/index').filters;
|
||||
t.ok(filters);
|
||||
EqualityFilter = filters.EqualityFilter;
|
||||
OrFilter = filters.OrFilter;
|
||||
t.ok(EqualityFilter);
|
||||
t.ok(OrFilter);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('Construct no args', function(t) {
|
||||
t.ok(new OrFilter());
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('Construct args', function(t) {
|
||||
var f = new OrFilter();
|
||||
f.addFilter(new EqualityFilter({
|
||||
attribute: 'foo',
|
||||
value: 'bar'
|
||||
}));
|
||||
f.addFilter(new EqualityFilter({
|
||||
attribute: 'zig',
|
||||
value: 'zag'
|
||||
}));
|
||||
t.ok(f);
|
||||
t.equal(f.toString(), '(|(foo=bar)(zig=zag))');
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('match true', function(t) {
|
||||
var f = new OrFilter();
|
||||
f.addFilter(new EqualityFilter({
|
||||
attribute: 'foo',
|
||||
value: 'bar'
|
||||
}));
|
||||
f.addFilter(new EqualityFilter({
|
||||
attribute: 'zig',
|
||||
value: 'zag'
|
||||
}));
|
||||
t.ok(f);
|
||||
t.ok(f.matches({ foo: 'bar', zig: 'zonk' }));
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('match false', function(t) {
|
||||
var f = new OrFilter();
|
||||
f.addFilter(new EqualityFilter({
|
||||
attribute: 'foo',
|
||||
value: 'bar'
|
||||
}));
|
||||
f.addFilter(new EqualityFilter({
|
||||
attribute: 'zig',
|
||||
value: 'zag'
|
||||
}));
|
||||
t.ok(f);
|
||||
t.ok(!f.matches({ foo: 'baz', zig: 'zonk' }));
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var test = require('tap').test;
|
||||
|
||||
var asn1 = require('asn1');
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var PresenceFilter;
|
||||
var BerReader = asn1.BerReader;
|
||||
var BerWriter = asn1.BerWriter;
|
||||
|
||||
|
||||
|
||||
///--- Tests
|
||||
|
||||
test('load library', function(t) {
|
||||
var filters = require('../../lib/index').filters;
|
||||
t.ok(filters);
|
||||
PresenceFilter = filters.PresenceFilter;
|
||||
t.ok(PresenceFilter);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('Construct no args', function(t) {
|
||||
var f = new PresenceFilter();
|
||||
t.ok(f);
|
||||
t.ok(!f.attribute);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('Construct args', function(t) {
|
||||
var f = new PresenceFilter({
|
||||
attribute: 'foo'
|
||||
});
|
||||
t.ok(f);
|
||||
t.equal(f.attribute, 'foo');
|
||||
t.equal(f.toString(), '(foo=*)');
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('match true', function(t) {
|
||||
var f = new PresenceFilter({
|
||||
attribute: 'foo'
|
||||
});
|
||||
t.ok(f);
|
||||
t.ok(f.matches({ foo: 'bar' }));
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('match false', function(t) {
|
||||
var f = new PresenceFilter({
|
||||
attribute: 'foo'
|
||||
});
|
||||
t.ok(f);
|
||||
t.ok(!f.matches({ bar: 'foo' }));
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('parse ok', function(t) {
|
||||
var writer = new BerWriter();
|
||||
writer.writeString('foo');
|
||||
|
||||
var f = new PresenceFilter();
|
||||
t.ok(f);
|
||||
t.ok(f.parse(new BerReader(writer.buffer)));
|
||||
t.ok(f.matches({ foo: 'bar' }));
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('parse bad', function(t) {
|
||||
var writer = new BerWriter();
|
||||
writer.writeInt(20);
|
||||
|
||||
var f = new PresenceFilter();
|
||||
t.ok(f);
|
||||
try {
|
||||
f.parse(new BerReader(writer.buffer));
|
||||
t.fail('Should have thrown InvalidAsn1Error');
|
||||
} catch (e) {
|
||||
t.equal(e.name, 'InvalidAsn1Error');
|
||||
}
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,110 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var test = require('tap').test;
|
||||
|
||||
var asn1 = require('asn1');
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var SubstringFilter;
|
||||
var BerReader = asn1.BerReader;
|
||||
var BerWriter = asn1.BerWriter;
|
||||
|
||||
|
||||
|
||||
///--- Tests
|
||||
|
||||
test('load library', function(t) {
|
||||
var filters = require('../../lib/index').filters;
|
||||
t.ok(filters);
|
||||
SubstringFilter = filters.SubstringFilter;
|
||||
t.ok(SubstringFilter);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('Construct no args', function(t) {
|
||||
var f = new SubstringFilter();
|
||||
t.ok(f);
|
||||
t.ok(!f.attribute);
|
||||
t.ok(!f.value);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('Construct args', function(t) {
|
||||
var f = new SubstringFilter({
|
||||
attribute: 'foo',
|
||||
initial: 'bar',
|
||||
any: ['zig', 'zag'],
|
||||
'final': 'baz'
|
||||
});
|
||||
t.ok(f);
|
||||
t.equal(f.attribute, 'foo');
|
||||
t.equal(f.initial, 'bar');
|
||||
t.equal(f.any.length, 2);
|
||||
t.equal(f.any[0], 'zig');
|
||||
t.equal(f.any[1], 'zag');
|
||||
t.equal(f['final'], 'baz');
|
||||
t.equal(f.toString(), '(foo=bar*zig*zag*baz)');
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('match true', function(t) {
|
||||
var f = new SubstringFilter({
|
||||
attribute: 'foo',
|
||||
initial: 'bar',
|
||||
any: ['zig', 'zag'],
|
||||
'final': 'baz'
|
||||
});
|
||||
t.ok(f);
|
||||
t.ok(f.matches({ foo: 'barmoozigbarzagblahbaz' }));
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('match false', function(t) {
|
||||
var f = new SubstringFilter({
|
||||
attribute: 'foo',
|
||||
initial: 'bar',
|
||||
foo: ['zig', 'zag'],
|
||||
'final': 'baz'
|
||||
});
|
||||
t.ok(f);
|
||||
t.ok(!f.matches({ foo: 'bafmoozigbarzagblahbaz' }));
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('parse ok', function(t) {
|
||||
var writer = new BerWriter();
|
||||
writer.writeString('foo');
|
||||
writer.startSequence();
|
||||
writer.writeString('bar', 0x80);
|
||||
writer.writeString('bad', 0x81);
|
||||
writer.writeString('baz', 0x82);
|
||||
writer.endSequence();
|
||||
var f = new SubstringFilter();
|
||||
t.ok(f);
|
||||
t.ok(f.parse(new BerReader(writer.buffer)));
|
||||
t.ok(f.matches({ foo: 'bargoobadgoobaz' }));
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('parse bad', function(t) {
|
||||
var writer = new BerWriter();
|
||||
writer.writeString('foo');
|
||||
writer.writeInt(20);
|
||||
|
||||
var f = new SubstringFilter();
|
||||
t.ok(f);
|
||||
try {
|
||||
f.parse(new BerReader(writer.buffer));
|
||||
t.fail('Should have thrown InvalidAsn1Error');
|
||||
} catch (e) {
|
||||
}
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,115 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var test = require('tap').test;
|
||||
|
||||
var asn1 = require('asn1');
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var BerReader = asn1.BerReader;
|
||||
var BerWriter = asn1.BerWriter;
|
||||
var AddRequest;
|
||||
var Attribute;
|
||||
var dn;
|
||||
|
||||
///--- Tests
|
||||
|
||||
test('load library', function(t) {
|
||||
AddRequest = require('../../lib/index').AddRequest;
|
||||
Attribute = require('../../lib/index').Attribute;
|
||||
dn = require('../../lib/index').dn;
|
||||
t.ok(AddRequest);
|
||||
t.ok(Attribute);
|
||||
t.ok(dn);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('new no args', function(t) {
|
||||
t.ok(new AddRequest());
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('new with args', function(t) {
|
||||
var req = new AddRequest({
|
||||
entry: dn.parse('cn=foo, o=test'),
|
||||
attributes: [new Attribute({type: 'cn', vals: ['foo']}),
|
||||
new Attribute({type: 'objectclass', vals: ['person']})]
|
||||
});
|
||||
t.ok(req);
|
||||
t.equal(req.dn.toString(), 'cn=foo, o=test');
|
||||
t.equal(req.attributes.length, 2);
|
||||
t.equal(req.attributes[0].type, 'cn');
|
||||
t.equal(req.attributes[0].vals[0], 'foo');
|
||||
t.equal(req.attributes[1].type, 'objectclass');
|
||||
t.equal(req.attributes[1].vals[0], 'person');
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('parse', function(t) {
|
||||
var ber = new BerWriter();
|
||||
ber.writeString('cn=foo,o=test');
|
||||
|
||||
ber.startSequence();
|
||||
|
||||
ber.startSequence();
|
||||
ber.writeString('cn');
|
||||
ber.startSequence(0x31);
|
||||
ber.writeString('foo');
|
||||
ber.endSequence();
|
||||
ber.endSequence();
|
||||
|
||||
ber.startSequence();
|
||||
ber.writeString('objectclass');
|
||||
ber.startSequence(0x31);
|
||||
ber.writeString('person');
|
||||
ber.endSequence();
|
||||
ber.endSequence();
|
||||
|
||||
ber.endSequence();
|
||||
|
||||
var req = new AddRequest();
|
||||
t.ok(req._parse(new BerReader(ber.buffer)));
|
||||
t.equal(req.dn.toString(), 'cn=foo, o=test');
|
||||
t.equal(req.attributes.length, 2);
|
||||
t.equal(req.attributes[0].type, 'cn');
|
||||
t.equal(req.attributes[0].vals[0], 'foo');
|
||||
t.equal(req.attributes[1].type, 'objectclass');
|
||||
t.equal(req.attributes[1].vals[0], 'person');
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('toBer', function(t) {
|
||||
var req = new AddRequest({
|
||||
messageID: 123,
|
||||
entry: dn.parse('cn=foo, o=test'),
|
||||
attributes: [new Attribute({type: 'cn', vals: ['foo']}),
|
||||
new Attribute({type: 'objectclass', vals: ['person']})]
|
||||
});
|
||||
|
||||
t.ok(req);
|
||||
|
||||
var ber = new BerReader(req.toBer());
|
||||
t.ok(ber);
|
||||
t.equal(ber.readSequence(), 0x30);
|
||||
t.equal(ber.readInt(), 123);
|
||||
t.equal(ber.readSequence(), 0x68);
|
||||
t.equal(ber.readString(), 'cn=foo, o=test');
|
||||
t.ok(ber.readSequence());
|
||||
|
||||
t.ok(ber.readSequence());
|
||||
t.equal(ber.readString(), 'cn');
|
||||
t.equal(ber.readSequence(), 0x31);
|
||||
t.equal(ber.readString(), 'foo');
|
||||
|
||||
t.ok(ber.readSequence());
|
||||
t.equal(ber.readString(), 'objectclass');
|
||||
t.equal(ber.readSequence(), 0x31);
|
||||
t.equal(ber.readString(), 'person');
|
||||
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,76 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var test = require('tap').test;
|
||||
|
||||
var asn1 = require('asn1');
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var BerReader = asn1.BerReader;
|
||||
var BerWriter = asn1.BerWriter;
|
||||
var AddResponse;
|
||||
|
||||
|
||||
///--- Tests
|
||||
|
||||
test('load library', function(t) {
|
||||
AddResponse = require('../../lib/index').AddResponse;
|
||||
t.ok(AddResponse);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('new no args', function(t) {
|
||||
t.ok(new AddResponse());
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('new with args', function(t) {
|
||||
var res = new AddResponse({
|
||||
messageID: 123,
|
||||
status: 0
|
||||
});
|
||||
t.ok(res);
|
||||
t.equal(res.messageID, 123);
|
||||
t.equal(res.status, 0);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('parse', function(t) {
|
||||
var ber = new BerWriter();
|
||||
ber.writeEnumeration(0);
|
||||
ber.writeString('cn=root');
|
||||
ber.writeString('foo');
|
||||
|
||||
var res = new AddResponse();
|
||||
t.ok(res._parse(new BerReader(ber.buffer)));
|
||||
t.equal(res.status, 0);
|
||||
t.equal(res.matchedDN, 'cn=root');
|
||||
t.equal(res.errorMessage, 'foo');
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('toBer', function(t) {
|
||||
var res = new AddResponse({
|
||||
messageID: 123,
|
||||
status: 3,
|
||||
matchedDN: 'cn=root',
|
||||
errorMessage: 'foo'
|
||||
});
|
||||
t.ok(res);
|
||||
|
||||
var ber = new BerReader(res.toBer());
|
||||
t.ok(ber);
|
||||
t.equal(ber.readSequence(), 0x30);
|
||||
t.equal(ber.readInt(), 123);
|
||||
t.equal(ber.readSequence(), 0x69);
|
||||
t.equal(ber.readEnumeration(), 3);
|
||||
t.equal(ber.readString(), 'cn=root');
|
||||
t.equal(ber.readString(), 'foo');
|
||||
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,82 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var test = require('tap').test;
|
||||
|
||||
var asn1 = require('asn1');
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var BerReader = asn1.BerReader;
|
||||
var BerWriter = asn1.BerWriter;
|
||||
var BindRequest;
|
||||
var dn;
|
||||
|
||||
///--- Tests
|
||||
|
||||
test('load library', function(t) {
|
||||
BindRequest = require('../../lib/index').BindRequest;
|
||||
dn = require('../../lib/index').dn;
|
||||
t.ok(BindRequest);
|
||||
t.ok(dn);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('new no args', function(t) {
|
||||
t.ok(new BindRequest());
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('new with args', function(t) {
|
||||
var req = new BindRequest({
|
||||
version: 3,
|
||||
name: dn.parse('cn=root'),
|
||||
credentials: 'secret'
|
||||
});
|
||||
t.ok(req);
|
||||
t.equal(req.version, 3);
|
||||
t.equal(req.name.toString(), 'cn=root');
|
||||
t.equal(req.credentials, 'secret');
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('parse', function(t) {
|
||||
var ber = new BerWriter();
|
||||
ber.writeInt(3);
|
||||
ber.writeString('cn=root');
|
||||
ber.writeString('secret', 0x80);
|
||||
|
||||
var req = new BindRequest();
|
||||
t.ok(req._parse(new BerReader(ber.buffer)));
|
||||
t.equal(req.version, 3);
|
||||
t.equal(req.dn, 'cn=root');
|
||||
t.ok(req.name.constructor);
|
||||
t.equal(req.name.constructor.name, 'DN');
|
||||
t.equal(req.credentials, 'secret');
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('toBer', function(t) {
|
||||
var req = new BindRequest({
|
||||
messageID: 123,
|
||||
version: 3,
|
||||
name: dn.parse('cn=root'),
|
||||
credentials: 'secret'
|
||||
});
|
||||
t.ok(req);
|
||||
|
||||
var ber = new BerReader(req.toBer());
|
||||
t.ok(ber);
|
||||
t.equal(ber.readSequence(), 0x30);
|
||||
t.equal(ber.readInt(), 123);
|
||||
t.equal(ber.readSequence(), 0x60);
|
||||
t.equal(ber.readInt(), 0x03);
|
||||
t.equal(ber.readString(), 'cn=root');
|
||||
t.equal(ber.readString(0x80), 'secret');
|
||||
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,76 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var test = require('tap').test;
|
||||
|
||||
var asn1 = require('asn1');
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var BerReader = asn1.BerReader;
|
||||
var BerWriter = asn1.BerWriter;
|
||||
var BindResponse;
|
||||
|
||||
|
||||
///--- Tests
|
||||
|
||||
test('load library', function(t) {
|
||||
BindResponse = require('../../lib/index').BindResponse;
|
||||
t.ok(BindResponse);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('new no args', function(t) {
|
||||
t.ok(new BindResponse());
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('new with args', function(t) {
|
||||
var res = new BindResponse({
|
||||
messageID: 123,
|
||||
status: 0
|
||||
});
|
||||
t.ok(res);
|
||||
t.equal(res.messageID, 123);
|
||||
t.equal(res.status, 0);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('parse', function(t) {
|
||||
var ber = new BerWriter();
|
||||
ber.writeEnumeration(0);
|
||||
ber.writeString('cn=root');
|
||||
ber.writeString('foo');
|
||||
|
||||
var res = new BindResponse();
|
||||
t.ok(res._parse(new BerReader(ber.buffer)));
|
||||
t.equal(res.status, 0);
|
||||
t.equal(res.matchedDN, 'cn=root');
|
||||
t.equal(res.errorMessage, 'foo');
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('toBer', function(t) {
|
||||
var res = new BindResponse({
|
||||
messageID: 123,
|
||||
status: 3,
|
||||
matchedDN: 'cn=root',
|
||||
errorMessage: 'foo'
|
||||
});
|
||||
t.ok(res);
|
||||
|
||||
var ber = new BerReader(res.toBer());
|
||||
t.ok(ber);
|
||||
t.equal(ber.readSequence(), 0x30);
|
||||
t.equal(ber.readInt(), 123);
|
||||
t.equal(ber.readSequence(), 0x61);
|
||||
t.equal(ber.readEnumeration(), 3);
|
||||
t.equal(ber.readString(), 'cn=root');
|
||||
t.equal(ber.readString(), 'foo');
|
||||
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,87 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var test = require('tap').test;
|
||||
|
||||
var asn1 = require('asn1');
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var BerReader = asn1.BerReader;
|
||||
var BerWriter = asn1.BerWriter;
|
||||
var CompareRequest;
|
||||
var dn;
|
||||
|
||||
///--- Tests
|
||||
|
||||
test('load library', function(t) {
|
||||
CompareRequest = require('../../lib/index').CompareRequest;
|
||||
dn = require('../../lib/index').dn;
|
||||
t.ok(CompareRequest);
|
||||
t.ok(dn);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('new no args', function(t) {
|
||||
t.ok(new CompareRequest());
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('new with args', function(t) {
|
||||
var req = new CompareRequest({
|
||||
entry: dn.parse('cn=foo, o=test'),
|
||||
attribute: 'sn',
|
||||
value: 'testy'
|
||||
});
|
||||
t.ok(req);
|
||||
t.equal(req.dn, 'cn=foo, o=test');
|
||||
t.equal(req.attribute, 'sn');
|
||||
t.equal(req.value, 'testy');
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('parse', function(t) {
|
||||
var ber = new BerWriter();
|
||||
ber.writeString('cn=foo,o=test');
|
||||
|
||||
ber.startSequence();
|
||||
ber.writeString('sn');
|
||||
ber.writeString('testy');
|
||||
ber.endSequence();
|
||||
|
||||
|
||||
var req = new CompareRequest();
|
||||
t.ok(req._parse(new BerReader(ber.buffer)));
|
||||
t.equal(req.dn, 'cn=foo, o=test');
|
||||
t.equal(req.attribute, 'sn');
|
||||
t.equal(req.value, 'testy');
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('toBer', function(t) {
|
||||
var req = new CompareRequest({
|
||||
messageID: 123,
|
||||
entry: dn.parse('cn=foo, o=test'),
|
||||
attribute: 'sn',
|
||||
value: 'testy'
|
||||
});
|
||||
|
||||
t.ok(req);
|
||||
|
||||
var ber = new BerReader(req.toBer());
|
||||
t.ok(ber);
|
||||
t.equal(ber.readSequence(), 0x30);
|
||||
t.equal(ber.readInt(), 123);
|
||||
t.equal(ber.readSequence(), 0x6e);
|
||||
t.equal(ber.readString(), 'cn=foo, o=test');
|
||||
t.ok(ber.readSequence());
|
||||
|
||||
t.equal(ber.readString(), 'sn');
|
||||
t.equal(ber.readString(), 'testy');
|
||||
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,76 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var test = require('tap').test;
|
||||
|
||||
var asn1 = require('asn1');
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var BerReader = asn1.BerReader;
|
||||
var BerWriter = asn1.BerWriter;
|
||||
var CompareResponse;
|
||||
|
||||
|
||||
///--- Tests
|
||||
|
||||
test('load library', function(t) {
|
||||
CompareResponse = require('../../lib/index').CompareResponse;
|
||||
t.ok(CompareResponse);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('new no args', function(t) {
|
||||
t.ok(new CompareResponse());
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('new with args', function(t) {
|
||||
var res = new CompareResponse({
|
||||
messageID: 123,
|
||||
status: 0
|
||||
});
|
||||
t.ok(res);
|
||||
t.equal(res.messageID, 123);
|
||||
t.equal(res.status, 0);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('parse', function(t) {
|
||||
var ber = new BerWriter();
|
||||
ber.writeEnumeration(0);
|
||||
ber.writeString('cn=root');
|
||||
ber.writeString('foo');
|
||||
|
||||
var res = new CompareResponse();
|
||||
t.ok(res._parse(new BerReader(ber.buffer)));
|
||||
t.equal(res.status, 0);
|
||||
t.equal(res.matchedDN, 'cn=root');
|
||||
t.equal(res.errorMessage, 'foo');
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('toBer', function(t) {
|
||||
var res = new CompareResponse({
|
||||
messageID: 123,
|
||||
status: 3,
|
||||
matchedDN: 'cn=root',
|
||||
errorMessage: 'foo'
|
||||
});
|
||||
t.ok(res);
|
||||
|
||||
var ber = new BerReader(res.toBer());
|
||||
t.ok(ber);
|
||||
t.equal(ber.readSequence(), 0x30);
|
||||
t.equal(ber.readInt(), 123);
|
||||
t.equal(ber.readSequence(), 0x6f);
|
||||
t.equal(ber.readEnumeration(), 3);
|
||||
t.equal(ber.readString(), 'cn=root');
|
||||
t.equal(ber.readString(), 'foo');
|
||||
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,69 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var test = require('tap').test;
|
||||
|
||||
var asn1 = require('asn1');
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var BerReader = asn1.BerReader;
|
||||
var BerWriter = asn1.BerWriter;
|
||||
var DeleteRequest;
|
||||
var dn;
|
||||
|
||||
|
||||
///--- Tests
|
||||
|
||||
test('load library', function(t) {
|
||||
DeleteRequest = require('../../lib/index').DeleteRequest;
|
||||
dn = require('../../lib/index').dn;
|
||||
t.ok(DeleteRequest);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('new no args', function(t) {
|
||||
t.ok(new DeleteRequest());
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('new with args', function(t) {
|
||||
var req = new DeleteRequest({
|
||||
entry: dn.parse('cn=test')
|
||||
});
|
||||
t.ok(req);
|
||||
t.equal(req.dn, 'cn=test');
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('parse', function(t) {
|
||||
var ber = new BerWriter();
|
||||
ber.writeString('cn=test', 0x4a);
|
||||
|
||||
var req = new DeleteRequest();
|
||||
var reader = new BerReader(ber.buffer);
|
||||
reader.readSequence(0x4a);
|
||||
t.ok(req.parse(reader.buffer, reader.length));
|
||||
t.equal(req.dn, 'cn=test');
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('toBer', function(t) {
|
||||
var req = new DeleteRequest({
|
||||
messageID: 123,
|
||||
entry: dn.parse('cn=test')
|
||||
});
|
||||
t.ok(req);
|
||||
|
||||
var ber = new BerReader(req.toBer());
|
||||
t.ok(ber);
|
||||
t.equal(ber.readSequence(), 0x30);
|
||||
t.equal(ber.readInt(), 123);
|
||||
t.equal(ber.readString(0x4a), 'cn=test');
|
||||
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,76 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var test = require('tap').test;
|
||||
|
||||
var asn1 = require('asn1');
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var BerReader = asn1.BerReader;
|
||||
var BerWriter = asn1.BerWriter;
|
||||
var DeleteResponse;
|
||||
|
||||
|
||||
///--- Tests
|
||||
|
||||
test('load library', function(t) {
|
||||
DeleteResponse = require('../../lib/index').DeleteResponse;
|
||||
t.ok(DeleteResponse);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('new no args', function(t) {
|
||||
t.ok(new DeleteResponse());
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('new with args', function(t) {
|
||||
var res = new DeleteResponse({
|
||||
messageID: 123,
|
||||
status: 0
|
||||
});
|
||||
t.ok(res);
|
||||
t.equal(res.messageID, 123);
|
||||
t.equal(res.status, 0);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('parse', function(t) {
|
||||
var ber = new BerWriter();
|
||||
ber.writeEnumeration(0);
|
||||
ber.writeString('cn=root');
|
||||
ber.writeString('foo');
|
||||
|
||||
var res = new DeleteResponse();
|
||||
t.ok(res._parse(new BerReader(ber.buffer)));
|
||||
t.equal(res.status, 0);
|
||||
t.equal(res.matchedDN, 'cn=root');
|
||||
t.equal(res.errorMessage, 'foo');
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('toBer', function(t) {
|
||||
var res = new DeleteResponse({
|
||||
messageID: 123,
|
||||
status: 3,
|
||||
matchedDN: 'cn=root',
|
||||
errorMessage: 'foo'
|
||||
});
|
||||
t.ok(res);
|
||||
|
||||
var ber = new BerReader(res.toBer());
|
||||
t.ok(ber);
|
||||
t.equal(ber.readSequence(), 0x30);
|
||||
t.equal(ber.readInt(), 123);
|
||||
t.equal(ber.readSequence(), 0x6b);
|
||||
t.equal(ber.readEnumeration(), 3);
|
||||
t.equal(ber.readString(), 'cn=root');
|
||||
t.equal(ber.readString(), 'foo');
|
||||
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,76 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var test = require('tap').test;
|
||||
|
||||
var asn1 = require('asn1');
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var BerReader = asn1.BerReader;
|
||||
var BerWriter = asn1.BerWriter;
|
||||
var ExtendedRequest;
|
||||
var dn;
|
||||
|
||||
///--- Tests
|
||||
|
||||
test('load library', function(t) {
|
||||
ExtendedRequest = require('../../lib/index').ExtendedRequest;
|
||||
dn = require('../../lib/index').dn;
|
||||
t.ok(ExtendedRequest);
|
||||
t.ok(dn);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('new no args', function(t) {
|
||||
t.ok(new ExtendedRequest());
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('new with args', function(t) {
|
||||
var req = new ExtendedRequest({
|
||||
requestName: '1.2.3.4',
|
||||
requestValue: 'test'
|
||||
});
|
||||
t.ok(req);
|
||||
t.equal(req.requestName, '1.2.3.4');
|
||||
t.equal(req.requestValue, 'test');
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('parse', function(t) {
|
||||
var ber = new BerWriter();
|
||||
ber.writeString('1.2.3.4', 0x80);
|
||||
ber.writeString('test', 0x81);
|
||||
|
||||
|
||||
var req = new ExtendedRequest();
|
||||
t.ok(req._parse(new BerReader(ber.buffer)));
|
||||
t.equal(req.requestName, '1.2.3.4');
|
||||
t.equal(req.requestValue, 'test');
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('toBer', function(t) {
|
||||
var req = new ExtendedRequest({
|
||||
messageID: 123,
|
||||
requestName: '1.2.3.4',
|
||||
requestValue: 'test'
|
||||
});
|
||||
|
||||
t.ok(req);
|
||||
|
||||
var ber = new BerReader(req.toBer());
|
||||
t.ok(ber);
|
||||
t.equal(ber.readSequence(), 0x30);
|
||||
t.equal(ber.readInt(), 123);
|
||||
t.equal(ber.readSequence(), 0x77);
|
||||
t.equal(ber.readString(0x80), '1.2.3.4');
|
||||
t.equal(ber.readString(0x81), 'test');
|
||||
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,88 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var test = require('tap').test;
|
||||
|
||||
var asn1 = require('asn1');
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var BerReader = asn1.BerReader;
|
||||
var BerWriter = asn1.BerWriter;
|
||||
var ExtendedResponse;
|
||||
|
||||
|
||||
///--- Tests
|
||||
|
||||
test('load library', function(t) {
|
||||
ExtendedResponse = require('../../lib/index').ExtendedResponse;
|
||||
t.ok(ExtendedResponse);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('new no args', function(t) {
|
||||
t.ok(new ExtendedResponse());
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('new with args', function(t) {
|
||||
var res = new ExtendedResponse({
|
||||
messageID: 123,
|
||||
status: 0,
|
||||
responseName: '1.2.3.4',
|
||||
responseValue: 'test'
|
||||
});
|
||||
t.ok(res);
|
||||
t.equal(res.messageID, 123);
|
||||
t.equal(res.status, 0);
|
||||
t.equal(res.responseName, '1.2.3.4');
|
||||
t.equal(res.responseValue, 'test');
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('parse', function(t) {
|
||||
var ber = new BerWriter();
|
||||
ber.writeEnumeration(0);
|
||||
ber.writeString('cn=root');
|
||||
ber.writeString('foo');
|
||||
ber.writeString('1.2.3.4', 0x8a);
|
||||
ber.writeString('test', 0x8b);
|
||||
|
||||
var res = new ExtendedResponse();
|
||||
t.ok(res._parse(new BerReader(ber.buffer)));
|
||||
t.equal(res.status, 0);
|
||||
t.equal(res.matchedDN, 'cn=root');
|
||||
t.equal(res.errorMessage, 'foo');
|
||||
t.equal(res.responseName, '1.2.3.4');
|
||||
t.equal(res.responseValue, 'test');
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('toBer', function(t) {
|
||||
var res = new ExtendedResponse({
|
||||
messageID: 123,
|
||||
status: 3,
|
||||
matchedDN: 'cn=root',
|
||||
errorMessage: 'foo',
|
||||
responseName: '1.2.3.4',
|
||||
responseValue: 'test'
|
||||
});
|
||||
t.ok(res);
|
||||
|
||||
var ber = new BerReader(res.toBer());
|
||||
t.ok(ber);
|
||||
t.equal(ber.readSequence(), 0x30);
|
||||
t.equal(ber.readInt(), 123);
|
||||
t.equal(ber.readSequence(), 0x78);
|
||||
t.equal(ber.readEnumeration(), 3);
|
||||
t.equal(ber.readString(), 'cn=root');
|
||||
t.equal(ber.readString(), 'foo');
|
||||
t.equal(ber.readString(0x8a), '1.2.3.4');
|
||||
t.equal(ber.readString(0x8b), 'test');
|
||||
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,82 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var test = require('tap').test;
|
||||
|
||||
var asn1 = require('asn1');
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var BerReader = asn1.BerReader;
|
||||
var BerWriter = asn1.BerWriter;
|
||||
var ModifyDNRequest;
|
||||
var dn;
|
||||
|
||||
///--- Tests
|
||||
|
||||
test('load library', function(t) {
|
||||
ModifyDNRequest = require('../../lib/index').ModifyDNRequest;
|
||||
dn = require('../../lib/index').dn;
|
||||
t.ok(ModifyDNRequest);
|
||||
t.ok(dn);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('new no args', function(t) {
|
||||
t.ok(new ModifyDNRequest());
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('new with args', function(t) {
|
||||
var req = new ModifyDNRequest({
|
||||
entry: dn.parse('cn=foo, o=test'),
|
||||
newRdn: dn.parse('cn=foo2'),
|
||||
deleteOldRdn: true
|
||||
});
|
||||
t.ok(req);
|
||||
t.equal(req.dn, 'cn=foo, o=test');
|
||||
t.equal(req.newRdn.toString(), 'cn=foo2');
|
||||
t.equal(req.deleteOldRdn, true);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('parse', function(t) {
|
||||
var ber = new BerWriter();
|
||||
ber.writeString('cn=foo,o=test');
|
||||
ber.writeString('cn=foo2');
|
||||
ber.writeBoolean(true);
|
||||
|
||||
var req = new ModifyDNRequest();
|
||||
t.ok(req._parse(new BerReader(ber.buffer)));
|
||||
t.equal(req.dn, 'cn=foo, o=test');
|
||||
t.equal(req.newRdn.toString(), 'cn=foo2');
|
||||
t.equal(req.deleteOldRdn, true);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('toBer', function(t) {
|
||||
var req = new ModifyDNRequest({
|
||||
messageID: 123,
|
||||
entry: dn.parse('cn=foo, o=test'),
|
||||
newRdn: dn.parse('cn=foo2'),
|
||||
deleteOldRdn: true
|
||||
});
|
||||
|
||||
t.ok(req);
|
||||
|
||||
var ber = new BerReader(req.toBer());
|
||||
t.ok(ber);
|
||||
t.equal(ber.readSequence(), 0x30);
|
||||
t.equal(ber.readInt(), 123);
|
||||
t.equal(ber.readSequence(), 0x6c);
|
||||
t.equal(ber.readString(), 'cn=foo, o=test');
|
||||
t.equal(ber.readString(), 'cn=foo2');
|
||||
t.equal(ber.readBoolean(), true);
|
||||
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,76 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var test = require('tap').test;
|
||||
|
||||
var asn1 = require('asn1');
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var BerReader = asn1.BerReader;
|
||||
var BerWriter = asn1.BerWriter;
|
||||
var ModifyDNResponse;
|
||||
|
||||
|
||||
///--- Tests
|
||||
|
||||
test('load library', function(t) {
|
||||
ModifyDNResponse = require('../../lib/index').ModifyDNResponse;
|
||||
t.ok(ModifyDNResponse);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('new no args', function(t) {
|
||||
t.ok(new ModifyDNResponse());
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('new with args', function(t) {
|
||||
var res = new ModifyDNResponse({
|
||||
messageID: 123,
|
||||
status: 0
|
||||
});
|
||||
t.ok(res);
|
||||
t.equal(res.messageID, 123);
|
||||
t.equal(res.status, 0);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('parse', function(t) {
|
||||
var ber = new BerWriter();
|
||||
ber.writeEnumeration(0);
|
||||
ber.writeString('cn=root');
|
||||
ber.writeString('foo');
|
||||
|
||||
var res = new ModifyDNResponse();
|
||||
t.ok(res._parse(new BerReader(ber.buffer)));
|
||||
t.equal(res.status, 0);
|
||||
t.equal(res.matchedDN, 'cn=root');
|
||||
t.equal(res.errorMessage, 'foo');
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('toBer', function(t) {
|
||||
var res = new ModifyDNResponse({
|
||||
messageID: 123,
|
||||
status: 3,
|
||||
matchedDN: 'cn=root',
|
||||
errorMessage: 'foo'
|
||||
});
|
||||
t.ok(res);
|
||||
|
||||
var ber = new BerReader(res.toBer());
|
||||
t.ok(ber);
|
||||
t.equal(ber.readSequence(), 0x30);
|
||||
t.equal(ber.readInt(), 123);
|
||||
t.equal(ber.readSequence(), 0x6d);
|
||||
t.equal(ber.readEnumeration(), 3);
|
||||
t.equal(ber.readString(), 'cn=root');
|
||||
t.equal(ber.readString(), 'foo');
|
||||
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,114 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var test = require('tap').test;
|
||||
|
||||
var asn1 = require('asn1');
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var BerReader = asn1.BerReader;
|
||||
var BerWriter = asn1.BerWriter;
|
||||
var ModifyRequest;
|
||||
var Attribute;
|
||||
var Change;
|
||||
var dn;
|
||||
|
||||
///--- Tests
|
||||
|
||||
test('load library', function(t) {
|
||||
ModifyRequest = require('../../lib/index').ModifyRequest;
|
||||
Attribute = require('../../lib/index').Attribute;
|
||||
Change = require('../../lib/index').Change;
|
||||
dn = require('../../lib/index').dn;
|
||||
t.ok(ModifyRequest);
|
||||
t.ok(Attribute);
|
||||
t.ok(Change);
|
||||
t.ok(dn);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('new no args', function(t) {
|
||||
t.ok(new ModifyRequest());
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('new with args', function(t) {
|
||||
var req = new ModifyRequest({
|
||||
object: dn.parse('cn=foo, o=test'),
|
||||
changes: [new Change({
|
||||
operation: 'Replace',
|
||||
modification: new Attribute({type: 'objectclass', vals: ['person']})
|
||||
})]
|
||||
});
|
||||
t.ok(req);
|
||||
t.equal(req.dn, 'cn=foo, o=test');
|
||||
t.equal(req.changes.length, 1);
|
||||
t.equal(req.changes[0].operation, 'Replace');
|
||||
t.equal(req.changes[0].modification.type, 'objectclass');
|
||||
t.equal(req.changes[0].modification.vals[0], 'person');
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('parse', function(t) {
|
||||
var ber = new BerWriter();
|
||||
ber.writeString('cn=foo,o=test');
|
||||
ber.startSequence();
|
||||
|
||||
ber.startSequence();
|
||||
ber.writeEnumeration(0x02);
|
||||
|
||||
ber.startSequence();
|
||||
ber.writeString('objectclass');
|
||||
ber.startSequence(0x31);
|
||||
ber.writeString('person');
|
||||
ber.endSequence();
|
||||
ber.endSequence();
|
||||
|
||||
ber.endSequence();
|
||||
|
||||
ber.endSequence();
|
||||
|
||||
var req = new ModifyRequest();
|
||||
t.ok(req._parse(new BerReader(ber.buffer)));
|
||||
t.equal(req.dn, 'cn=foo, o=test');
|
||||
t.equal(req.changes.length, 1);
|
||||
t.equal(req.changes[0].operation, 'Replace');
|
||||
t.equal(req.changes[0].modification.type, 'objectclass');
|
||||
t.equal(req.changes[0].modification.vals[0], 'person');
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('toBer', function(t) {
|
||||
var req = new ModifyRequest({
|
||||
messageID: 123,
|
||||
object: dn.parse('cn=foo, o=test'),
|
||||
changes: [new Change({
|
||||
operation: 'Replace',
|
||||
modification: new Attribute({type: 'objectclass', vals: ['person']})
|
||||
})]
|
||||
});
|
||||
|
||||
t.ok(req);
|
||||
|
||||
var ber = new BerReader(req.toBer());
|
||||
t.ok(ber);
|
||||
t.equal(ber.readSequence(), 0x30);
|
||||
t.equal(ber.readInt(), 123);
|
||||
t.equal(ber.readSequence(), 0x66);
|
||||
t.equal(ber.readString(), 'cn=foo, o=test');
|
||||
t.ok(ber.readSequence());
|
||||
t.ok(ber.readSequence());
|
||||
t.equal(ber.readEnumeration(), 0x02);
|
||||
|
||||
t.ok(ber.readSequence());
|
||||
t.equal(ber.readString(), 'objectclass');
|
||||
t.equal(ber.readSequence(), 0x31);
|
||||
t.equal(ber.readString(), 'person');
|
||||
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,76 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var test = require('tap').test;
|
||||
|
||||
var asn1 = require('asn1');
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var BerReader = asn1.BerReader;
|
||||
var BerWriter = asn1.BerWriter;
|
||||
var ModifyResponse;
|
||||
|
||||
|
||||
///--- Tests
|
||||
|
||||
test('load library', function(t) {
|
||||
ModifyResponse = require('../../lib/index').ModifyResponse;
|
||||
t.ok(ModifyResponse);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('new no args', function(t) {
|
||||
t.ok(new ModifyResponse());
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('new with args', function(t) {
|
||||
var res = new ModifyResponse({
|
||||
messageID: 123,
|
||||
status: 0
|
||||
});
|
||||
t.ok(res);
|
||||
t.equal(res.messageID, 123);
|
||||
t.equal(res.status, 0);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('parse', function(t) {
|
||||
var ber = new BerWriter();
|
||||
ber.writeEnumeration(0);
|
||||
ber.writeString('cn=root');
|
||||
ber.writeString('foo');
|
||||
|
||||
var res = new ModifyResponse();
|
||||
t.ok(res._parse(new BerReader(ber.buffer)));
|
||||
t.equal(res.status, 0);
|
||||
t.equal(res.matchedDN, 'cn=root');
|
||||
t.equal(res.errorMessage, 'foo');
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('toBer', function(t) {
|
||||
var res = new ModifyResponse({
|
||||
messageID: 123,
|
||||
status: 3,
|
||||
matchedDN: 'cn=root',
|
||||
errorMessage: 'foo'
|
||||
});
|
||||
t.ok(res);
|
||||
|
||||
var ber = new BerReader(res.toBer());
|
||||
t.ok(ber);
|
||||
t.equal(ber.readSequence(), 0x30);
|
||||
t.equal(ber.readInt(), 123);
|
||||
t.equal(ber.readSequence(), 0x67);
|
||||
t.equal(ber.readEnumeration(), 3);
|
||||
t.equal(ber.readString(), 'cn=root');
|
||||
t.equal(ber.readString(), 'foo');
|
||||
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,116 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var test = require('tap').test;
|
||||
|
||||
var asn1 = require('asn1');
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var BerReader = asn1.BerReader;
|
||||
var BerWriter = asn1.BerWriter;
|
||||
var SearchEntry;
|
||||
var Attribute;
|
||||
var dn;
|
||||
|
||||
///--- Tests
|
||||
|
||||
test('load library', function(t) {
|
||||
SearchEntry = require('../../lib/index').SearchEntry;
|
||||
Attribute = require('../../lib/index').Attribute;
|
||||
dn = require('../../lib/index').dn;
|
||||
t.ok(SearchEntry);
|
||||
t.ok(dn);
|
||||
t.ok(Attribute);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('new no args', function(t) {
|
||||
t.ok(new SearchEntry());
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('new with args', function(t) {
|
||||
var res = new SearchEntry({
|
||||
messageID: 123,
|
||||
objectName: dn.parse('cn=foo, o=test'),
|
||||
attributes: [new Attribute({type: 'cn', vals: ['foo']}),
|
||||
new Attribute({type: 'objectclass', vals: ['person']})]
|
||||
});
|
||||
t.ok(res);
|
||||
t.equal(res.messageID, 123);
|
||||
t.equal(res.dn, 'cn=foo, o=test');
|
||||
t.equal(res.attributes.length, 2);
|
||||
t.equal(res.attributes[0].type, 'cn');
|
||||
t.equal(res.attributes[0].vals[0], 'foo');
|
||||
t.equal(res.attributes[1].type, 'objectclass');
|
||||
t.equal(res.attributes[1].vals[0], 'person');
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('parse', function(t) {
|
||||
var ber = new BerWriter();
|
||||
ber.writeString('cn=foo, o=test');
|
||||
|
||||
ber.startSequence();
|
||||
|
||||
ber.startSequence();
|
||||
ber.writeString('cn');
|
||||
ber.startSequence(0x31);
|
||||
ber.writeString('foo');
|
||||
ber.endSequence();
|
||||
ber.endSequence();
|
||||
|
||||
ber.startSequence();
|
||||
ber.writeString('objectclass');
|
||||
ber.startSequence(0x31);
|
||||
ber.writeString('person');
|
||||
ber.endSequence();
|
||||
ber.endSequence();
|
||||
|
||||
ber.endSequence();
|
||||
|
||||
var res = new SearchEntry();
|
||||
t.ok(res._parse(new BerReader(ber.buffer)));
|
||||
t.equal(res.dn, 'cn=foo, o=test');
|
||||
t.equal(res.attributes.length, 2);
|
||||
t.equal(res.attributes[0].type, 'cn');
|
||||
t.equal(res.attributes[0].vals[0], 'foo');
|
||||
t.equal(res.attributes[1].type, 'objectclass');
|
||||
t.equal(res.attributes[1].vals[0], 'person');
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('toBer', function(t) {
|
||||
var res = new SearchEntry({
|
||||
messageID: 123,
|
||||
objectName: dn.parse('cn=foo, o=test'),
|
||||
attributes: [new Attribute({type: 'cn', vals: ['foo']}),
|
||||
new Attribute({type: 'objectclass', vals: ['person']})]
|
||||
});
|
||||
t.ok(res);
|
||||
|
||||
var ber = new BerReader(res.toBer());
|
||||
t.ok(ber);
|
||||
t.equal(ber.readSequence(), 0x30);
|
||||
t.equal(ber.readInt(), 123);
|
||||
t.equal(ber.readSequence(), 0x64);
|
||||
t.equal(ber.readString(), 'cn=foo, o=test');
|
||||
t.ok(ber.readSequence());
|
||||
|
||||
t.ok(ber.readSequence());
|
||||
t.equal(ber.readString(), 'cn');
|
||||
t.equal(ber.readSequence(), 0x31);
|
||||
t.equal(ber.readString(), 'foo');
|
||||
|
||||
t.ok(ber.readSequence());
|
||||
t.equal(ber.readString(), 'objectclass');
|
||||
t.equal(ber.readSequence(), 0x31);
|
||||
t.equal(ber.readString(), 'person');
|
||||
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,120 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var test = require('tap').test;
|
||||
|
||||
var asn1 = require('asn1');
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var BerReader = asn1.BerReader;
|
||||
var BerWriter = asn1.BerWriter;
|
||||
var SearchRequest;
|
||||
var EqualityFilter;
|
||||
var dn;
|
||||
|
||||
///--- Tests
|
||||
|
||||
test('load library', function(t) {
|
||||
SearchRequest = require('../../lib/index').SearchRequest;
|
||||
EqualityFilter = require('../../lib/index').EqualityFilter;
|
||||
dn = require('../../lib/index').dn;
|
||||
t.ok(SearchRequest);
|
||||
t.ok(EqualityFilter);
|
||||
t.ok(dn);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('new no args', function(t) {
|
||||
t.ok(new SearchRequest());
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('new with args', function(t) {
|
||||
var req = new SearchRequest({
|
||||
baseObject: dn.parse('cn=foo, o=test'),
|
||||
filter: new EqualityFilter({
|
||||
attribute: 'email',
|
||||
value: 'foo@bar.com'
|
||||
}),
|
||||
attributes: ['cn', 'sn']
|
||||
});
|
||||
t.ok(req);
|
||||
t.equal(req.dn.toString(), 'cn=foo, o=test');
|
||||
t.equal(req.filter.toString(), '(email=foo@bar.com)');
|
||||
t.equal(req.attributes.length, 2);
|
||||
t.equal(req.attributes[0], 'cn');
|
||||
t.equal(req.attributes[1], 'sn');
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('parse', function(t) {
|
||||
var f = new EqualityFilter({
|
||||
attribute: 'email',
|
||||
value: 'foo@bar.com'
|
||||
});
|
||||
|
||||
var ber = new BerWriter();
|
||||
ber.writeString('cn=foo,o=test');
|
||||
ber.writeEnumeration(0);
|
||||
ber.writeEnumeration(0);
|
||||
ber.writeInt(1);
|
||||
ber.writeInt(2);
|
||||
ber.writeBoolean(false);
|
||||
ber = f.toBer(ber);
|
||||
|
||||
var req = new SearchRequest();
|
||||
t.ok(req._parse(new BerReader(ber.buffer)));
|
||||
t.equal(req.dn.toString(), 'cn=foo, o=test');
|
||||
t.equal(req.scope, 'base');
|
||||
t.equal(req.derefAliases, 0);
|
||||
t.equal(req.sizeLimit, 1);
|
||||
t.equal(req.timeLimit, 2);
|
||||
t.equal(req.typesOnly, false);
|
||||
t.equal(req.filter.toString(), '(email=foo@bar.com)');
|
||||
t.equal(req.attributes.length, 0);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('toBer', function(t) {
|
||||
var req = new SearchRequest({
|
||||
messageID: 123,
|
||||
baseObject: dn.parse('cn=foo, o=test'),
|
||||
scope: 1,
|
||||
derefAliases: 2,
|
||||
sizeLimit: 10,
|
||||
timeLimit: 20,
|
||||
typesOnly: true,
|
||||
filter: new EqualityFilter({
|
||||
attribute: 'email',
|
||||
value: 'foo@bar.com'
|
||||
}),
|
||||
attributes: ['cn', 'sn']
|
||||
});
|
||||
|
||||
t.ok(req);
|
||||
|
||||
var ber = new BerReader(req.toBer());
|
||||
t.ok(ber);
|
||||
t.equal(ber.readSequence(), 0x30);
|
||||
t.equal(ber.readInt(), 123);
|
||||
t.equal(ber.readSequence(), 0x63);
|
||||
t.equal(ber.readString(), 'cn=foo, o=test');
|
||||
t.equal(ber.readEnumeration(), 1);
|
||||
t.equal(ber.readEnumeration(), 2);
|
||||
t.equal(ber.readInt(), 10);
|
||||
t.equal(ber.readInt(), 20);
|
||||
t.ok(ber.readBoolean());
|
||||
t.equal(ber.readSequence(), 0xa3);
|
||||
t.equal(ber.readString(), 'email');
|
||||
t.equal(ber.readString(), 'foo@bar.com');
|
||||
t.ok(ber.readSequence());
|
||||
t.equal(ber.readString(), 'cn');
|
||||
t.equal(ber.readString(), 'sn');
|
||||
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,76 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var test = require('tap').test;
|
||||
|
||||
var asn1 = require('asn1');
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var BerReader = asn1.BerReader;
|
||||
var BerWriter = asn1.BerWriter;
|
||||
var SearchResponse;
|
||||
|
||||
|
||||
///--- Tests
|
||||
|
||||
test('load library', function(t) {
|
||||
SearchResponse = require('../../lib/index').SearchResponse;
|
||||
t.ok(SearchResponse);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('new no args', function(t) {
|
||||
t.ok(new SearchResponse());
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('new with args', function(t) {
|
||||
var res = new SearchResponse({
|
||||
messageID: 123,
|
||||
status: 0
|
||||
});
|
||||
t.ok(res);
|
||||
t.equal(res.messageID, 123);
|
||||
t.equal(res.status, 0);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('parse', function(t) {
|
||||
var ber = new BerWriter();
|
||||
ber.writeEnumeration(0);
|
||||
ber.writeString('cn=root');
|
||||
ber.writeString('foo');
|
||||
|
||||
var res = new SearchResponse();
|
||||
t.ok(res._parse(new BerReader(ber.buffer)));
|
||||
t.equal(res.status, 0);
|
||||
t.equal(res.matchedDN, 'cn=root');
|
||||
t.equal(res.errorMessage, 'foo');
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('toBer', function(t) {
|
||||
var res = new SearchResponse({
|
||||
messageID: 123,
|
||||
status: 3,
|
||||
matchedDN: 'cn=root',
|
||||
errorMessage: 'foo'
|
||||
});
|
||||
t.ok(res);
|
||||
|
||||
var ber = new BerReader(res.toBer());
|
||||
t.ok(ber);
|
||||
t.equal(ber.readSequence(), 0x30);
|
||||
t.equal(ber.readInt(), 123);
|
||||
t.equal(ber.readSequence(), 0x65);
|
||||
t.equal(ber.readEnumeration(), 3);
|
||||
t.equal(ber.readString(), 'cn=root');
|
||||
t.equal(ber.readString(), 'foo');
|
||||
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,57 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var test = require('tap').test;
|
||||
|
||||
var asn1 = require('asn1');
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var BerReader = asn1.BerReader;
|
||||
var BerWriter = asn1.BerWriter;
|
||||
var UnbindRequest;
|
||||
|
||||
|
||||
///--- Tests
|
||||
|
||||
test('load library', function(t) {
|
||||
UnbindRequest = require('../../lib/index').UnbindRequest;
|
||||
t.ok(UnbindRequest);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('new no args', function(t) {
|
||||
t.ok(new UnbindRequest());
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('new with args', function(t) {
|
||||
var req = new UnbindRequest({});
|
||||
t.ok(req);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('parse', function(t) {
|
||||
var ber = new BerWriter();
|
||||
|
||||
var req = new UnbindRequest();
|
||||
t.ok(req._parse(new BerReader(ber.buffer)));
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('toBer', function(t) {
|
||||
var req = new UnbindRequest({
|
||||
messageID: 123
|
||||
});
|
||||
t.ok(req);
|
||||
|
||||
var ber = new BerReader(req.toBer());
|
||||
t.ok(ber);
|
||||
t.equal(ber.readSequence(), 0x30);
|
||||
t.equal(ber.readInt(), 123);
|
||||
t.end();
|
||||
});
|
|
@ -0,0 +1,73 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var test = require('tap').test;
|
||||
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var url;
|
||||
|
||||
|
||||
|
||||
///--- Tests
|
||||
|
||||
test('load library', function(t) {
|
||||
url = require('../lib/index').url;
|
||||
t.ok(url);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('parse empty', function(t) {
|
||||
var u = url.parse('ldap:///');
|
||||
t.equal(u.hostname, 'localhost');
|
||||
t.equal(u.port, 389);
|
||||
t.ok(!u.DN);
|
||||
t.ok(!u.attributes);
|
||||
t.equal(u.secure, false);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('parse hostname', function(t) {
|
||||
var u = url.parse('ldap://example.com/');
|
||||
t.equal(u.hostname, 'example.com');
|
||||
t.equal(u.port, 389);
|
||||
t.ok(!u.DN);
|
||||
t.ok(!u.attributes);
|
||||
t.equal(u.secure, false);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('parse host and port', function(t) {
|
||||
var u = url.parse('ldap://example.com:1389/');
|
||||
t.equal(u.hostname, 'example.com');
|
||||
t.equal(u.port, 1389);
|
||||
t.ok(!u.DN);
|
||||
t.ok(!u.attributes);
|
||||
t.equal(u.secure, false);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('parse full', function(t) {
|
||||
|
||||
var u = url.parse('ldaps://ldap.example.com:1389/dc=example%20,dc=com' +
|
||||
'?cn,sn?sub?(cn=Babs%20Jensen)');
|
||||
|
||||
t.equal(u.secure, true);
|
||||
t.equal(u.hostname, 'ldap.example.com');
|
||||
t.equal(u.port, 1389);
|
||||
t.equal(u.DN, 'dc=example ,dc=com');
|
||||
t.ok(u.attributes);
|
||||
t.equal(u.attributes.length, 2);
|
||||
t.equal(u.attributes[0], 'cn');
|
||||
t.equal(u.attributes[1], 'sn');
|
||||
t.equal(u.scope, 'sub');
|
||||
t.equal(u.filter, '(cn=Babs Jensen)');
|
||||
|
||||
t.end();
|
||||
});
|
Loading…
Reference in New Issue