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