function AbandonRequest(options) {
options = options || {};
assert.object(options);
assert.optionalNumber(options.abandonID);
options.protocolOp = Protocol.LDAP_REQ_ABANDON;
LDAPMessage.call(this, options);
this.abandonID = options.abandonID || 0;
}n/a
function AbandonResponse(options) {
options = options || {};
assert.object(options);
options.protocolOp = 0;
LDAPMessage.call(this, options);
}n/a
function AbandonedError(message) {
LDAPError.call(this, message, null, AbandonedError);
}n/a
function AddRequest(options) {
options = options || {};
assert.object(options);
lassert.optionalStringDN(options.entry);
lassert.optionalArrayOfAttribute(options.attributes);
options.protocolOp = Protocol.LDAP_REQ_ADD;
LDAPMessage.call(this, options);
this.entry = options.entry || null;
this.attributes = options.attributes ? options.attributes.slice(0) : [];
}n/a
function AddResponse(options) {
options = options || {};
assert.object(options);
options.protocolOp = Protocol.LDAP_REP_ADD;
LDAPResult.call(this, options);
}n/a
AdminLimitExceededError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
AffectsMultipleDsasError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
AliasDerefProblemError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
AliasProblemError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function AndFilter(options) {
parents.AndFilter.call(this, options);
}n/a
function AndFilter(options) {
if (typeof (options) === 'object') {
assert.arrayOfObject(options.filters, 'options.filters');
} else {
options = {};
}
this.__defineGetter__('type', function () { return 'and'; });
this.filters = options.filters ? options.filters.slice() : [];
var self = this;
this.__defineGetter__('json', function () {
return {
type: 'And',
filters: self.filters
};
});
}n/a
function ApproximateFilter(options) {
parents.ApproximateFilter.call(this, options);
}n/a
function ApproximateFilter(options) {
if (typeof (options) === 'object') {
assert.string(options.attribute, 'options.attribute');
assert.string(options.value, 'options.value');
this.attribute = options.attribute;
this.value = options.value;
}
var self = this;
this.__defineGetter__('type', function () { return 'approx'; });
this.__defineGetter__('json', function () {
return {
type: 'ApproximateMatch',
attribute: self.attribute,
value: self.value
};
});
}n/a
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');
} else {
options = {};
}
this.type = options.type || '';
this._vals = [];
if (options.vals !== undefined && options.vals !== null)
this.vals = options.vals;
}n/a
AttributeOrValueExistsError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
AuthMethodNotSupportedError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function BindRequest(options) {
options = options || {};
assert.object(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 || '';
}n/a
function BindResponse(options) {
options = options || {};
assert.object(options);
options.protocolOp = Protocol.LDAP_REP_BIND;
LDAPResult.call(this, options);
}n/a
BusyError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function Change(options) {
if (options) {
assert.object(options);
assert.optionalString(options.operation);
} else {
options = {};
}
this._modification = false;
this.operation = options.operation || options.type || 'add';
this.modification = options.modification || {};
}n/a
function Client(options) {
assert.ok(options);
EventEmitter.call(this, options);
var self = this;
var _url;
if (options.url)
_url = url.parse(options.url);
this.host = _url ? _url.hostname : undefined;
this.port = _url ? _url.port : false;
this.secure = _url ? _url.secure : false;
this.url = _url;
this.tlsOptions = options.tlsOptions;
this.socketPath = options.socketPath || false;
this.log = options.log.child({clazz: 'Client'}, true);
this.timeout = parseInt((options.timeout || 0), 10);
this.connectTimeout = parseInt((options.connectTimeout || 0), 10);
this.idleTimeout = parseInt((options.idleTimeout || 0), 10);
if (options.reconnect) {
// Fall back to defaults if options.reconnect === true
var rOpts = (typeof (options.reconnect) === 'object') ?
options.reconnect : {};
this.reconnect = {
initialDelay: parseInt(rOpts.initialDelay || 100, 10),
maxDelay: parseInt(rOpts.maxDelay || 10000, 10),
failAfter: parseInt(rOpts.failAfter, 10) || Infinity
};
}
this.strictDN = (options.strictDN !== undefined) ? options.strictDN : true;
this.queue = new RequestQueue({
size: parseInt((options.queueSize || 0), 10),
timeout: parseInt((options.queueTimeout || 0), 10)
});
if (options.queueDisable) {
this.queue.freeze();
}
// Implicitly configure setup action to bind the client if bindDN and
// bindCredentials are passed in. This will more closely mimic PooledClient
// auto-login behavior.
if (options.bindDN !== undefined &&
options.bindCredentials !== undefined) {
this.on('setup', function (clt, cb) {
clt.bind(options.bindDN, options.bindCredentials, function (err) {
if (err) {
self.emit('error', err);
}
cb(err);
});
});
}
this._socket = null;
this.connected = false;
this.connect();
}n/a
CompareFalseError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function CompareRequest(options) {
options = options || {};
assert.object(options);
assert.optionalString(options.attribute);
assert.optionalString(options.value);
lassert.optionalStringDN(options.entry);
options.protocolOp = Protocol.LDAP_REQ_COMPARE;
LDAPMessage.call(this, options);
this.entry = options.entry || null;
this.attribute = options.attribute || '';
this.value = options.value || '';
}n/a
function CompareResponse(options) {
options = options || {};
assert.object(options);
options.protocolOp = Protocol.LDAP_REP_COMPARE;
LDAPResult.call(this, options);
}n/a
CompareTrueError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
ConfidentialityRequiredError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function ConnectionError(message) {
LDAPError.call(this, message, null, ConnectionError);
}n/a
ConstraintViolationError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function Control(options) {
assert.optionalObject(options);
options = options || {};
assert.optionalString(options.type);
assert.optionalBool(options.criticality);
if (options.value) {
assert.buffer(options.value);
}
this.type = options.type || '';
this.criticality = options.critical || options.criticality || false;
this.value = options.value || null;
}n/a
function DN(rdns) {
assert.optionalArrayOfObject(rdns, '[object] required');
this.rdns = rdns ? rdns.slice() : [];
this._format = {};
}n/a
function DeleteRequest(options) {
options = options || {};
assert.object(options);
lassert.optionalStringDN(options.entry);
options.protocolOp = Protocol.LDAP_REQ_DELETE;
LDAPMessage.call(this, options);
this.entry = options.entry || null;
}n/a
function DeleteResponse(options) {
options = options || {};
assert.object(options);
options.protocolOp = Protocol.LDAP_REP_DELETE;
LDAPResult.call(this, options);
}n/a
EntryAlreadyExistsError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function EntryChangeNotificationControl(options) {
assert.optionalObject(options);
options = options || {};
options.type = EntryChangeNotificationControl.OID;
if (options.value) {
if (Buffer.isBuffer(options.value)) {
this.parse(options.value);
} else if (typeof (options.value) === 'object') {
this._value = options.value;
} else {
throw new TypeError('options.value must be a Buffer or Object');
}
options.value = null;
}
Control.call(this, options);
}n/a
function EqualityFilter(options) {
parents.EqualityFilter.call(this, options);
}n/a
function EqualityFilter(options) {
if (typeof (options) === 'object') {
assert.string(options.attribute, 'options.attribute');
this.attribute = options.attribute;
// Prefer Buffers over strings to make filter cloning easier
if (options.raw) {
this.raw = options.raw;
} else {
this.raw = new Buffer(options.value);
}
} else {
this.raw = new Buffer(0);
}
var self = this;
this.__defineGetter__('type', function () { return 'equal'; });
this.__defineGetter__('value', function () {
return (Buffer.isBuffer(self.raw)) ? self.raw.toString() : self.raw;
});
this.__defineSetter__('value', function (data) {
if (typeof (data) === 'string') {
self.raw = new Buffer(data);
} else if (Buffer.isBuffer(data)) {
self.raw = new Buffer(data.length);
data.copy(self.raw);
} else {
self.raw = data;
}
});
this.__defineGetter__('json', function () {
return {
type: 'EqualityMatch',
attribute: self.attribute,
value: self.value
};
});
}n/a
function ExtendedRequest(options) {
options = options || {};
assert.object(options);
assert.optionalString(options.requestName);
if (options.requestValue &&
!(Buffer.isBuffer(options.requestValue) ||
typeof (options.requestValue) === 'string')) {
throw new TypeError('options.requestValue must be a buffer or a string');
}
options.protocolOp = Protocol.LDAP_REQ_EXTENSION;
LDAPMessage.call(this, options);
this.requestName = options.requestName || '';
this.requestValue = options.requestValue;
}n/a
function ExtendedResponse(options) {
options = options || {};
assert.object(options);
assert.optionalString(options.responseName);
assert.optionalString(options.responsevalue);
this.responseName = options.responseName || undefined;
this.responseValue = options.responseValue || undefined;
options.protocolOp = Protocol.LDAP_REP_EXTENSION;
LDAPResult.call(this, options);
}n/a
function ExtensibleFilter(options) {
parents.ExtensibleFilter.call(this, options);
}n/a
function ExtensibleFilter(options) {
if (typeof (options) === 'object') {
assert.optionalString(options.rule, 'options.rule');
assert.optionalString(options.matchType, 'options.matchType');
assert.optionalString(options.attribute, 'options.attribute');
assert.optionalString(options.value, 'options.value');
} else {
options = {};
}
if (options.matchType !== undefined) {
this.matchType = options.matchType;
} else {
this.matchType = options.attribute;
}
this.dnAttributes = options.dnAttributes || false;
this.rule = options.rule;
this.value = (options.value !== undefined) ? options.value : '';
var self = this;
this.__defineGetter__('type', function () { return 'ext'; });
this.__defineGetter__('json', function () {
return {
type: 'ExtensibleMatch',
matchRule: self.rule,
matchType: self.matchType,
matchValue: self.value,
dnAttributes: self.dnAttributes
};
});
this.__defineGetter__('matchingRule', function () {
return self.rule;
});
this.__defineGetter__('matchValue', function () {
return self.value;
});
this.__defineGetter__('attribute', function () {
return this.matchType;
});
this.__defineSetter__('attribute', function (value) {
this.matchType = value;
});
}n/a
function GreaterThanEqualsFilter(options) {
parents.GreaterThanEqualsFilter.call(this, options);
}n/a
function GreaterThanEqualsFilter(options) {
if (typeof (options) === 'object') {
assert.string(options.attribute, 'options.attribute');
assert.string(options.value, 'options.value');
this.attribute = options.attribute;
this.value = options.value;
}
var self = this;
this.__defineGetter__('type', function () { return 'ge'; });
this.__defineGetter__('json', function () {
return {
type: 'GreaterThanEqualsMatch',
attribute: self.attribute,
value: self.value
};
});
}n/a
InappropriateAuthenticationError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
InappropriateMatchingError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
InsufficientAccessRightsError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
InvalidAttributeSyntaxError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
InvalidCredentialsError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
InvalidDnSyntaxError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function LDAPError(message, dn, caller) {
if (Error.captureStackTrace)
Error.captureStackTrace(this, caller || LDAPError);
this.lde_message = message;
this.lde_dn = dn;
}n/a
function Error() { [native code] }n/a
function LDAPMessage(options) {
assert.object(options);
this.messageID = options.messageID || 0;
this.protocolOp = options.protocolOp || undefined;
this.controls = options.controls ? options.controls.slice(0) : [];
this.log = options.log;
}n/a
function LDAPResult(options) {
options = options || {};
assert.object(options);
assert.optionalNumber(options.status);
assert.optionalString(options.matchedDN);
assert.optionalString(options.errorMessage);
assert.optionalArrayOfString(options.referrals);
LDAPMessage.call(this, options);
this.status = options.status || 0; // LDAP SUCCESS
this.matchedDN = options.matchedDN || '';
this.errorMessage = options.errorMessage || '';
this.referrals = options.referrals || [];
this.connection = options.connection || null;
}n/a
function LessThanEqualsFilter(options) {
parents.LessThanEqualsFilter.call(this, options);
}n/a
function LessThanEqualsFilter(options) {
if (typeof (options) === 'object') {
assert.string(options.attribute, 'options.attribute');
assert.string(options.value, 'options.attribute');
this.attribute = options.attribute;
this.value = options.value;
}
var self = this;
this.__defineGetter__('type', function () { return 'le'; });
this.__defineGetter__('json', function () {
return {
type: 'LessThanEqualsMatch',
attribute: self.attribute,
value: self.value
};
});
}n/a
LoopDetectError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function ModifyDNRequest(options) {
options = options || {};
assert.object(options);
assert.optionalBool(options.deleteOldRdn);
lassert.optionalStringDN(options.entry);
lassert.optionalDN(options.newRdn);
lassert.optionalDN(options.newSuperior);
options.protocolOp = Protocol.LDAP_REQ_MODRDN;
LDAPMessage.call(this, options);
this.entry = options.entry || null;
this.newRdn = options.newRdn || null;
this.deleteOldRdn = options.deleteOldRdn || true;
this.newSuperior = options.newSuperior || null;
}n/a
function ModifyDNResponse(options) {
options = options || {};
assert.object(options);
options.protocolOp = Protocol.LDAP_REP_MODRDN;
LDAPResult.call(this, options);
}n/a
function ModifyRequest(options) {
options = options || {};
assert.object(options);
lassert.optionalStringDN(options.object);
lassert.optionalArrayOfAttribute(options.attributes);
options.protocolOp = Protocol.LDAP_REQ_MODIFY;
LDAPMessage.call(this, options);
this.object = options.object || null;
this.changes = options.changes ? options.changes.slice(0) : [];
}n/a
function ModifyResponse(options) {
options = options || {};
assert.object(options);
options.protocolOp = Protocol.LDAP_REP_MODIFY;
LDAPResult.call(this, options);
}n/a
NamingViolationError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
NoSuchAttributeError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
NoSuchObjectError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
NotAllowedOnNonLeafError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
NotAllowedOnRdnError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function NotFilter(options) {
parents.NotFilter.call(this, options);
}n/a
function NotFilter(options) {
if (typeof (options) === 'object') {
assert.object(options.filter, 'options.filter');
} else {
options = {};
}
this.filter = options.filter || {};
var self = this;
this.__defineGetter__('type', function () { return 'not'; });
this.__defineGetter__('json', function () {
return {
type: 'Not',
filter: self.filter
};
});
}n/a
ObjectclassModsProhibitedError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
ObjectclassViolationError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
OperationsError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function OrFilter(options) {
parents.OrFilter.call(this, options);
}n/a
function OrFilter(options) {
if (typeof (options) === 'object') {
assert.arrayOfObject(options.filters, 'options.filters');
} else {
options = {};
}
this.filters = options.filters ? options.filters.slice() : [];
var self = this;
this.__defineGetter__('type', function () { return 'or'; });
this.__defineGetter__('json', function () {
return {
type: 'Or',
filters: self.filters
};
});
}n/a
OtherError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function PagedResultsControl(options) {
assert.optionalObject(options);
options = options || {};
options.type = PagedResultsControl.OID;
if (options.value) {
if (Buffer.isBuffer(options.value)) {
this.parse(options.value);
} else if (typeof (options.value) === 'object') {
this._value = options.value;
} else {
throw new TypeError('options.value must be a Buffer or Object');
}
options.value = null;
}
Control.call(this, options);
}n/a
function Parser(options) {
assert.object(options);
assert.object(options.log);
EventEmitter.call(this);
this.buffer = null;
this.log = options.log;
}n/a
function PersistentSearch() {
this.clientList = [];
}n/a
function PersistentSearchControl(options) {
assert.optionalObject(options);
options = options || {};
options.type = PersistentSearchControl.OID;
if (options.value) {
if (Buffer.isBuffer(options.value)) {
this.parse(options.value);
} else if (typeof (options.value) === 'object') {
this._value = options.value;
} else {
throw new TypeError('options.value must be a Buffer or Object');
}
options.value = null;
}
Control.call(this, options);
}n/a
function PresenceFilter(options) {
parents.PresenceFilter.call(this, options);
}n/a
function PresenceFilter(options) {
if (typeof (options) === 'object') {
assert.string(options.attribute, 'options.attribute');
this.attribute = options.attribute;
}
var self = this;
this.__defineGetter__('type', function () { return 'present'; });
this.__defineGetter__('json', function () {
return {
type: 'PresenceMatch',
attribute: self.attribute
};
});
}n/a
ProtocolError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
ProxiedAuthorizationDeniedError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function RDN(obj) {
var self = this;
this.attrs = {};
if (obj) {
Object.keys(obj).forEach(function (k) {
self.set(k, obj[k]);
});
}
}n/a
ReferralError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
SaslBindInProgressError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function SearchEntry(options) {
options = options || {};
assert.object(options);
lassert.optionalStringDN(options.objectName);
options.protocolOp = Protocol.LDAP_REP_SEARCH_ENTRY;
LDAPMessage.call(this, options);
this.objectName = options.objectName || null;
this.setAttributes(options.attributes || []);
}n/a
function SearchReference(options) {
options = options || {};
assert.object(options);
options.protocolOp = Protocol.LDAP_REP_SEARCH_REF;
LDAPMessage.call(this, options);
this.uris = options.uris || [];
}n/a
function SearchRequest(options) {
options = options || {};
assert.object(options);
options.protocolOp = Protocol.LDAP_REQ_SEARCH;
LDAPMessage.call(this, options);
if (options.baseObject !== undefined) {
this.baseObject = options.baseObject;
} else {
this.baseObject = dn.parse('');
}
this.scope = options.scope || 'base';
this.derefAliases = options.derefAliases || Protocol.NEVER_DEREF_ALIASES;
this.sizeLimit = options.sizeLimit || 0;
this.timeLimit = options.timeLimit || 0;
this.typesOnly = options.typesOnly || false;
this.filter = options.filter || null;
this.attributes = options.attributes ? options.attributes.slice(0) : [];
}n/a
function SearchResponse(options) {
options = options || {};
assert.object(options);
options.protocolOp = Protocol.LDAP_REP_SEARCH;
LDAPResult.call(this, options);
this.attributes = options.attributes ? options.attributes.slice() : [];
this.notAttributes = [];
this.sentEntries = 0;
}n/a
function Server(options) {
if (options) {
if (typeof (options) !== 'object')
throw new TypeError('options (object) required');
if (typeof (options.log) !== 'object')
throw new TypeError('options.log must be an object');
if (options.certificate || options.key) {
if (!(options.certificate && options.key) ||
(typeof (options.certificate) !== 'string' &&
!Buffer.isBuffer(options.certificate)) ||
(typeof (options.key) !== 'string' &&
!Buffer.isBuffer(options.key))) {
throw new TypeError('options.certificate and options.key ' +
'(string or buffer) are both required for TLS');
}
}
} else {
options = {};
}
var self = this;
EventEmitter.call(this, options);
this._chain = [];
this.log = options.log;
this.strictDN = (options.strictDN !== undefined) ? options.strictDN : true;
var log = this.log;
function setupConnection(c) {
assert.ok(c);
if (c.type === 'unix') {
c.remoteAddress = self.server.path;
c.remotePort = c.fd;
} else if (c.socket) {
// TLS
c.remoteAddress = c.socket.remoteAddress;
c.remotePort = c.socket.remotePort;
}
var rdn = new dn.RDN({cn: 'anonymous'});
c.ldap = {
id: c.remoteAddress + ':' + c.remotePort,
config: options,
_bindDN: new DN([rdn])
};
c.addListener('timeout', function () {
log.trace('%s timed out', c.ldap.id);
c.destroy();
});
c.addListener('end', function () {
log.trace('%s shutdown', c.ldap.id);
});
c.addListener('error', function (err) {
log.warn('%s unexpected connection error', c.ldap.id, err);
self.emit('clientError', err);
c.destroy();
});
c.addListener('close', function (had_err) {
log.trace('%s close; had_err=%j', c.ldap.id, had_err);
c.end();
});
c.ldap.__defineGetter__('bindDN', function () {
return c.ldap._bindDN;
});
c.ldap.__defineSetter__('bindDN', function (val) {
if (!(val instanceof DN))
throw new TypeError('DN required');
c.ldap._bindDN = val;
return val;
});
return c;
}
function newConnection(c) {
setupConnection(c);
log.trace('new connection from %s', c.ldap.id);
dtrace.fire('server-connection', function () {
return [c.remoteAddress];
});
c.parser = new Parser({
log: options.log
});
c.parser.on('message', function (req) {
req.connection = c;
req.logId = c.ldap.id + '::' + req.messageID;
req.startTime = new Date().getTime();
if (log.debug())
log.debug('%s: message received: req=%j', c.ldap.id, req.json);
var res = getResponse(req);
if (!res) {
log.warn('Unimplemented server method: %s', req.type);
c.destroy();
return false;
}
// parse string DNs for routing/etc
try {
switch (req.protocolOp) {
case Protocol.LDAP_REQ_BIND:
req.name = dn.parse(req.name);
break;
case Protocol.LDAP_REQ_ADD:
case Protocol.LDAP_REQ_COMPARE:
case Protocol.LDAP_REQ_DELETE:
req.entry = dn.parse(req.entry);
break;
case Protocol.LDAP_REQ_MODIFY:
req.object = dn.parse(req.object);
break;
case Protocol.LDAP_REQ_MODRDN:
req.entry = dn.parse(req.entry);
// TODO: handle newRdn/Superior
break;
case Protocol.LDAP_REQ_SEARCH:
req.baseObject = dn.parse(req.baseObject);
break;
default:
break;
}
} catch (e) {
if (self.strictDN) {
return res.end(errors.LDAP_INVALID_DN_SYNTAX);
}
}
res.connection = c;
res.logId = req.logId;
res.requestDN = req.dn;
var chain = self._getHandlerChain(req, res);
var i = 0;
return function (err) {
function sendError(err) {
res.status = err.code || errors.LDAP_OPERATIONS_ERROR;
res.matchedDN = req.suffix ? req.suffi ...n/a
function ServerSideSortingRequestControl(options) {
assert.optionalObject(options);
options = options || {};
options.type = ServerSideSortingRequestControl.OID;
if (options.value) {
if (Buffer.isBuffer(options.value)) {
this.parse(options.value);
} else if (Array.isArray(options.value)) {
assert.arrayOfObject(options.value, 'options.value must be Objects');
for (var i = 0; i < options.value.length; i++) {
if (!options.value[i].hasOwnProperty('attributeType')) {
throw new Error('Missing required key: attributeType');
}
}
this._value = options.value;
} else if (typeof (options.value) === 'object') {
if (!options.value.hasOwnProperty('attributeType')) {
throw new Error('Missing required key: attributeType');
}
this._value = [options.value];
} else {
throw new TypeError('options.value must be a Buffer, Array or Object');
}
options.value = null;
}
Control.call(this, options);
}n/a
function ServerSideSortingResponseControl(options) {
assert.optionalObject(options);
options = options || {};
options.type = ServerSideSortingResponseControl.OID;
options.criticality = false;
if (options.value) {
if (Buffer.isBuffer(options.value)) {
this.parse(options.value);
} else if (typeof (options.value) === 'object') {
if (VALID_CODES.indexOf(options.value.result) === -1) {
throw new Error('Invalid result code');
}
if (options.value.failedAttribute &&
typeof (options.value.failedAttribute) !== 'string') {
throw new Error('failedAttribute must be String');
}
this._value = options.value;
} else {
throw new TypeError('options.value must be a Buffer or Object');
}
options.value = null;
}
Control.call(this, options);
}n/a
SizeLimitExceededError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
StrongAuthRequiredError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function SubstringFilter(options) {
parents.SubstringFilter.call(this, options);
}n/a
function SubstringFilter(options) {
if (typeof (options) === 'object') {
assert.string(options.attribute, 'options.attribute');
this.attribute = options.attribute;
this.initial = options.initial;
this.any = options.any ? options.any.slice(0) : [];
this.final = options.final;
} else {
this.any = [];
}
var self = this;
this.__defineGetter__('type', function () { return 'substring'; });
this.__defineGetter__('json', function () {
return {
type: 'SubstringMatch',
initial: self.initial,
any: self.any,
final: self.final
};
});
}n/a
TimeLimitExceededError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function TimeoutError(message) {
LDAPError.call(this, message, null, TimeoutError);
}n/a
UnavailableCriticalExtensionError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
UnavailableError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function UnbindRequest(options) {
options = options || {};
assert.object(options);
options.protocolOp = Protocol.LDAP_REQ_UNBIND;
LDAPMessage.call(this, options);
}n/a
function UnbindResponse(options) {
options = options || {};
assert.object(options);
options.protocolOp = 0;
LDAPMessage.call(this, options);
}n/a
UndefinedAttributeTypeError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
UnwillingToPerformError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function createClient(options) {
if (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 (!xor(options.url, options.socketPath))
throw new TypeError('options.url ^ options.socketPath (String) required');
if (!options.log)
options.log = DEF_LOG;
if (typeof (options.log) !== 'object')
throw new TypeError('options.log must be an object');
return new Client(options);
}n/a
createServer = function (options) {
if (options === undefined)
options = {};
if (typeof (options) !== 'object')
throw new TypeError('options (object) required');
if (!options.log) {
options.log = new Logger({
name: 'ldapjs',
component: 'client',
stream: process.stderr
});
}
return new Server(options);
}...
## Usage
For full docs, head on over to <http://ldapjs.org>.
```javascript
var ldap = require('ldapjs');
var server = ldap.createServer();
server.search('dc=example', function(req, res, next) {
var obj = {
dn: req.dn.toString(),
attributes: {
objectclass: ['organization', 'top'],
o: 'example'
...function getControl(ber) {
assert.ok(ber);
if (ber.readSequence() === null)
return null;
var type;
var opts = {
criticality: false,
value: null
};
if (ber.length) {
var end = ber.offset + ber.length;
type = ber.readString();
if (ber.offset < end) {
if (ber.peek() === Ber.Boolean)
opts.criticality = ber.readBoolean();
}
if (ber.offset < end)
opts.value = ber.readString(Ber.OctetString, true);
}
var control;
switch (type) {
case PersistentSearchControl.OID:
control = new PersistentSearchControl(opts);
break;
case EntryChangeNotificationControl.OID:
control = new EntryChangeNotificationControl(opts);
break;
case PagedResultsControl.OID:
control = new PagedResultsControl(opts);
break;
case ServerSideSortingRequestControl.OID:
control = new ServerSideSortingRequestControl(opts);
break;
case ServerSideSortingResponseControl.OID:
control = new ServerSideSortingResponseControl(opts);
break;
default:
opts.type = type;
control = new Control(opts);
break;
}
return control;
}n/a
getError = function (res) {
assert.ok(res instanceof LDAPResult, '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);
}n/a
getMessage = function (code) {
assert.number(code, 'code (number) required');
var errObj = ERRORS[code];
return (errObj && errObj.message ? errObj.message : '');
}n/a
function isFilter(filter) {
if (!filter || typeof (filter) !== 'object') {
return false;
}
// Do our best to duck-type it
if (typeof (filter.toBer) === 'function' &&
typeof (filter.matches) === 'function' &&
TYPES[filter.type] !== undefined) {
return true;
}
return false;
}n/a
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();
var order = 0;
rdn.spLead = trim();
while (cur < len) {
var opts = {
order: order
};
var attr = parseAttrType();
trim();
if (cur >= len || name[cur++] !== '=')
throw invalidDN(name);
trim();
// Parameters about RDN value are set in 'opts' by parseAttrValue
var value = parseAttrValue(opts);
rdn.set(attr, value, opts);
rdn.spTrail = trim();
if (cur >= len || name[cur] !== '+')
break;
++cur;
++order;
}
return rdn;
}
function trim() {
var count = 0;
while ((cur < len) && isWhitespace(name[cur])) {
++cur;
count++;
}
return count;
}
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(opts) {
if (cur < len && name[cur] == '#') {
opts.binary = true;
return parseBinaryAttrValue();
} else if (cur < len && name[cur] == '"') {
opts.quoted = true;
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 str = '';
++cur; // Consume the first quote
while ((cur < len) && name[cur] != '"') {
if (name[cur] === '\\')
cur++;
str += name[cur++];
}
if (cur++ >= len) // no closing quote
throw invalidDN(name);
return str;
}
function parseStringAttrValue() {
var beg = cur;
var str = '';
var esc = -1;
while ((cur < len) && !atTerminator()) {
if (name[cur] === '\\') {
// Consume the backslash and mark its place just in case it's escaping
// whitespace which needs to be preserved.
esc = cur++;
}
if (cur === len) // backslash followed by nothing
throw invalidDN(name);
str += name[cur++];
}
// Trim off (unescaped) trailing whitespace and rewind cursor to the end of
// the AttrValue to record whitespace length.
for (; cur > beg; cur--) {
if (!isWhitespace(name[cur - 1]) || (esc === (cur - 1)))
break;
}
return str.slice(0, cur - beg);
}
function atTerminator() {
return (cur < len &&
(name[cur] === ',' ||
name[cur] === ';' ||
name[cur] === '+'));
}
var rdns = [];
// Short-circuit for empty DNs
if (len === 0)
return new DN(rdns);
rdns.push(parseRdn());
while (cur < len) {
if (name[cur] === ',' || name[cur] === ';') {
++cur;
rdns.push(parseRdn());
} else {
throw invalidDN(name);
}
}
return new DN(rdns);
}n/a
function parseString(str) {
var generic = parents.parse(str);
// The filter object(s) return from ldap-filter.parse lack the toBer/parse
// decoration that native ldapjs filter possess. cloneFilter adds that back.
return cloneFilter(generic);
}n/a
parseURL = 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)');
u.secure = (u.protocol === 'ldaps:');
if (!u.hostname)
u.hostname = 'localhost';
if (!u.port) {
u.port = (u.secure ? 636 : 389);
} else {
u.port = parseInt(u.port, 10);
}
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 = filter.parseString('(objectclass=*)');
else
u.filter = filter.parseString(u.filter);
}
return u;
}n/a
function SearchPager(opts) {
assert.object(opts);
assert.func(opts.callback);
assert.number(opts.pageSize);
EventEmitter.call(this, {});
this.callback = opts.callback;
this.controls = opts.controls;
this.pageSize = opts.pageSize;
this.pagePause = opts.pagePause;
this.controls.forEach(function (control) {
if (control.type === PagedControl.OID) {
// The point of using SearchPager is not having to do this.
// Toss an error if the pagedResultsControl is present
throw new Error('redundant pagedResultControl');
}
});
this.finished = false;
this.started = false;
var emitter = new EventEmitter();
emitter.on('searchEntry', this.emit.bind(this, 'searchEntry'));
emitter.on('end', this._onEnd.bind(this));
emitter.on('error', this._onError.bind(this));
this.childEmitter = emitter;
}n/a
function AbandonRequest(options) {
options = options || {};
assert.object(options);
assert.optionalNumber(options.abandonID);
options.protocolOp = Protocol.LDAP_REQ_ABANDON;
LDAPMessage.call(this, options);
this.abandonID = options.abandonID || 0;
}n/a
function LDAPMessage(options) {
assert.object(options);
this.messageID = options.messageID || 0;
this.protocolOp = options.protocolOp || undefined;
this.controls = options.controls ? options.controls.slice(0) : [];
this.log = options.log;
}n/a
_json = function (j) {
assert.ok(j);
j.abandonID = this.abandonID;
return j;
}n/a
_parse = function (ber, length) {
assert.ok(ber);
assert.ok(length);
// What a PITA - have to replicate ASN.1 integer logic to work around the
// way abandon is encoded and the way ldapjs framework handles "normal"
// messages
var buf = ber.buffer;
var offset = 0;
var value = 0;
var fb = buf[offset++];
value = fb & 0x7F;
for (var i = 1; i < length; i++) {
value <<= 8;
value |= (buf[offset++] & 0xff);
}
if ((fb & 0x80) == 0x80)
value = -value;
ber._offset += length;
this.abandonID = value;
return true;
}n/a
_toBer = function (ber) {
assert.ok(ber);
var i = this.abandonID;
var sz = 4;
while ((((i & 0xff800000) === 0) || ((i & 0xff800000) === 0xff800000)) &&
(sz > 1)) {
sz--;
i <<= 8;
}
assert.ok(sz <= 4);
while (sz-- > 0) {
ber.writeByte((i & 0xff000000) >> 24);
i <<= 8;
}
return ber;
}...
function mixin(target) {
target.prototype.toBer = function toBer(ber) {
if (!ber || !(ber instanceof BerWriter))
throw new TypeError('ber (BerWriter) required');
ber.startSequence(TYPES[this.type]);
ber = this._toBer(ber);
ber.endSequence();
return ber;
};
}
module.exports = {
isFilter: isFilter,
...function AbandonResponse(options) {
options = options || {};
assert.object(options);
options.protocolOp = 0;
LDAPMessage.call(this, options);
}n/a
function LDAPResult(options) {
options = options || {};
assert.object(options);
assert.optionalNumber(options.status);
assert.optionalString(options.matchedDN);
assert.optionalString(options.errorMessage);
assert.optionalArrayOfString(options.referrals);
LDAPMessage.call(this, options);
this.status = options.status || 0; // LDAP SUCCESS
this.matchedDN = options.matchedDN || '';
this.errorMessage = options.errorMessage || '';
this.referrals = options.referrals || [];
this.connection = options.connection || null;
}n/a
_json = function (j) {
return j;
}n/a
end = function (status) {}...
o: 'example'
}
};
if (req.filter.matches(obj.attributes))
res.send(obj);
res.end();
});
server.listen(1389, function() {
console.log('ldapjs listening at ' + server.url);
});
```
...function AbandonedError(message) {
LDAPError.call(this, message, null, AbandonedError);
}n/a
function LDAPError(message, dn, caller) {
if (Error.captureStackTrace)
Error.captureStackTrace(this, caller || LDAPError);
this.lde_message = message;
this.lde_dn = dn;
}n/a
function AddRequest(options) {
options = options || {};
assert.object(options);
lassert.optionalStringDN(options.entry);
lassert.optionalArrayOfAttribute(options.attributes);
options.protocolOp = Protocol.LDAP_REQ_ADD;
LDAPMessage.call(this, options);
this.entry = options.entry || null;
this.attributes = options.attributes ? options.attributes.slice(0) : [];
}n/a
function LDAPMessage(options) {
assert.object(options);
this.messageID = options.messageID || 0;
this.protocolOp = options.protocolOp || undefined;
this.controls = options.controls ? options.controls.slice(0) : [];
this.log = options.log;
}n/a
_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;
}n/a
_parse = function (ber) {
assert.ok(ber);
this.entry = ber.readString();
ber.readSequence();
var end = ber.offset + ber.length;
while (ber.offset < end) {
var a = new Attribute();
a.parse(ber);
a.type = a.type.toLowerCase();
if (a.type === 'objectclass') {
for (var i = 0; i < a.vals.length; i++)
a.vals[i] = a.vals[i].toLowerCase();
}
this.attributes.push(a);
}
this.attributes.sort(Attribute.compare);
return true;
}n/a
_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;
}...
function mixin(target) {
target.prototype.toBer = function toBer(ber) {
if (!ber || !(ber instanceof BerWriter))
throw new TypeError('ber (BerWriter) required');
ber.startSequence(TYPES[this.type]);
ber = this._toBer(ber);
ber.endSequence();
return ber;
};
}
module.exports = {
isFilter: isFilter,
...addAttribute = function (attr) {
if (!(attr instanceof Attribute))
throw new TypeError('attribute (Attribute) required');
return this.attributes.push(attr);
}n/a
attributeNames = function () {
var attrs = [];
for (var i = 0; i < this.attributes.length; i++)
attrs.push(this.attributes[i].type.toLowerCase());
return attrs;
}n/a
getAttribute = function (name) {
if (!name || typeof (name) !== 'string')
throw new TypeError('attribute name (string) required');
name = name.toLowerCase();
for (var i = 0; i < this.attributes.length; i++) {
if (this.attributes[i].type === name)
return this.attributes[i];
}
return null;
}n/a
indexOf = function (attr) {
if (!attr || typeof (attr) !== 'string')
throw new TypeError('attr (string) required');
for (var i = 0; i < this.attributes.length; i++) {
if (this.attributes[i].type === attr)
return i;
}
return -1;
}n/a
toObject = function () {
var self = this;
var obj = {
dn: self.entry ? self.entry.toString() : '',
attributes: {}
};
if (!this.attributes || !this.attributes.length)
return obj;
this.attributes.forEach(function (a) {
if (!obj.attributes[a.type])
obj.attributes[a.type] = [];
a.vals.forEach(function (v) {
if (obj.attributes[a.type].indexOf(v) === -1)
obj.attributes[a.type].push(v);
});
});
return obj;
}n/a
function AddResponse(options) {
options = options || {};
assert.object(options);
options.protocolOp = Protocol.LDAP_REP_ADD;
LDAPResult.call(this, options);
}n/a
function LDAPResult(options) {
options = options || {};
assert.object(options);
assert.optionalNumber(options.status);
assert.optionalString(options.matchedDN);
assert.optionalString(options.errorMessage);
assert.optionalArrayOfString(options.referrals);
LDAPMessage.call(this, options);
this.status = options.status || 0; // LDAP SUCCESS
this.matchedDN = options.matchedDN || '';
this.errorMessage = options.errorMessage || '';
this.referrals = options.referrals || [];
this.connection = options.connection || null;
}n/a
AdminLimitExceededError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
constructor = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function LDAPError(message, dn, caller) {
if (Error.captureStackTrace)
Error.captureStackTrace(this, caller || LDAPError);
this.lde_message = message;
this.lde_dn = dn;
}n/a
AffectsMultipleDsasError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
constructor = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function LDAPError(message, dn, caller) {
if (Error.captureStackTrace)
Error.captureStackTrace(this, caller || LDAPError);
this.lde_message = message;
this.lde_dn = dn;
}n/a
AliasDerefProblemError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
constructor = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function LDAPError(message, dn, caller) {
if (Error.captureStackTrace)
Error.captureStackTrace(this, caller || LDAPError);
this.lde_message = message;
this.lde_dn = dn;
}n/a
AliasProblemError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
constructor = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function LDAPError(message, dn, caller) {
if (Error.captureStackTrace)
Error.captureStackTrace(this, caller || LDAPError);
this.lde_message = message;
this.lde_dn = dn;
}n/a
function AndFilter(options) {
parents.AndFilter.call(this, options);
}n/a
function AndFilter(options) {
if (typeof (options) === 'object') {
assert.arrayOfObject(options.filters, 'options.filters');
} else {
options = {};
}
this.__defineGetter__('type', function () { return 'and'; });
this.filters = options.filters ? options.filters.slice() : [];
var self = this;
this.__defineGetter__('json', function () {
return {
type: 'And',
filters: self.filters
};
});
}n/a
_toBer = function (ber) {
assert.ok(ber);
this.filters.forEach(function (f) {
ber = f.toBer(ber);
});
return ber;
}...
function mixin(target) {
target.prototype.toBer = function toBer(ber) {
if (!ber || !(ber instanceof BerWriter))
throw new TypeError('ber (BerWriter) required');
ber.startSequence(TYPES[this.type]);
ber = this._toBer(ber);
ber.endSequence();
return ber;
};
}
module.exports = {
isFilter: isFilter,
...function toBer(ber) {
if (!ber || !(ber instanceof BerWriter))
throw new TypeError('ber (BerWriter) required');
ber.startSequence(TYPES[this.type]);
ber = this._toBer(ber);
ber.endSequence();
return ber;
}n/a
function Filter() {
}n/a
addFilter = function (filter) {
assert.object(filter, 'filter');
this.filters.push(filter);
}n/a
matches = function (target, strictAttrCase) {
assert.object(target, 'target');
if (this.filters.length === 0) {
/* true per RFC4526 */
return true;
}
for (var i = 0; i < this.filters.length; i++) {
if (!this.filters[i].matches(target, strictAttrCase))
return false;
}
return true;
}...
dn: req.dn.toString(),
attributes: {
objectclass: ['organization', 'top'],
o: 'example'
}
};
if (req.filter.matches(obj.attributes))
res.send(obj);
res.end();
});
server.listen(1389, function() {
console.log('ldapjs listening at ' + server.url);
...toString = function () {
var str = '(&';
this.filters.forEach(function (f) {
str += f.toString();
});
str += ')';
return str;
}...
```javascript
var ldap = require('ldapjs');
var server = ldap.createServer();
server.search('dc=example', function(req, res, next) {
var obj = {
dn: req.dn.toString(),
attributes: {
objectclass: ['organization', 'top'],
o: 'example'
}
};
if (req.filter.matches(obj.attributes))
...function forEach(cb) {
if (this.filter) {
// not
this.filter.forEach(cb);
} else if (this.filters) {
// and/or
this.filters.forEach(function (item) {
item.forEach(cb);
});
}
cb(this);
}...
module.exports = function () {
if (!SERVER_PROVIDER) {
try {
var dtrace = require('dtrace-provider');
SERVER_PROVIDER = dtrace.createDTraceProvider('ldapjs');
Object.keys(SERVER_PROBES).forEach(function (p) {
var args = SERVER_PROBES[p].splice(0);
args.unshift(p);
dtrace.DTraceProvider.prototype.addProbe.apply(SERVER_PROVIDER, args);
});
} catch (e) {
SERVER_PROVIDER = {
...function map(cb) {
var child;
if (this.filter) {
child = this.filter.map(cb);
if (child === null) {
// empty NOT not allowed
return null;
} else {
this.filter = child;
}
} else if (this.filters) {
child = this.filters.map(function (item) {
return item.map(cb);
}).filter(function (item) {
return (item !== null);
});
if (child.length === 0) {
// empty and/or not allowed
return null;
} else {
this.filters = child;
}
}
return cb(this);
}n/a
function ApproximateFilter(options) {
parents.ApproximateFilter.call(this, options);
}n/a
function ApproximateFilter(options) {
if (typeof (options) === 'object') {
assert.string(options.attribute, 'options.attribute');
assert.string(options.value, 'options.value');
this.attribute = options.attribute;
this.value = options.value;
}
var self = this;
this.__defineGetter__('type', function () { return 'approx'; });
this.__defineGetter__('json', function () {
return {
type: 'ApproximateMatch',
attribute: self.attribute,
value: self.value
};
});
}n/a
_toBer = function (ber) {
assert.ok(ber);
ber.writeString(this.attribute);
ber.writeString(this.value);
return ber;
}...
function mixin(target) {
target.prototype.toBer = function toBer(ber) {
if (!ber || !(ber instanceof BerWriter))
throw new TypeError('ber (BerWriter) required');
ber.startSequence(TYPES[this.type]);
ber = this._toBer(ber);
ber.endSequence();
return ber;
};
}
module.exports = {
isFilter: isFilter,
...parse = function (ber) {
assert.ok(ber);
this.attribute = ber.readString().toLowerCase();
this.value = ber.readString();
return true;
}n/a
function toBer(ber) {
if (!ber || !(ber instanceof BerWriter))
throw new TypeError('ber (BerWriter) required');
ber.startSequence(TYPES[this.type]);
ber = this._toBer(ber);
ber.endSequence();
return ber;
}n/a
function Filter() {
}n/a
matches = function () {
// Consumers must implement this themselves
throw new Error('approx match implementation missing');
}...
dn: req.dn.toString(),
attributes: {
objectclass: ['organization', 'top'],
o: 'example'
}
};
if (req.filter.matches(obj.attributes))
res.send(obj);
res.end();
});
server.listen(1389, function() {
console.log('ldapjs listening at ' + server.url);
...toString = function () {
return ('(' + helpers.escape(this.attribute) +
'~=' + helpers.escape(this.value) + ')');
}...
```javascript
var ldap = require('ldapjs');
var server = ldap.createServer();
server.search('dc=example', function(req, res, next) {
var obj = {
dn: req.dn.toString(),
attributes: {
objectclass: ['organization', 'top'],
o: 'example'
}
};
if (req.filter.matches(obj.attributes))
...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');
} else {
options = {};
}
this.type = options.type || '';
this._vals = [];
if (options.vals !== undefined && options.vals !== null)
this.vals = options.vals;
}n/a
function compare(a, b) {
if (!(Attribute.isAttribute(a)) || !(Attribute.isAttribute(b))) {
throw new TypeError('can only compare Attributes');
}
if (a.type < b.type) return -1;
if (a.type > b.type) return 1;
if (a.vals.length < b.vals.length) return -1;
if (a.vals.length > b.vals.length) return 1;
for (var i = 0; i < a.vals.length; i++) {
if (a.vals[i] < b.vals[i]) return -1;
if (a.vals[i] > b.vals[i]) return 1;
}
return 0;
}n/a
function isAttribute(attr) {
if (!attr || typeof (attr) !== 'object') {
return false;
}
if (attr instanceof Attribute) {
return true;
}
if ((typeof (attr.toBer) === 'function') &&
(typeof (attr.type) === 'string') &&
(Array.isArray(attr.vals)) &&
(attr.vals.filter(function (item) {
return (typeof (item) === 'string' ||
Buffer.isBuffer(item));
}).length === attr.vals.length)) {
return true;
}
return false;
}n/a
toBer = function (attr, ber) {
return Attribute.prototype.toBer.call(attr, ber);
}n/a
function addValue(val) {
if (Buffer.isBuffer(val)) {
this._vals.push(val);
} else {
this._vals.push(new Buffer(val + '', _bufferEncoding(this.type)));
}
}n/a
function parse(ber) {
assert.ok(ber);
ber.readSequence();
this.type = ber.readString();
if (ber.peek() === Protocol.LBER_SET) {
if (ber.readSequence(Protocol.LBER_SET)) {
var end = ber.offset + ber.length;
while (ber.offset < end)
this._vals.push(ber.readString(asn1.Ber.OctetString, true));
}
}
return true;
}n/a
function toBer(ber) {
assert.ok(ber);
ber.startSequence();
ber.writeString(this.type);
ber.startSequence(Protocol.LBER_SET);
if (this._vals.length) {
this._vals.forEach(function (b) {
ber.writeByte(asn1.Ber.OctetString);
ber.writeLength(b.length);
for (var i = 0; i < b.length; i++)
ber.writeByte(b[i]);
});
} else {
ber.writeStringArray([]);
}
ber.endSequence();
ber.endSequence();
return ber;
}n/a
toString = function () {
return JSON.stringify(this.json);
}...
```javascript
var ldap = require('ldapjs');
var server = ldap.createServer();
server.search('dc=example', function(req, res, next) {
var obj = {
dn: req.dn.toString(),
attributes: {
objectclass: ['organization', 'top'],
o: 'example'
}
};
if (req.filter.matches(obj.attributes))
...AttributeOrValueExistsError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
constructor = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function LDAPError(message, dn, caller) {
if (Error.captureStackTrace)
Error.captureStackTrace(this, caller || LDAPError);
this.lde_message = message;
this.lde_dn = dn;
}n/a
AuthMethodNotSupportedError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
constructor = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function LDAPError(message, dn, caller) {
if (Error.captureStackTrace)
Error.captureStackTrace(this, caller || LDAPError);
this.lde_message = message;
this.lde_dn = dn;
}n/a
function BindRequest(options) {
options = options || {};
assert.object(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 || '';
}n/a
function LDAPMessage(options) {
assert.object(options);
this.messageID = options.messageID || 0;
this.protocolOp = options.protocolOp || undefined;
this.controls = options.controls ? options.controls.slice(0) : [];
this.log = options.log;
}n/a
_json = function (j) {
assert.ok(j);
j.version = this.version;
j.name = this.name;
j.authenticationType = this.authentication;
j.credentials = this.credentials;
return j;
}n/a
_parse = function (ber) {
assert.ok(ber);
this.version = ber.readInt();
this.name = 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;
}n/a
_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;
}...
function mixin(target) {
target.prototype.toBer = function toBer(ber) {
if (!ber || !(ber instanceof BerWriter))
throw new TypeError('ber (BerWriter) required');
ber.startSequence(TYPES[this.type]);
ber = this._toBer(ber);
ber.endSequence();
return ber;
};
}
module.exports = {
isFilter: isFilter,
...function BindResponse(options) {
options = options || {};
assert.object(options);
options.protocolOp = Protocol.LDAP_REP_BIND;
LDAPResult.call(this, options);
}n/a
function LDAPResult(options) {
options = options || {};
assert.object(options);
assert.optionalNumber(options.status);
assert.optionalString(options.matchedDN);
assert.optionalString(options.errorMessage);
assert.optionalArrayOfString(options.referrals);
LDAPMessage.call(this, options);
this.status = options.status || 0; // LDAP SUCCESS
this.matchedDN = options.matchedDN || '';
this.errorMessage = options.errorMessage || '';
this.referrals = options.referrals || [];
this.connection = options.connection || null;
}n/a
BusyError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
constructor = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function LDAPError(message, dn, caller) {
if (Error.captureStackTrace)
Error.captureStackTrace(this, caller || LDAPError);
this.lde_message = message;
this.lde_dn = dn;
}n/a
function Change(options) {
if (options) {
assert.object(options);
assert.optionalString(options.operation);
} else {
options = {};
}
this._modification = false;
this.operation = options.operation || options.type || 'add';
this.modification = options.modification || {};
}n/a
function apply(change, obj, scalar) {
assert.string(change.operation);
assert.string(change.modification.type);
assert.ok(Array.isArray(change.modification.vals));
assert.object(obj);
var type = change.modification.type;
var vals = change.modification.vals;
var data = obj[type];
if (data !== undefined) {
if (!Array.isArray(data)) {
data = [data];
}
} else {
data = [];
}
switch (change.operation) {
case 'replace':
if (vals.length === 0) {
// replace empty is a delete
delete obj[type];
return obj;
} else {
data = vals;
}
break;
case 'add':
// add only new unique entries
var newValues = vals.filter(function (entry) {
return (data.indexOf(entry) === -1);
});
data = data.concat(newValues);
break;
case 'delete':
data = data.filter(function (entry) {
return (vals.indexOf(entry) === -1);
});
if (data.length === 0) {
// Erase the attribute if empty
delete obj[type];
return obj;
}
break;
default:
break;
}
if (scalar && data.length === 1) {
// store single-value outputs as scalars, if requested
obj[type] = data[0];
} else {
obj[type] = data;
}
return obj;
}...
var dtrace = require('dtrace-provider');
SERVER_PROVIDER = dtrace.createDTraceProvider('ldapjs');
Object.keys(SERVER_PROBES).forEach(function (p) {
var args = SERVER_PROBES[p].splice(0);
args.unshift(p);
dtrace.DTraceProvider.prototype.addProbe.apply(SERVER_PROVIDER, args);
});
} catch (e) {
SERVER_PROVIDER = {
fire: function () {
},
enable: function () {
},
...compare = function (a, b) {
if (!Change.isChange(a) || !Change.isChange(b))
throw new TypeError('can only compare Changes');
if (a.operation < b.operation)
return -1;
if (a.operation > b.operation)
return 1;
return Attribute.compare(a.modification, b.modification);
}n/a
function isChange(change) {
if (!change || typeof (change) !== 'object') {
return false;
}
if ((change instanceof Change) ||
((typeof (change.toBer) === 'function') &&
(change.modification !== undefined) &&
(change.operation !== undefined))) {
return true;
}
return false;
}n/a
parse = function (ber) {
assert.ok(ber);
ber.readSequence();
this._operation = ber.readEnumeration();
this._modification = new Attribute();
this._modification.parse(ber);
return true;
}n/a
toBer = function (ber) {
assert.ok(ber);
ber.startSequence();
ber.writeEnumeration(this._operation);
ber = this._modification.toBer(ber);
ber.endSequence();
return ber;
}n/a
function Client(options) {
assert.ok(options);
EventEmitter.call(this, options);
var self = this;
var _url;
if (options.url)
_url = url.parse(options.url);
this.host = _url ? _url.hostname : undefined;
this.port = _url ? _url.port : false;
this.secure = _url ? _url.secure : false;
this.url = _url;
this.tlsOptions = options.tlsOptions;
this.socketPath = options.socketPath || false;
this.log = options.log.child({clazz: 'Client'}, true);
this.timeout = parseInt((options.timeout || 0), 10);
this.connectTimeout = parseInt((options.connectTimeout || 0), 10);
this.idleTimeout = parseInt((options.idleTimeout || 0), 10);
if (options.reconnect) {
// Fall back to defaults if options.reconnect === true
var rOpts = (typeof (options.reconnect) === 'object') ?
options.reconnect : {};
this.reconnect = {
initialDelay: parseInt(rOpts.initialDelay || 100, 10),
maxDelay: parseInt(rOpts.maxDelay || 10000, 10),
failAfter: parseInt(rOpts.failAfter, 10) || Infinity
};
}
this.strictDN = (options.strictDN !== undefined) ? options.strictDN : true;
this.queue = new RequestQueue({
size: parseInt((options.queueSize || 0), 10),
timeout: parseInt((options.queueTimeout || 0), 10)
});
if (options.queueDisable) {
this.queue.freeze();
}
// Implicitly configure setup action to bind the client if bindDN and
// bindCredentials are passed in. This will more closely mimic PooledClient
// auto-login behavior.
if (options.bindDN !== undefined &&
options.bindCredentials !== undefined) {
this.on('setup', function (clt, cb) {
clt.bind(options.bindDN, options.bindCredentials, function (err) {
if (err) {
self.emit('error', err);
}
cb(err);
});
});
}
this._socket = null;
this.connected = false;
this.connect();
}n/a
function EventEmitter() {
EventEmitter.init.call(this);
}n/a
function _flushQueue() {
// Pull items we're about to process out of the queue.
this.queue.flush(this._send.bind(this));
}n/a
function _onClose(had_err) {
var socket = this._socket;
var tracker = this._tracker;
socket.removeAllListeners('connect')
.removeAllListeners('data')
.removeAllListeners('drain')
.removeAllListeners('end')
.removeAllListeners('error')
.removeAllListeners('timeout');
this._socket = null;
this.connected = false;
((socket.socket) ? socket.socket : socket).removeAllListeners('close');
if (this.log.trace())
this.log.trace('close event had_err=%s', had_err ? 'yes' : 'no');
this.emit('close', had_err);
// On close we have to walk the outstanding messages and go invoke their
// callback with an error.
tracker.pending.forEach(function (msgid) {
var cb = tracker.fetch(msgid);
tracker.remove(msgid);
if (socket.unbindMessageID !== parseInt(msgid, 10)) {
return cb(new ConnectionError(tracker.id + ' closed'));
} else {
// Unbinds will be communicated as a success since we're closed
var unbind = new UnbindResponse({messageID: msgid});
unbind.status = 'unbind';
return cb(unbind);
}
});
// Trash any parser or starttls state
this._tracker = null;
delete this._starttls;
// Automatically fire reconnect logic if the socket was closed for any reason
// other than a user-initiated unbind.
if (this.reconnect && !this.unbound) {
this.connect();
}
this.unbound = false;
return false;
}n/a
function _send(message, expect, emitter, callback, _bypass) {
assert.ok(message);
assert.ok(expect);
assert.optionalObject(emitter);
assert.ok(callback);
// Allow connect setup traffic to bypass checks
if (_bypass && this._socket && this._socket.writable) {
return this._sendSocket(message, expect, emitter, callback);
}
if (!this._socket || !this.connected) {
if (!this.queue.enqueue(message, expect, emitter, callback)) {
callback(new ConnectionError('connection unavailable'));
}
// Initiate reconnect if needed
if (this.reconnect) {
this.connect();
}
return false;
} else {
this._flushQueue();
return this._sendSocket(message, expect, emitter, callback);
}
}n/a
function _sendSocket(message, expect, emitter, callback) {
var conn = this._socket;
var tracker = this._tracker;
var log = this.log;
var self = this;
var timer = false;
var sentEmitter = false;
function sendResult(event, obj) {
if (event === 'error' && self.listeners('resultError')) {
self.emit('resultError', obj);
}
if (emitter) {
if (event === 'error') {
// Error will go unhandled if emitter hasn't been sent via callback.
// Execute callback with the error instead.
if (!sentEmitter)
return callback(obj);
}
return emitter.emit(event, obj);
}
if (event === 'error')
return callback(obj);
return callback(null, obj);
}
function messageCallback(msg) {
if (timer)
clearTimeout(timer);
if (log.trace())
log.trace({msg: msg ? msg.json : null}, 'response received');
if (expect === 'abandon')
return sendResult('end', null);
if (msg instanceof SearchEntry || msg instanceof SearchReference) {
var event = msg.constructor.name;
event = event[0].toLowerCase() + event.slice(1);
return sendResult(event, msg);
} else {
tracker.remove(message.messageID);
// Potentially mark client as idle
self._updateIdle();
if (msg instanceof LDAPResult) {
if (expect.indexOf(msg.status) === -1) {
return sendResult('error', errors.getError(msg));
}
return sendResult('end', msg);
} else if (msg instanceof Error) {
return sendResult('error', msg);
} else {
return sendResult('error', new errors.ProtocolError(msg.type));
}
}
}
function onRequestTimeout() {
self.emit('timeout', message);
var cb = tracker.fetch(message.messageID);
if (cb) {
//FIXME: the timed-out request should be abandoned
cb(new errors.TimeoutError('request timeout (client interrupt)'));
}
}
function writeCallback() {
if (expect === 'abandon') {
// Mark the messageID specified as abandoned
tracker.abandon(message.abandonID);
// No need to track the abandon request itself
tracker.remove(message.id);
return callback(null);
} else if (expect === 'unbind') {
conn.unbindMessageID = message.id;
// Mark client as disconnected once unbind clears the socket
self.connected = false;
// Some servers will RST the connection after receiving an unbind.
// Socket errors are blackholed since the connection is being closed.
conn.removeAllListeners('error');
conn.on('error', function () {});
conn.end();
} else if (emitter) {
sentEmitter = true;
return callback(null, emitter);
}
return false;
}
// Start actually doing something...
tracker.track(message, messageCallback);
// Mark client as active
this._updateIdle(true);
if (self.timeout) {
log.trace('Setting timeout to %d', self.timeout);
timer = setTimeout(onRequestTimeout, self.timeout);
}
if (log.trace())
log.trace('sending request %j', message.json);
try {
return conn.write(message.toBer(), writeCallback);
} catch (e) {
if (timer)
clearTimeout(timer);
log.trace({err: e}, 'Error writing message to socket');
return callback(e);
}
}n/a
function _updateIdle(override) {
if (this.idleTimeout === 0) {
return;
}
// Client must be connected but not waiting on any request data
var self = this;
function isIdle(disable) {
return ((disable !== true) &&
(self._socket && self.connected) &&
(self._tracker.pending.length === 0));
}
if (isIdle(override)) {
if (!this._idleTimer) {
this._idleTimer = setTimeout(function () {
// Double-check idleness in case socket was torn down
if (isIdle()) {
self.emit('idle');
}
}, this.idleTimeout);
}
} else {
if (this._idleTimer) {
clearTimeout(this._idleTimer);
this._idleTimer = null;
}
}
}n/a
function abandon(messageID, controls, callback) {
assert.number(messageID, 'messageID');
if (typeof (controls) === 'function') {
callback = controls;
controls = [];
} else {
controls = validateControls(controls);
}
assert.func(callback, 'callback');
var req = new AbandonRequest({
abandonID: messageID,
controls: controls
});
return this._send(req, 'abandon', null, callback);
}n/a
function add(name, entry, controls, callback) {
assert.ok(name !== undefined, 'name');
assert.object(entry, 'entry');
if (typeof (controls) === 'function') {
callback = controls;
controls = [];
} else {
controls = validateControls(controls);
}
assert.func(callback, 'callback');
if (Array.isArray(entry)) {
entry.forEach(function (a) {
if (!Attribute.isAttribute(a))
throw new TypeError('entry must be an Array of Attributes');
});
} else {
var save = entry;
entry = [];
Object.keys(save).forEach(function (k) {
var attr = new Attribute({type: k});
if (Array.isArray(save[k])) {
save[k].forEach(function (v) {
attr.addValue(v.toString());
});
} else {
attr.addValue(save[k].toString());
}
entry.push(attr);
});
}
var req = new AddRequest({
entry: ensureDN(name, this.strictDN),
attributes: entry,
controls: controls
});
return this._send(req, [errors.LDAP_SUCCESS], null, callback);
}n/a
function bind(name, credentials, controls, callback, _bypass) {
if (typeof (name) !== 'string' && !(name instanceof dn.DN))
throw new TypeError('name (string) required');
assert.optionalString(credentials, 'credentials');
if (typeof (controls) === 'function') {
callback = controls;
controls = [];
} else {
controls = validateControls(controls);
}
assert.func(callback, 'callback');
var req = new BindRequest({
name: name || '',
authentication: 'Simple',
credentials: credentials || '',
controls: controls
});
return this._send(req, [errors.LDAP_SUCCESS], null, callback, _bypass);
}...
}
});
this.finished = false;
this.started = false;
var emitter = new EventEmitter();
emitter.on('searchEntry', this.emit.bind(this, 'searchEntry'));
emitter.on('end', this._onEnd.bind(this));
emitter.on('error', this._onError.bind(this));
this.childEmitter = emitter;
}
util.inherits(SearchPager, EventEmitter);
module.exports = SearchPager;
...function compare(name, attr, value, controls, callback) {
assert.ok(name !== undefined, 'name');
assert.string(attr, 'attr');
assert.string(value, 'value');
if (typeof (controls) === 'function') {
callback = controls;
controls = [];
} else {
controls = validateControls(controls);
}
assert.func(callback, 'callback');
var req = new CompareRequest({
entry: ensureDN(name, this.strictDN),
attribute: attr,
value: value,
controls: controls
});
return this._send(req, CMP_EXPECT, null, function (err, res) {
if (err)
return callback(err);
return callback(null, (res.status === errors.LDAP_COMPARE_TRUE), res);
});
}n/a
function connect() {
if (this.connecting || this.connected) {
return;
}
var self = this;
var log = this.log;
var socket;
var tracker;
// Establish basic socket connection
function connectSocket(cb) {
cb = once(cb);
function onResult(err, res) {
if (err) {
if (self.connectTimer) {
clearTimeout(self.connectTimer);
self.connectTimer = null;
}
self.emit('connectError', err);
}
cb(err, res);
}
function onConnect() {
if (self.connectTimer) {
clearTimeout(self.connectTimer);
self.connectTimer = null;
}
socket.removeAllListeners('error')
.removeAllListeners('connect')
.removeAllListeners('secureConnect');
tracker.id = nextClientId() + '__' + tracker.id;
self.log = self.log.child({ldap_id: tracker.id}, true);
// Move on to client setup
setupClient(cb);
}
var port = (self.port || self.socketPath);
if (self.secure) {
socket = tls.connect(port, self.host, self.tlsOptions);
socket.once('secureConnect', onConnect);
} else {
socket = net.connect(port, self.host);
socket.once('connect', onConnect);
}
socket.once('error', onResult);
initSocket();
// Setup connection timeout handling, if desired
if (self.connectTimeout) {
self.connectTimer = setTimeout(function onConnectTimeout() {
if (!socket || !socket.readable || !socket.writeable) {
socket.destroy();
self._socket = null;
onResult(new ConnectionError('connection timeout'));
}
}, self.connectTimeout);
}
}
// Initialize socket events and LDAP parser.
function initSocket() {
tracker = new MessageTracker({
id: self.url ? self.url.href : self.socketPath,
parser: new Parser({log: log})
});
// This won't be set on TLS. So. Very. Annoying.
if (typeof (socket.setKeepAlive) !== 'function') {
socket.setKeepAlive = function setKeepAlive(enable, delay) {
return socket.socket ?
socket.socket.setKeepAlive(enable, delay) : false;
};
}
socket.on('data', function onData(data) {
if (log.trace())
log.trace('data event: %s', util.inspect(data));
tracker.parser.write(data);
});
// The "router"
tracker.parser.on('message', function onMessage(message) {
message.connection = self._socket;
var callback = tracker.fetch(message.messageID);
if (!callback) {
log.error({message: message.json}, 'unsolicited message');
return false;
}
return callback(message);
});
tracker.parser.on('error', function onParseError(err) {
self.emit('error', new VError(err, 'Parser error for %s',
tracker.id));
self.connected = false;
socket.end();
});
}
// After connect, register socket event handlers and run any setup actions
function setupClient(cb) {
cb = once(cb);
// Indicate failure if anything goes awry during setup
function bail(err) {
socket.destroy();
cb(err || new Error('client error during setup'));
}
// Work around lack of close event on tls.socket in node < 0.11
((socket.socket) ? socket.socket : socket).once('close', bail);
socket.once('error', bail);
socket.once('end', bail);
socket.once('timeout', bail);
self._socket = socket;
self._tracker = tracker;
// Run any requested setup (such as automatically performing a bind) on
// socket before signalling successful connection.
// This setup needs to bypass the request queue since all other activity is
// blocked until the connection is considered fully established post-setup.
// Only allow bind/search/starttls for now.
var basicClient = {
bind: function bindBypass(name, credentials, controls, callback) {
return self.bind(name, credentials, controls, callback, true);
},
search: function searchBypass(base, options, controls, callback) {
return self.search(base, option ...n/a
function del(name, controls, callback) {
assert.ok(name !== undefined, 'name');
if (typeof (controls) === 'function') {
callback = controls;
controls = [];
} else {
controls = validateControls(controls);
}
assert.func(callback, 'callback');
var req = new DeleteRequest({
entry: ensureDN(name, this.strictDN),
controls: controls
});
return this._send(req, [errors.LDAP_SUCCESS], null, callback);
}n/a
function destroy(err) {
this.destroyed = true;
this.queue.freeze();
// Purge any queued requests which are now meaningless
this.queue.flush(function (msg, expect, emitter, cb) {
if (typeof (cb) === 'function') {
cb(new Error('client destroyed'));
}
});
if (this.connected) {
this.unbind();
} else if (this._socket) {
this._socket.destroy();
}
this.emit('destroy', err);
}n/a
function exop(name, value, controls, callback) {
assert.string(name, 'name');
if (typeof (value) === 'function') {
callback = value;
controls = [];
value = '';
}
if (!(Buffer.isBuffer(value) || typeof (value) === 'string'))
throw new TypeError('value (Buffer || string) required');
if (typeof (controls) === 'function') {
callback = controls;
controls = [];
} else {
controls = validateControls(controls);
}
assert.func(callback, 'callback');
var req = new ExtendedRequest({
requestName: name,
requestValue: value,
controls: controls
});
return this._send(req, [errors.LDAP_SUCCESS], null, function (err, res) {
if (err)
return callback(err);
return callback(null, res.responseValue || '', res);
});
}n/a
function modify(name, change, controls, callback) {
assert.ok(name !== undefined, 'name');
assert.object(change, 'change');
var changes = [];
function changeFromObject(change) {
if (!change.operation && !change.type)
throw new Error('change.operation required');
if (typeof (change.modification) !== 'object')
throw new Error('change.modification (object) required');
if (Object.keys(change.modification).length == 2 &&
typeof (change.modification.type) === 'string' &&
Array.isArray(change.modification.vals)) {
// Use modification directly if it's already normalized:
changes.push(new Change({
operation: change.operation || change.type,
modification: change.modification
}));
} else {
// Normalize the modification object
Object.keys(change.modification).forEach(function (k) {
var mod = {};
mod[k] = change.modification[k];
changes.push(new Change({
operation: change.operation || change.type,
modification: mod
}));
});
}
}
if (Change.isChange(change)) {
changes.push(change);
} else if (Array.isArray(change)) {
change.forEach(function (c) {
if (Change.isChange(c)) {
changes.push(c);
} else {
changeFromObject(c);
}
});
} else {
changeFromObject(change);
}
if (typeof (controls) === 'function') {
callback = controls;
controls = [];
} else {
controls = validateControls(controls);
}
assert.func(callback, 'callback');
var req = new ModifyRequest({
object: ensureDN(name, this.strictDN),
changes: changes,
controls: controls
});
return this._send(req, [errors.LDAP_SUCCESS], null, callback);
}n/a
function modifyDN(name, newName, controls, callback) {
assert.ok(name !== undefined, 'name');
assert.string(newName, 'newName');
if (typeof (controls) === 'function') {
callback = controls;
controls = [];
} else {
controls = validateControls(controls);
}
assert.func(callback);
var DN = ensureDN(name);
// TODO: is non-strict handling desired here?
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], null, callback);
}n/a
function search(base, options, controls, callback, _bypass) {
assert.ok(base !== undefined, 'search base');
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 (typeof (options.filter) === 'string') {
options.filter = filters.parseString(options.filter);
} else if (!options.filter) {
options.filter = new PresenceFilter({attribute: 'objectclass'});
} else if (!filters.isFilter(options.filter)) {
throw new TypeError('options.filter (Filter) required');
}
if (typeof (controls) === 'function') {
callback = controls;
controls = [];
} else {
controls = validateControls(controls);
}
assert.func(callback, 'callback');
if (options.attributes) {
if (!Array.isArray(options.attributes)) {
if (typeof (options.attributes) === 'string') {
options.attributes = [options.attributes];
} else {
throw new TypeError('options.attributes must be an Array of Strings');
}
}
}
var self = this;
var baseDN = ensureDN(base, this.strictDN);
function sendRequest(ctrls, emitter, cb) {
var req = new SearchRequest({
baseObject: baseDN,
scope: options.scope || 'base',
filter: options.filter,
derefAliases: options.derefAliases || Protocol.NEVER_DEREF_ALIASES,
sizeLimit: options.sizeLimit || 0,
timeLimit: options.timeLimit || 10,
typesOnly: options.typesOnly || false,
attributes: options.attributes || [],
controls: ctrls
});
return self._send(req,
[errors.LDAP_SUCCESS],
emitter,
cb,
_bypass);
}
if (options.paged) {
// Perform automated search paging
var pageOpts = typeof (options.paged) === 'object' ? options.paged : {};
var size = 100; // Default page size
if (pageOpts.pageSize > 0) {
size = pageOpts.pageSize;
} else if (options.sizeLimit > 1) {
// According to the RFC, servers should ignore the paging control if
// pageSize >= sizelimit. Some might still send results, but it's safer
// to stay under that figure when assigning a default value.
size = options.sizeLimit - 1;
}
var pager = new SearchPager({
callback: callback,
controls: controls,
pageSize: size,
pagePause: pageOpts.pagePause
});
pager.on('search', sendRequest);
pager.begin();
} else {
sendRequest(controls, new EventEmitter(), callback);
}
}...
For full docs, head on over to <http://ldapjs.org>.
```javascript
var ldap = require('ldapjs');
var server = ldap.createServer();
server.search('dc=example', function(req, res, next) {
var obj = {
dn: req.dn.toString(),
attributes: {
objectclass: ['organization', 'top'],
o: 'example'
}
};
...function starttls(options, controls, callback, _bypass) {
assert.optionalObject(options);
options = options || {};
callback = once(callback);
var self = this;
if (this._starttls) {
return callback(new Error('STARTTLS already in progress or active'));
}
function onSend(err, emitter) {
if (err) {
callback(err);
return;
}
/*
* Now that the request has been sent, block all outgoing messages
* until an error is received or we successfully complete the setup.
*/
// TODO: block traffic
self._starttls = {
started: true
};
emitter.on('error', function (err) {
self._starttls = null;
callback(err);
});
emitter.on('end', function (res) {
var sock = self._socket;
/*
* Unplumb socket data during SSL negotiation.
* This will prevent the LDAP parser from stumbling over the TLS
* handshake and raising a ruckus.
*/
sock.removeAllListeners('data');
options.socket = sock;
var secure = tls.connect(options);
secure.once('secureConnect', function () {
/*
* Wire up 'data' and 'error' handlers like the normal socket.
* Handling 'end' events isn't necessary since the underlying socket
* will handle those.
*/
secure.removeAllListeners('error');
secure.on('data', function onData(data) {
if (self.log.trace())
self.log.trace('data event: %s', util.inspect(data));
self._tracker.parser.write(data);
});
secure.on('error', function (err) {
if (self.log.trace())
self.log.trace({err: err}, 'error event: %s', new Error().stack);
self.emit('error', err);
sock.destroy();
});
callback(null);
});
secure.once('error', function (err) {
// If the SSL negotiation failed, to back to plain mode.
self._starttls = null;
secure.removeAllListeners();
callback(err);
});
self._starttls.success = true;
self._socket = secure;
});
}
var req = new ExtendedRequest({
requestName: '1.3.6.1.4.1.1466.20037',
requestValue: null,
controls: controls
});
return this._send(req,
[errors.LDAP_SUCCESS],
new EventEmitter(),
onSend,
_bypass);
}n/a
function unbind(callback) {
if (!callback)
callback = function () {};
if (typeof (callback) !== 'function')
throw new TypeError('callback must be a function');
// When the socket closes, it is useful to know whether it was due to a
// user-initiated unbind or something else.
this.unbound = true;
if (!this._socket)
return callback();
var req = new UnbindRequest();
return this._send(req, 'unbind', null, callback);
}n/a
CompareFalseError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
constructor = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function LDAPError(message, dn, caller) {
if (Error.captureStackTrace)
Error.captureStackTrace(this, caller || LDAPError);
this.lde_message = message;
this.lde_dn = dn;
}n/a
function CompareRequest(options) {
options = options || {};
assert.object(options);
assert.optionalString(options.attribute);
assert.optionalString(options.value);
lassert.optionalStringDN(options.entry);
options.protocolOp = Protocol.LDAP_REQ_COMPARE;
LDAPMessage.call(this, options);
this.entry = options.entry || null;
this.attribute = options.attribute || '';
this.value = options.value || '';
}n/a
function LDAPMessage(options) {
assert.object(options);
this.messageID = options.messageID || 0;
this.protocolOp = options.protocolOp || undefined;
this.controls = options.controls ? options.controls.slice(0) : [];
this.log = options.log;
}n/a
_json = function (j) {
assert.ok(j);
j.entry = this.entry.toString();
j.attribute = this.attribute;
j.value = this.value;
return j;
}n/a
_parse = function (ber) {
assert.ok(ber);
this.entry = ber.readString();
ber.readSequence();
this.attribute = ber.readString().toLowerCase();
this.value = ber.readString();
return true;
}n/a
_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;
}...
function mixin(target) {
target.prototype.toBer = function toBer(ber) {
if (!ber || !(ber instanceof BerWriter))
throw new TypeError('ber (BerWriter) required');
ber.startSequence(TYPES[this.type]);
ber = this._toBer(ber);
ber.endSequence();
return ber;
};
}
module.exports = {
isFilter: isFilter,
...function CompareResponse(options) {
options = options || {};
assert.object(options);
options.protocolOp = Protocol.LDAP_REP_COMPARE;
LDAPResult.call(this, options);
}n/a
function LDAPResult(options) {
options = options || {};
assert.object(options);
assert.optionalNumber(options.status);
assert.optionalString(options.matchedDN);
assert.optionalString(options.errorMessage);
assert.optionalArrayOfString(options.referrals);
LDAPMessage.call(this, options);
this.status = options.status || 0; // LDAP SUCCESS
this.matchedDN = options.matchedDN || '';
this.errorMessage = options.errorMessage || '';
this.referrals = options.referrals || [];
this.connection = options.connection || null;
}n/a
end = function (matches) {
var status = 0x06;
if (typeof (matches) === 'boolean') {
if (!matches)
status = 0x05; // Compare false
} else {
status = matches;
}
return LDAPResult.prototype.end.call(this, status);
}...
o: 'example'
}
};
if (req.filter.matches(obj.attributes))
res.send(obj);
res.end();
});
server.listen(1389, function() {
console.log('ldapjs listening at ' + server.url);
});
```
...CompareTrueError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
constructor = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function LDAPError(message, dn, caller) {
if (Error.captureStackTrace)
Error.captureStackTrace(this, caller || LDAPError);
this.lde_message = message;
this.lde_dn = dn;
}n/a
ConfidentialityRequiredError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
constructor = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function LDAPError(message, dn, caller) {
if (Error.captureStackTrace)
Error.captureStackTrace(this, caller || LDAPError);
this.lde_message = message;
this.lde_dn = dn;
}n/a
function ConnectionError(message) {
LDAPError.call(this, message, null, ConnectionError);
}n/a
function LDAPError(message, dn, caller) {
if (Error.captureStackTrace)
Error.captureStackTrace(this, caller || LDAPError);
this.lde_message = message;
this.lde_dn = dn;
}n/a
ConstraintViolationError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
constructor = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function LDAPError(message, dn, caller) {
if (Error.captureStackTrace)
Error.captureStackTrace(this, caller || LDAPError);
this.lde_message = message;
this.lde_dn = dn;
}n/a
function Control(options) {
assert.optionalObject(options);
options = options || {};
assert.optionalString(options.type);
assert.optionalBool(options.criticality);
if (options.value) {
assert.buffer(options.value);
}
this.type = options.type || '';
this.criticality = options.critical || options.criticality || false;
this.value = options.value || null;
}n/a
function toBer(ber) {
assert.ok(ber);
ber.startSequence();
ber.writeString(this.type || '');
ber.writeBoolean(this.criticality);
if (typeof (this._toBer) === 'function') {
this._toBer(ber);
} else {
if (this.value)
ber.writeString(this.value);
}
ber.endSequence();
return;
}n/a
function toString() {
return this.json;
}...
```javascript
var ldap = require('ldapjs');
var server = ldap.createServer();
server.search('dc=example', function(req, res, next) {
var obj = {
dn: req.dn.toString(),
attributes: {
objectclass: ['organization', 'top'],
o: 'example'
}
};
if (req.filter.matches(obj.attributes))
...function DN(rdns) {
assert.optionalArrayOfObject(rdns, '[object] required');
this.rdns = rdns ? rdns.slice() : [];
this._format = {};
}n/a
function isDN(dn) {
if (!dn || typeof (dn) !== 'object') {
return false;
}
if (dn instanceof DN) {
return true;
}
if (Array.isArray(dn.rdns)) {
// Really simple duck-typing for now
return true;
}
return false;
}n/a
function childOf(dn) {
if (typeof (dn) !== 'object')
dn = parse(dn);
return dn.parentOf(this);
}n/a
function dnClone() {
var dn = new DN(this.rdns);
dn._format = this._format;
return dn;
}n/a
function dnEquals(dn) {
if (typeof (dn) !== 'object')
dn = parse(dn);
if (this.rdns.length !== dn.rdns.length)
return false;
for (var i = 0; i < this.rdns.length; i++) {
if (!this.rdns[i].equals(dn.rdns[i]))
return false;
}
return true;
}n/a
function dnFormat(options) {
assert.optionalObject(options, 'options must be an object');
options = options || this._format;
var str = '';
this.rdns.forEach(function (rdn) {
var rdnString = rdn.format(options);
if (str.length !== 0) {
str += ',';
}
if (options.keepSpace) {
str += (repeatChar(' ', rdn.spLead) +
rdnString + repeatChar(' ', rdn.spTrail));
} else if (options.skipSpace === true || str.length === 0) {
str += rdnString;
} else {
str += ' ' + rdnString;
}
});
return str;
}...
///--- Helpers
// Copied from mcavage/node-assert-plus
function _assert(arg, type, name) {
name = name || type;
throw new assert.AssertionError({
message: util.format('%s (%s) required', name, type),
actual: typeof (arg),
expected: type,
operator: '===',
stackStartFunction: _assert.caller
});
}
...function isEmpty() {
return (this.rdns.length === 0);
}n/a
function dnParent() {
if (this.rdns.length !== 0) {
var save = this.rdns.shift();
var dn = new DN(this.rdns);
this.rdns.unshift(save);
return dn;
}
return null;
}n/a
function parentOf(dn) {
if (typeof (dn) !== 'object')
dn = parse(dn);
if (this.rdns.length >= dn.rdns.length)
return false;
var diff = dn.rdns.length - this.rdns.length;
for (var i = this.rdns.length - 1; i >= 0; i--) {
var myRDN = this.rdns[i];
var theirRDN = dn.rdns[i + diff];
if (!myRDN.equals(theirRDN))
return false;
}
return true;
}n/a
function dnPop() {
return this.rdns.pop();
}n/a
function dnPush(rdn) {
assert.object(rdn, 'rdn (RDN) required');
return this.rdns.push(rdn);
}...
};
/**
* Initiate a search for the next page using the returned cookie value.
*/
SearchPager.prototype._nextPage = function _nextPage(cookie) {
var controls = this.controls.slice(0);
controls.push(new PagedControl({
value: {
size: this.pageSize,
cookie: cookie
}
}));
this.emit('search', controls, this.childEmitter,
...function dnReverse() {
this.rdns.reverse();
return this;
}n/a
function setFormat(options) {
assert.object(options, 'options must be an object');
this._format = options;
}n/a
function dnShift() {
return this.rdns.shift();
}n/a
function dnToString() {
return this.format();
}...
```javascript
var ldap = require('ldapjs');
var server = ldap.createServer();
server.search('dc=example', function(req, res, next) {
var obj = {
dn: req.dn.toString(),
attributes: {
objectclass: ['organization', 'top'],
o: 'example'
}
};
if (req.filter.matches(obj.attributes))
...function dnUnshift(rdn) {
assert.object(rdn, 'rdn (RDN) required');
return this.rdns.unshift(rdn);
}...
if (!SERVER_PROVIDER) {
try {
var dtrace = require('dtrace-provider');
SERVER_PROVIDER = dtrace.createDTraceProvider('ldapjs');
Object.keys(SERVER_PROBES).forEach(function (p) {
var args = SERVER_PROBES[p].splice(0);
args.unshift(p);
dtrace.DTraceProvider.prototype.addProbe.apply(SERVER_PROVIDER, args);
});
} catch (e) {
SERVER_PROVIDER = {
fire: function () {
},
...function DeleteRequest(options) {
options = options || {};
assert.object(options);
lassert.optionalStringDN(options.entry);
options.protocolOp = Protocol.LDAP_REQ_DELETE;
LDAPMessage.call(this, options);
this.entry = options.entry || null;
}n/a
function LDAPMessage(options) {
assert.object(options);
this.messageID = options.messageID || 0;
this.protocolOp = options.protocolOp || undefined;
this.controls = options.controls ? options.controls.slice(0) : [];
this.log = options.log;
}n/a
_json = function (j) {
assert.ok(j);
j.entry = this.entry;
return j;
}n/a
_parse = function (ber, length) {
assert.ok(ber);
this.entry = ber.buffer.slice(0, length).toString('utf8');
ber._offset += ber.length;
return true;
}n/a
_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;
}...
function mixin(target) {
target.prototype.toBer = function toBer(ber) {
if (!ber || !(ber instanceof BerWriter))
throw new TypeError('ber (BerWriter) required');
ber.startSequence(TYPES[this.type]);
ber = this._toBer(ber);
ber.endSequence();
return ber;
};
}
module.exports = {
isFilter: isFilter,
...function DeleteResponse(options) {
options = options || {};
assert.object(options);
options.protocolOp = Protocol.LDAP_REP_DELETE;
LDAPResult.call(this, options);
}n/a
function LDAPResult(options) {
options = options || {};
assert.object(options);
assert.optionalNumber(options.status);
assert.optionalString(options.matchedDN);
assert.optionalString(options.errorMessage);
assert.optionalArrayOfString(options.referrals);
LDAPMessage.call(this, options);
this.status = options.status || 0; // LDAP SUCCESS
this.matchedDN = options.matchedDN || '';
this.errorMessage = options.errorMessage || '';
this.referrals = options.referrals || [];
this.connection = options.connection || null;
}n/a
EntryAlreadyExistsError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
constructor = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function LDAPError(message, dn, caller) {
if (Error.captureStackTrace)
Error.captureStackTrace(this, caller || LDAPError);
this.lde_message = message;
this.lde_dn = dn;
}n/a
function EntryChangeNotificationControl(options) {
assert.optionalObject(options);
options = options || {};
options.type = EntryChangeNotificationControl.OID;
if (options.value) {
if (Buffer.isBuffer(options.value)) {
this.parse(options.value);
} else if (typeof (options.value) === 'object') {
this._value = options.value;
} else {
throw new TypeError('options.value must be a Buffer or Object');
}
options.value = null;
}
Control.call(this, options);
}n/a
function Control(options) {
assert.optionalObject(options);
options = options || {};
assert.optionalString(options.type);
assert.optionalBool(options.criticality);
if (options.value) {
assert.buffer(options.value);
}
this.type = options.type || '';
this.criticality = options.critical || options.criticality || false;
this.value = options.value || null;
}n/a
_json = function (obj) {
obj.controlValue = this.value;
return obj;
}n/a
_toBer = function (ber) {
assert.ok(ber);
if (!this._value)
return;
var writer = new BerWriter();
writer.startSequence();
writer.writeInt(this.value.changeType);
if (this.value.previousDN)
writer.writeString(this.value.previousDN);
writer.writeInt(parseInt(this.value.changeNumber, 10));
writer.endSequence();
ber.writeBuffer(writer.buffer, 0x04);
}...
function mixin(target) {
target.prototype.toBer = function toBer(ber) {
if (!ber || !(ber instanceof BerWriter))
throw new TypeError('ber (BerWriter) required');
ber.startSequence(TYPES[this.type]);
ber = this._toBer(ber);
ber.endSequence();
return ber;
};
}
module.exports = {
isFilter: isFilter,
...function parse(buffer) {
assert.ok(buffer);
var ber = new BerReader(buffer);
if (ber.readSequence()) {
this._value = {
changeType: ber.readInt()
};
// if the operation was moddn, then parse the optional previousDN attr
if (this._value.changeType === 8)
this._value.previousDN = ber.readString();
this._value.changeNumber = ber.readInt();
return true;
}
return false;
}n/a
function EqualityFilter(options) {
parents.EqualityFilter.call(this, options);
}n/a
function EqualityFilter(options) {
if (typeof (options) === 'object') {
assert.string(options.attribute, 'options.attribute');
this.attribute = options.attribute;
// Prefer Buffers over strings to make filter cloning easier
if (options.raw) {
this.raw = options.raw;
} else {
this.raw = new Buffer(options.value);
}
} else {
this.raw = new Buffer(0);
}
var self = this;
this.__defineGetter__('type', function () { return 'equal'; });
this.__defineGetter__('value', function () {
return (Buffer.isBuffer(self.raw)) ? self.raw.toString() : self.raw;
});
this.__defineSetter__('value', function (data) {
if (typeof (data) === 'string') {
self.raw = new Buffer(data);
} else if (Buffer.isBuffer(data)) {
self.raw = new Buffer(data.length);
data.copy(self.raw);
} else {
self.raw = data;
}
});
this.__defineGetter__('json', function () {
return {
type: 'EqualityMatch',
attribute: self.attribute,
value: self.value
};
});
}n/a
_toBer = function (ber) {
assert.ok(ber);
ber.writeString(this.attribute);
ber.writeBuffer(this.raw, ASN1.OctetString);
return ber;
}...
function mixin(target) {
target.prototype.toBer = function toBer(ber) {
if (!ber || !(ber instanceof BerWriter))
throw new TypeError('ber (BerWriter) required');
ber.startSequence(TYPES[this.type]);
ber = this._toBer(ber);
ber.endSequence();
return ber;
};
}
module.exports = {
isFilter: isFilter,
...matches = function (target, strictAttrCase) {
assert.object(target, 'target');
var tv = parents.getAttrValue(target, this.attribute, strictAttrCase);
var value = this.value;
if (this.attribute.toLowerCase() === 'objectclass') {
/*
* Perform case-insensitive match for objectClass since nearly every LDAP
* implementation behaves in this manner.
*/
value = value.toLowerCase();
return parents.testValues(function (v) {
return value === v.toLowerCase();
}, tv);
} else {
return parents.testValues(function (v) {
return value === v;
}, tv);
}
}...
dn: req.dn.toString(),
attributes: {
objectclass: ['organization', 'top'],
o: 'example'
}
};
if (req.filter.matches(obj.attributes))
res.send(obj);
res.end();
});
server.listen(1389, function() {
console.log('ldapjs listening at ' + server.url);
...parse = function (ber) {
assert.ok(ber);
this.attribute = ber.readString().toLowerCase();
this.value = ber.readString(ASN1.OctetString, true);
if (this.attribute === 'objectclass')
this.value = this.value.toLowerCase();
return true;
}n/a
function toBer(ber) {
if (!ber || !(ber instanceof BerWriter))
throw new TypeError('ber (BerWriter) required');
ber.startSequence(TYPES[this.type]);
ber = this._toBer(ber);
ber.endSequence();
return ber;
}n/a
function Filter() {
}n/a
matches = function (target, strictAttrCase) {
assert.object(target, 'target');
var tv = helpers.getAttrValue(target, this.attribute, strictAttrCase);
var value = this.value;
return helpers.testValues(function (v) {
return value === v;
}, tv);
}...
dn: req.dn.toString(),
attributes: {
objectclass: ['organization', 'top'],
o: 'example'
}
};
if (req.filter.matches(obj.attributes))
res.send(obj);
res.end();
});
server.listen(1389, function() {
console.log('ldapjs listening at ' + server.url);
...toString = function () {
return ('(' + helpers.escape(this.attribute) +
'=' + helpers.escape(this.value) + ')');
}...
```javascript
var ldap = require('ldapjs');
var server = ldap.createServer();
server.search('dc=example', function(req, res, next) {
var obj = {
dn: req.dn.toString(),
attributes: {
objectclass: ['organization', 'top'],
o: 'example'
}
};
if (req.filter.matches(obj.attributes))
...function ExtendedRequest(options) {
options = options || {};
assert.object(options);
assert.optionalString(options.requestName);
if (options.requestValue &&
!(Buffer.isBuffer(options.requestValue) ||
typeof (options.requestValue) === 'string')) {
throw new TypeError('options.requestValue must be a buffer or a string');
}
options.protocolOp = Protocol.LDAP_REQ_EXTENSION;
LDAPMessage.call(this, options);
this.requestName = options.requestName || '';
this.requestValue = options.requestValue;
}n/a
function LDAPMessage(options) {
assert.object(options);
this.messageID = options.messageID || 0;
this.protocolOp = options.protocolOp || undefined;
this.controls = options.controls ? options.controls.slice(0) : [];
this.log = options.log;
}n/a
_json = function (j) {
assert.ok(j);
j.requestName = this.requestName;
j.requestValue = (Buffer.isBuffer(this.requestValue)) ?
this.requestValue.toString('hex') : this.requestValue;
return j;
}n/a
_parse = function (ber) {
assert.ok(ber);
this.requestName = ber.readString(0x80);
if (ber.peek() === 0x81)
try {
this.requestValue = ber.readString(0x81);
} catch (e) {
this.requestValue = ber.readBuffer(0x81);
}
return true;
}n/a
_toBer = function (ber) {
assert.ok(ber);
ber.writeString(this.requestName, 0x80);
if (Buffer.isBuffer(this.requestValue)) {
ber.writeBuffer(this.requestValue, 0x81);
} else if (typeof (this.requestValue) === 'string') {
ber.writeString(this.requestValue, 0x81);
}
return ber;
}...
function mixin(target) {
target.prototype.toBer = function toBer(ber) {
if (!ber || !(ber instanceof BerWriter))
throw new TypeError('ber (BerWriter) required');
ber.startSequence(TYPES[this.type]);
ber = this._toBer(ber);
ber.endSequence();
return ber;
};
}
module.exports = {
isFilter: isFilter,
...function ExtendedResponse(options) {
options = options || {};
assert.object(options);
assert.optionalString(options.responseName);
assert.optionalString(options.responsevalue);
this.responseName = options.responseName || undefined;
this.responseValue = options.responseValue || undefined;
options.protocolOp = Protocol.LDAP_REP_EXTENSION;
LDAPResult.call(this, options);
}n/a
function LDAPResult(options) {
options = options || {};
assert.object(options);
assert.optionalNumber(options.status);
assert.optionalString(options.matchedDN);
assert.optionalString(options.errorMessage);
assert.optionalArrayOfString(options.referrals);
LDAPMessage.call(this, options);
this.status = options.status || 0; // LDAP SUCCESS
this.matchedDN = options.matchedDN || '';
this.errorMessage = options.errorMessage || '';
this.referrals = options.referrals || [];
this.connection = options.connection || null;
}n/a
_json = function (j) {
assert.ok(j);
j = LDAPResult.prototype._json.call(this, j);
j.responseName = this.responseName;
j.responseValue = this.responseValue;
return j;
}n/a
_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;
}n/a
_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;
}...
function mixin(target) {
target.prototype.toBer = function toBer(ber) {
if (!ber || !(ber instanceof BerWriter))
throw new TypeError('ber (BerWriter) required');
ber.startSequence(TYPES[this.type]);
ber = this._toBer(ber);
ber.endSequence();
return ber;
};
}
module.exports = {
isFilter: isFilter,
...function ExtensibleFilter(options) {
parents.ExtensibleFilter.call(this, options);
}n/a
function ExtensibleFilter(options) {
if (typeof (options) === 'object') {
assert.optionalString(options.rule, 'options.rule');
assert.optionalString(options.matchType, 'options.matchType');
assert.optionalString(options.attribute, 'options.attribute');
assert.optionalString(options.value, 'options.value');
} else {
options = {};
}
if (options.matchType !== undefined) {
this.matchType = options.matchType;
} else {
this.matchType = options.attribute;
}
this.dnAttributes = options.dnAttributes || false;
this.rule = options.rule;
this.value = (options.value !== undefined) ? options.value : '';
var self = this;
this.__defineGetter__('type', function () { return 'ext'; });
this.__defineGetter__('json', function () {
return {
type: 'ExtensibleMatch',
matchRule: self.rule,
matchType: self.matchType,
matchValue: self.value,
dnAttributes: self.dnAttributes
};
});
this.__defineGetter__('matchingRule', function () {
return self.rule;
});
this.__defineGetter__('matchValue', function () {
return self.value;
});
this.__defineGetter__('attribute', function () {
return this.matchType;
});
this.__defineSetter__('attribute', function (value) {
this.matchType = value;
});
}n/a
_toBer = function (ber) {
assert.ok(ber);
if (this.rule)
ber.writeString(this.rule, 0x81);
if (this.matchType)
ber.writeString(this.matchType, 0x82);
ber.writeString(this.value, 0x83);
if (this.dnAttributes)
ber.writeBoolean(this.dnAttributes, 0x84);
return ber;
}...
function mixin(target) {
target.prototype.toBer = function toBer(ber) {
if (!ber || !(ber instanceof BerWriter))
throw new TypeError('ber (BerWriter) required');
ber.startSequence(TYPES[this.type]);
ber = this._toBer(ber);
ber.endSequence();
return ber;
};
}
module.exports = {
isFilter: isFilter,
...parse = function (ber) {
var end = ber.offset + ber.length;
while (ber.offset < end) {
var tag = ber.peek();
switch (tag) {
case 0x81:
this.rule = ber.readString(tag);
break;
case 0x82:
this.matchType = ber.readString(tag);
break;
case 0x83:
this.value = ber.readString(tag);
break;
case 0x84:
this.dnAttributes = ber.readBoolean(tag);
break;
default:
throw new Error('Invalid ext_match filter type: 0x' + tag.toString(16));
}
}
return true;
}n/a
function toBer(ber) {
if (!ber || !(ber instanceof BerWriter))
throw new TypeError('ber (BerWriter) required');
ber.startSequence(TYPES[this.type]);
ber = this._toBer(ber);
ber.endSequence();
return ber;
}n/a
function Filter() {
}n/a
matches = function () {
// Consumers must implement this themselves
throw new Error('ext match implementation missing');
}...
dn: req.dn.toString(),
attributes: {
objectclass: ['organization', 'top'],
o: 'example'
}
};
if (req.filter.matches(obj.attributes))
res.send(obj);
res.end();
});
server.listen(1389, function() {
console.log('ldapjs listening at ' + server.url);
...toString = function () {
var str = '(';
if (this.matchType)
str += this.matchType;
str += ':';
if (this.dnAttributes)
str += 'dn:';
if (this.rule)
str += this.rule + ':';
return (str + '=' + this.value + ')');
}...
```javascript
var ldap = require('ldapjs');
var server = ldap.createServer();
server.search('dc=example', function(req, res, next) {
var obj = {
dn: req.dn.toString(),
attributes: {
objectclass: ['organization', 'top'],
o: 'example'
}
};
if (req.filter.matches(obj.attributes))
...function GreaterThanEqualsFilter(options) {
parents.GreaterThanEqualsFilter.call(this, options);
}n/a
function GreaterThanEqualsFilter(options) {
if (typeof (options) === 'object') {
assert.string(options.attribute, 'options.attribute');
assert.string(options.value, 'options.value');
this.attribute = options.attribute;
this.value = options.value;
}
var self = this;
this.__defineGetter__('type', function () { return 'ge'; });
this.__defineGetter__('json', function () {
return {
type: 'GreaterThanEqualsMatch',
attribute: self.attribute,
value: self.value
};
});
}n/a
_toBer = function (ber) {
assert.ok(ber);
ber.writeString(this.attribute);
ber.writeString(this.value);
return ber;
}...
function mixin(target) {
target.prototype.toBer = function toBer(ber) {
if (!ber || !(ber instanceof BerWriter))
throw new TypeError('ber (BerWriter) required');
ber.startSequence(TYPES[this.type]);
ber = this._toBer(ber);
ber.endSequence();
return ber;
};
}
module.exports = {
isFilter: isFilter,
...parse = function (ber) {
assert.ok(ber);
this.attribute = ber.readString().toLowerCase();
this.value = ber.readString();
return true;
}n/a
function toBer(ber) {
if (!ber || !(ber instanceof BerWriter))
throw new TypeError('ber (BerWriter) required');
ber.startSequence(TYPES[this.type]);
ber = this._toBer(ber);
ber.endSequence();
return ber;
}n/a
function Filter() {
}n/a
matches = function (target, strictAttrCase) {
assert.object(target, 'target');
var tv = helpers.getAttrValue(target, this.attribute, strictAttrCase);
var value = this.value;
return helpers.testValues(function (v) {
return value <= v;
}, tv);
}...
dn: req.dn.toString(),
attributes: {
objectclass: ['organization', 'top'],
o: 'example'
}
};
if (req.filter.matches(obj.attributes))
res.send(obj);
res.end();
});
server.listen(1389, function() {
console.log('ldapjs listening at ' + server.url);
...toString = function () {
return ('(' + helpers.escape(this.attribute) +
'>=' + helpers.escape(this.value) + ')');
}...
```javascript
var ldap = require('ldapjs');
var server = ldap.createServer();
server.search('dc=example', function(req, res, next) {
var obj = {
dn: req.dn.toString(),
attributes: {
objectclass: ['organization', 'top'],
o: 'example'
}
};
if (req.filter.matches(obj.attributes))
...InappropriateAuthenticationError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
constructor = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function LDAPError(message, dn, caller) {
if (Error.captureStackTrace)
Error.captureStackTrace(this, caller || LDAPError);
this.lde_message = message;
this.lde_dn = dn;
}n/a
InappropriateMatchingError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
constructor = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function LDAPError(message, dn, caller) {
if (Error.captureStackTrace)
Error.captureStackTrace(this, caller || LDAPError);
this.lde_message = message;
this.lde_dn = dn;
}n/a
InsufficientAccessRightsError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
constructor = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function LDAPError(message, dn, caller) {
if (Error.captureStackTrace)
Error.captureStackTrace(this, caller || LDAPError);
this.lde_message = message;
this.lde_dn = dn;
}n/a
InvalidAttributeSyntaxError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
constructor = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function LDAPError(message, dn, caller) {
if (Error.captureStackTrace)
Error.captureStackTrace(this, caller || LDAPError);
this.lde_message = message;
this.lde_dn = dn;
}n/a
InvalidCredentialsError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
constructor = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function LDAPError(message, dn, caller) {
if (Error.captureStackTrace)
Error.captureStackTrace(this, caller || LDAPError);
this.lde_message = message;
this.lde_dn = dn;
}n/a
InvalidDnSyntaxError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
constructor = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function LDAPError(message, dn, caller) {
if (Error.captureStackTrace)
Error.captureStackTrace(this, caller || LDAPError);
this.lde_message = message;
this.lde_dn = dn;
}n/a
function LDAPError(message, dn, caller) {
if (Error.captureStackTrace)
Error.captureStackTrace(this, caller || LDAPError);
this.lde_message = message;
this.lde_dn = dn;
}n/a
function Error() { [native code] }n/a
function Error() { [native code] }n/a
function captureStackTrace() { [native code] }n/a
function LDAPMessage(options) {
assert.object(options);
this.messageID = options.messageID || 0;
this.protocolOp = options.protocolOp || undefined;
this.controls = options.controls ? options.controls.slice(0) : [];
this.log = options.log;
}n/a
parse = function (ber) {
assert.ok(ber);
if (this.log.trace())
this.log.trace('parse: data=%s', util.inspect(ber.buffer));
// Delegate off to the specific type to parse
this._parse(ber, ber.length);
// Look for controls
if (ber.peek() === 0xa0) {
ber.readSequence();
var end = ber.offset + ber.length;
while (ber.offset < end) {
var c = getControl(ber);
if (c)
this.controls.push(c);
}
}
if (this.log.trace())
this.log.trace('Parsing done: %j', this.json);
return true;
}n/a
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();
if (this.controls && this.controls.length) {
writer.startSequence(0xa0);
this.controls.forEach(function (c) {
c.toBer(writer);
});
writer.endSequence();
}
writer.endSequence();
return writer.buffer;
}n/a
toString = function () {
return JSON.stringify(this.json);
}...
```javascript
var ldap = require('ldapjs');
var server = ldap.createServer();
server.search('dc=example', function(req, res, next) {
var obj = {
dn: req.dn.toString(),
attributes: {
objectclass: ['organization', 'top'],
o: 'example'
}
};
if (req.filter.matches(obj.attributes))
...function LDAPResult(options) {
options = options || {};
assert.object(options);
assert.optionalNumber(options.status);
assert.optionalString(options.matchedDN);
assert.optionalString(options.errorMessage);
assert.optionalArrayOfString(options.referrals);
LDAPMessage.call(this, options);
this.status = options.status || 0; // LDAP SUCCESS
this.matchedDN = options.matchedDN || '';
this.errorMessage = options.errorMessage || '';
this.referrals = options.referrals || [];
this.connection = options.connection || null;
}n/a
function LDAPMessage(options) {
assert.object(options);
this.messageID = options.messageID || 0;
this.protocolOp = options.protocolOp || undefined;
this.controls = options.controls ? options.controls.slice(0) : [];
this.log = options.log;
}n/a
_json = function (j) {
assert.ok(j);
j.status = this.status;
j.matchedDN = this.matchedDN;
j.errorMessage = this.errorMessage;
j.referrals = this.referrals;
return j;
}n/a
_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;
}n/a
_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;
}...
function mixin(target) {
target.prototype.toBer = function toBer(ber) {
if (!ber || !(ber instanceof BerWriter))
throw new TypeError('ber (BerWriter) required');
ber.startSequence(TYPES[this.type]);
ber = this._toBer(ber);
ber.endSequence();
return ber;
};
}
module.exports = {
isFilter: isFilter,
...end = function (status) {
assert.ok(this.connection);
if (typeof (status) === 'number')
this.status = status;
var ber = this.toBer();
if (this.log.debug())
this.log.debug('%s: sending: %j', this.connection.ldap.id, this.json);
try {
var self = this;
this.connection.write(ber);
if (self._dtraceOp && self._dtraceId) {
dtrace.fire('server-' + self._dtraceOp + '-done', function () {
var c = self.connection || {ldap: {}};
return [
self._dtraceId || 0,
(c.remoteAddress || ''),
c.ldap.bindDN ? c.ldap.bindDN.toString() : '',
(self.requestDN ? self.requestDN.toString() : ''),
status || self.status,
self.errorMessage
];
});
}
} catch (e) {
this.log.warn(e, '%s failure to write message %j',
this.connection.ldap.id, this.json);
}
}...
o: 'example'
}
};
if (req.filter.matches(obj.attributes))
res.send(obj);
res.end();
});
server.listen(1389, function() {
console.log('ldapjs listening at ' + server.url);
});
```
...function LessThanEqualsFilter(options) {
parents.LessThanEqualsFilter.call(this, options);
}n/a
function LessThanEqualsFilter(options) {
if (typeof (options) === 'object') {
assert.string(options.attribute, 'options.attribute');
assert.string(options.value, 'options.attribute');
this.attribute = options.attribute;
this.value = options.value;
}
var self = this;
this.__defineGetter__('type', function () { return 'le'; });
this.__defineGetter__('json', function () {
return {
type: 'LessThanEqualsMatch',
attribute: self.attribute,
value: self.value
};
});
}n/a
_toBer = function (ber) {
assert.ok(ber);
ber.writeString(this.attribute);
ber.writeString(this.value);
return ber;
}...
function mixin(target) {
target.prototype.toBer = function toBer(ber) {
if (!ber || !(ber instanceof BerWriter))
throw new TypeError('ber (BerWriter) required');
ber.startSequence(TYPES[this.type]);
ber = this._toBer(ber);
ber.endSequence();
return ber;
};
}
module.exports = {
isFilter: isFilter,
...parse = function (ber) {
assert.ok(ber);
this.attribute = ber.readString().toLowerCase();
this.value = ber.readString();
return true;
}n/a
function toBer(ber) {
if (!ber || !(ber instanceof BerWriter))
throw new TypeError('ber (BerWriter) required');
ber.startSequence(TYPES[this.type]);
ber = this._toBer(ber);
ber.endSequence();
return ber;
}n/a
function Filter() {
}n/a
matches = function (target, strictAttrCase) {
assert.object(target, 'target');
var tv = helpers.getAttrValue(target, this.attribute, strictAttrCase);
var value = this.value;
return helpers.testValues(function (v) {
return value >= v;
}, tv);
}...
dn: req.dn.toString(),
attributes: {
objectclass: ['organization', 'top'],
o: 'example'
}
};
if (req.filter.matches(obj.attributes))
res.send(obj);
res.end();
});
server.listen(1389, function() {
console.log('ldapjs listening at ' + server.url);
...toString = function () {
return ('(' + helpers.escape(this.attribute) +
'<=' + helpers.escape(this.value) + ')');
}...
```javascript
var ldap = require('ldapjs');
var server = ldap.createServer();
server.search('dc=example', function(req, res, next) {
var obj = {
dn: req.dn.toString(),
attributes: {
objectclass: ['organization', 'top'],
o: 'example'
}
};
if (req.filter.matches(obj.attributes))
...LoopDetectError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
constructor = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function LDAPError(message, dn, caller) {
if (Error.captureStackTrace)
Error.captureStackTrace(this, caller || LDAPError);
this.lde_message = message;
this.lde_dn = dn;
}n/a
function ModifyDNRequest(options) {
options = options || {};
assert.object(options);
assert.optionalBool(options.deleteOldRdn);
lassert.optionalStringDN(options.entry);
lassert.optionalDN(options.newRdn);
lassert.optionalDN(options.newSuperior);
options.protocolOp = Protocol.LDAP_REQ_MODRDN;
LDAPMessage.call(this, options);
this.entry = options.entry || null;
this.newRdn = options.newRdn || null;
this.deleteOldRdn = options.deleteOldRdn || true;
this.newSuperior = options.newSuperior || null;
}n/a
function LDAPMessage(options) {
assert.object(options);
this.messageID = options.messageID || 0;
this.protocolOp = options.protocolOp || undefined;
this.controls = options.controls ? options.controls.slice(0) : [];
this.log = options.log;
}n/a
_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;
}n/a
_parse = function (ber) {
assert.ok(ber);
this.entry = ber.readString();
this.newRdn = dn.parse(ber.readString());
this.deleteOldRdn = ber.readBoolean();
if (ber.peek() === 0x80)
this.newSuperior = dn.parse(ber.readString(0x80));
return true;
}n/a
_toBer = function (ber) {
//assert.ok(ber);
ber.writeString(this.entry.toString());
ber.writeString(this.newRdn.toString());
ber.writeBoolean(this.deleteOldRdn);
if (this.newSuperior) {
var s = this.newSuperior.toString();
var len = Buffer.byteLength(s);
ber.writeByte(0x80); // MODIFY_DN_REQUEST_NEW_SUPERIOR_TAG
ber.writeByte(len);
ber._ensure(len);
ber._buf.write(s, ber._offset);
ber._offset += len;
}
return ber;
}...
function mixin(target) {
target.prototype.toBer = function toBer(ber) {
if (!ber || !(ber instanceof BerWriter))
throw new TypeError('ber (BerWriter) required');
ber.startSequence(TYPES[this.type]);
ber = this._toBer(ber);
ber.endSequence();
return ber;
};
}
module.exports = {
isFilter: isFilter,
...function ModifyDNResponse(options) {
options = options || {};
assert.object(options);
options.protocolOp = Protocol.LDAP_REP_MODRDN;
LDAPResult.call(this, options);
}n/a
function LDAPResult(options) {
options = options || {};
assert.object(options);
assert.optionalNumber(options.status);
assert.optionalString(options.matchedDN);
assert.optionalString(options.errorMessage);
assert.optionalArrayOfString(options.referrals);
LDAPMessage.call(this, options);
this.status = options.status || 0; // LDAP SUCCESS
this.matchedDN = options.matchedDN || '';
this.errorMessage = options.errorMessage || '';
this.referrals = options.referrals || [];
this.connection = options.connection || null;
}n/a
function ModifyRequest(options) {
options = options || {};
assert.object(options);
lassert.optionalStringDN(options.object);
lassert.optionalArrayOfAttribute(options.attributes);
options.protocolOp = Protocol.LDAP_REQ_MODIFY;
LDAPMessage.call(this, options);
this.object = options.object || null;
this.changes = options.changes ? options.changes.slice(0) : [];
}n/a
function LDAPMessage(options) {
assert.object(options);
this.messageID = options.messageID || 0;
this.protocolOp = options.protocolOp || undefined;
this.controls = options.controls ? options.controls.slice(0) : [];
this.log = options.log;
}n/a
_json = function (j) {
assert.ok(j);
j.object = this.object;
j.changes = [];
this.changes.forEach(function (c) {
j.changes.push(c.json);
});
return j;
}n/a
_parse = function (ber) {
assert.ok(ber);
this.object = ber.readString();
ber.readSequence();
var end = ber.offset + ber.length;
while (ber.offset < end) {
var c = new Change();
c.parse(ber);
c.modification.type = c.modification.type.toLowerCase();
this.changes.push(c);
}
this.changes.sort(Change.compare);
return true;
}n/a
_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;
}...
function mixin(target) {
target.prototype.toBer = function toBer(ber) {
if (!ber || !(ber instanceof BerWriter))
throw new TypeError('ber (BerWriter) required');
ber.startSequence(TYPES[this.type]);
ber = this._toBer(ber);
ber.endSequence();
return ber;
};
}
module.exports = {
isFilter: isFilter,
...function ModifyResponse(options) {
options = options || {};
assert.object(options);
options.protocolOp = Protocol.LDAP_REP_MODIFY;
LDAPResult.call(this, options);
}n/a
function LDAPResult(options) {
options = options || {};
assert.object(options);
assert.optionalNumber(options.status);
assert.optionalString(options.matchedDN);
assert.optionalString(options.errorMessage);
assert.optionalArrayOfString(options.referrals);
LDAPMessage.call(this, options);
this.status = options.status || 0; // LDAP SUCCESS
this.matchedDN = options.matchedDN || '';
this.errorMessage = options.errorMessage || '';
this.referrals = options.referrals || [];
this.connection = options.connection || null;
}n/a
NamingViolationError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
constructor = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function LDAPError(message, dn, caller) {
if (Error.captureStackTrace)
Error.captureStackTrace(this, caller || LDAPError);
this.lde_message = message;
this.lde_dn = dn;
}n/a
NoSuchAttributeError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
constructor = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function LDAPError(message, dn, caller) {
if (Error.captureStackTrace)
Error.captureStackTrace(this, caller || LDAPError);
this.lde_message = message;
this.lde_dn = dn;
}n/a
NoSuchObjectError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
constructor = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function LDAPError(message, dn, caller) {
if (Error.captureStackTrace)
Error.captureStackTrace(this, caller || LDAPError);
this.lde_message = message;
this.lde_dn = dn;
}n/a
NotAllowedOnNonLeafError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
constructor = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function LDAPError(message, dn, caller) {
if (Error.captureStackTrace)
Error.captureStackTrace(this, caller || LDAPError);
this.lde_message = message;
this.lde_dn = dn;
}n/a
NotAllowedOnRdnError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
constructor = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function LDAPError(message, dn, caller) {
if (Error.captureStackTrace)
Error.captureStackTrace(this, caller || LDAPError);
this.lde_message = message;
this.lde_dn = dn;
}n/a
function NotFilter(options) {
parents.NotFilter.call(this, options);
}n/a
function NotFilter(options) {
if (typeof (options) === 'object') {
assert.object(options.filter, 'options.filter');
} else {
options = {};
}
this.filter = options.filter || {};
var self = this;
this.__defineGetter__('type', function () { return 'not'; });
this.__defineGetter__('json', function () {
return {
type: 'Not',
filter: self.filter
};
});
}n/a
_toBer = function (ber) {
assert.ok(ber);
return this.filter.toBer(ber);
}...
function mixin(target) {
target.prototype.toBer = function toBer(ber) {
if (!ber || !(ber instanceof BerWriter))
throw new TypeError('ber (BerWriter) required');
ber.startSequence(TYPES[this.type]);
ber = this._toBer(ber);
ber.endSequence();
return ber;
};
}
module.exports = {
isFilter: isFilter,
...function toBer(ber) {
if (!ber || !(ber instanceof BerWriter))
throw new TypeError('ber (BerWriter) required');
ber.startSequence(TYPES[this.type]);
ber = this._toBer(ber);
ber.endSequence();
return ber;
}n/a
function Filter() {
}n/a
addFilter = function (filter) {
assert.object(filter, 'filter');
this.filter = filter;
}n/a
matches = function (target, strictAttrCase) {
return !this.filter.matches(target, strictAttrCase);
}...
dn: req.dn.toString(),
attributes: {
objectclass: ['organization', 'top'],
o: 'example'
}
};
if (req.filter.matches(obj.attributes))
res.send(obj);
res.end();
});
server.listen(1389, function() {
console.log('ldapjs listening at ' + server.url);
...toString = function () {
return '(!' + this.filter.toString() + ')';
}...
```javascript
var ldap = require('ldapjs');
var server = ldap.createServer();
server.search('dc=example', function(req, res, next) {
var obj = {
dn: req.dn.toString(),
attributes: {
objectclass: ['organization', 'top'],
o: 'example'
}
};
if (req.filter.matches(obj.attributes))
...ObjectclassModsProhibitedError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
constructor = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function LDAPError(message, dn, caller) {
if (Error.captureStackTrace)
Error.captureStackTrace(this, caller || LDAPError);
this.lde_message = message;
this.lde_dn = dn;
}n/a
ObjectclassViolationError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
constructor = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function LDAPError(message, dn, caller) {
if (Error.captureStackTrace)
Error.captureStackTrace(this, caller || LDAPError);
this.lde_message = message;
this.lde_dn = dn;
}n/a
OperationsError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
constructor = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function LDAPError(message, dn, caller) {
if (Error.captureStackTrace)
Error.captureStackTrace(this, caller || LDAPError);
this.lde_message = message;
this.lde_dn = dn;
}n/a
function OrFilter(options) {
parents.OrFilter.call(this, options);
}n/a
function OrFilter(options) {
if (typeof (options) === 'object') {
assert.arrayOfObject(options.filters, 'options.filters');
} else {
options = {};
}
this.filters = options.filters ? options.filters.slice() : [];
var self = this;
this.__defineGetter__('type', function () { return 'or'; });
this.__defineGetter__('json', function () {
return {
type: 'Or',
filters: self.filters
};
});
}n/a
_toBer = function (ber) {
assert.ok(ber);
this.filters.forEach(function (f) {
ber = f.toBer(ber);
});
return ber;
}...
function mixin(target) {
target.prototype.toBer = function toBer(ber) {
if (!ber || !(ber instanceof BerWriter))
throw new TypeError('ber (BerWriter) required');
ber.startSequence(TYPES[this.type]);
ber = this._toBer(ber);
ber.endSequence();
return ber;
};
}
module.exports = {
isFilter: isFilter,
...function toBer(ber) {
if (!ber || !(ber instanceof BerWriter))
throw new TypeError('ber (BerWriter) required');
ber.startSequence(TYPES[this.type]);
ber = this._toBer(ber);
ber.endSequence();
return ber;
}n/a
function Filter() {
}n/a
addFilter = function (filter) {
assert.object(filter, 'filter');
this.filters.push(filter);
}n/a
matches = function (target, strictAttrCase) {
assert.object(target, 'target');
for (var i = 0; i < this.filters.length; i++) {
if (this.filters[i].matches(target, strictAttrCase))
return true;
}
return false;
}...
dn: req.dn.toString(),
attributes: {
objectclass: ['organization', 'top'],
o: 'example'
}
};
if (req.filter.matches(obj.attributes))
res.send(obj);
res.end();
});
server.listen(1389, function() {
console.log('ldapjs listening at ' + server.url);
...toString = function () {
var str = '(|';
this.filters.forEach(function (f) {
str += f.toString();
});
str += ')';
return str;
}...
```javascript
var ldap = require('ldapjs');
var server = ldap.createServer();
server.search('dc=example', function(req, res, next) {
var obj = {
dn: req.dn.toString(),
attributes: {
objectclass: ['organization', 'top'],
o: 'example'
}
};
if (req.filter.matches(obj.attributes))
...OtherError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
constructor = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function LDAPError(message, dn, caller) {
if (Error.captureStackTrace)
Error.captureStackTrace(this, caller || LDAPError);
this.lde_message = message;
this.lde_dn = dn;
}n/a
function PagedResultsControl(options) {
assert.optionalObject(options);
options = options || {};
options.type = PagedResultsControl.OID;
if (options.value) {
if (Buffer.isBuffer(options.value)) {
this.parse(options.value);
} else if (typeof (options.value) === 'object') {
this._value = options.value;
} else {
throw new TypeError('options.value must be a Buffer or Object');
}
options.value = null;
}
Control.call(this, options);
}n/a
function Control(options) {
assert.optionalObject(options);
options = options || {};
assert.optionalString(options.type);
assert.optionalBool(options.criticality);
if (options.value) {
assert.buffer(options.value);
}
this.type = options.type || '';
this.criticality = options.critical || options.criticality || false;
this.value = options.value || null;
}n/a
_json = function (obj) {
obj.controlValue = this.value;
return obj;
}n/a
_toBer = function (ber) {
assert.ok(ber);
if (!this._value)
return;
var writer = new BerWriter();
writer.startSequence();
writer.writeInt(this.value.size);
if (this.value.cookie && this.value.cookie.length > 0) {
writer.writeBuffer(this.value.cookie, asn1.Ber.OctetString);
} else {
writer.writeString(''); //writeBuffer rejects zero-length buffers
}
writer.endSequence();
ber.writeBuffer(writer.buffer, 0x04);
}...
function mixin(target) {
target.prototype.toBer = function toBer(ber) {
if (!ber || !(ber instanceof BerWriter))
throw new TypeError('ber (BerWriter) required');
ber.startSequence(TYPES[this.type]);
ber = this._toBer(ber);
ber.endSequence();
return ber;
};
}
module.exports = {
isFilter: isFilter,
...function parse(buffer) {
assert.ok(buffer);
var ber = new BerReader(buffer);
if (ber.readSequence()) {
this._value = {};
this._value.size = ber.readInt();
this._value.cookie = ber.readString(asn1.Ber.OctetString, true);
//readString returns '' instead of a zero-length buffer
if (!this._value.cookie)
this._value.cookie = new Buffer(0);
return true;
}
return false;
}n/a
function Parser(options) {
assert.object(options);
assert.object(options.log);
EventEmitter.call(this);
this.buffer = null;
this.log = options.log;
}n/a
function EventEmitter() {
EventEmitter.init.call(this);
}n/a
getMessage = function (ber) {
assert.ok(ber);
var self = this;
var messageID = ber.readInt();
var type = ber.readSequence();
var Message;
switch (type) {
case Protocol.LDAP_REQ_ABANDON:
Message = AbandonRequest;
break;
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_REF:
Message = SearchReference;
break;
case Protocol.LDAP_REP_SEARCH:
Message = SearchResponse;
break;
case Protocol.LDAP_REQ_UNBIND:
Message = UnbindRequest;
break;
default:
this.emit('error',
new Error('Op 0x' + (type ? type.toString(16) : '??') +
' not supported'),
new LDAPResult({
messageID: messageID,
protocolOp: type || Protocol.LDAP_REP_EXTENSION
}));
return false;
}
return new Message({
messageID: messageID,
log: self.log
});
}n/a
write = function (data) {
if (!data || !Buffer.isBuffer(data))
throw new TypeError('data (buffer) required');
var nextMessage = null;
var self = this;
function end() {
if (nextMessage)
return self.write(nextMessage);
return true;
}
self.buffer = (self.buffer ? Buffer.concat([self.buffer, data]) : data);
var ber = new BerReader(self.buffer);
var foundSeq = false;
try {
foundSeq = ber.readSequence();
} catch (e) {
this.emit('error', e);
}
if (!foundSeq || ber.remain < ber.length) {
// ENOTENOUGH
return false;
} else if (ber.remain > ber.length) {
// ETOOMUCH
// This is sort of ugly, but allows us to make miminal copies
nextMessage = self.buffer.slice(ber.offset + ber.length);
ber._size = ber.offset + ber.length;
assert.equal(ber.remain, ber.length);
}
// If we're here, ber holds the message, and nextMessage is temporarily
// pointing at the next sequence of data (if it exists)
self.buffer = null;
var message;
try {
// Bail here if peer isn't speaking protocol at all
message = this.getMessage(ber);
if (!message) {
return end();
}
message.parse(ber);
} catch (e) {
this.emit('error', e, message);
return false;
}
this.emit('message', message);
return end();
}n/a
function PersistentSearch() {
this.clientList = [];
}n/a
addClient = function (req, res, callback) {
if (typeof (req) !== 'object')
throw new TypeError('req must be an object');
if (typeof (res) !== 'object')
throw new TypeError('res must be an object');
if (callback && typeof (callback) !== 'function')
throw new TypeError('callback must be a function');
var log = req.log;
var client = {};
client.req = req;
client.res = res;
log.debug('%s storing client', req.logId);
this.clientList.push(client);
log.debug('%s stored client', req.logId);
log.debug('%s total number of clients %s',
req.logId, this.clientList.length);
if (callback)
callback(client);
}n/a
removeClient = function (req, res, callback) {
if (typeof (req) !== 'object')
throw new TypeError('req must be an object');
if (typeof (res) !== 'object')
throw new TypeError('res must be an object');
if (callback && typeof (callback) !== 'function')
throw new TypeError('callback must be a function');
var log = req.log;
log.debug('%s removing client', req.logId);
var client = {};
client.req = req;
client.res = res;
// remove the client if it exists
this.clientList.forEach(function (element, index, array) {
if (element.req === client.req) {
log.debug('%s removing client from list', req.logId);
array.splice(index, 1);
}
});
log.debug('%s number of persistent search clients %s',
req.logId, this.clientList.length);
if (callback)
callback(client);
}n/a
function PersistentSearchControl(options) {
assert.optionalObject(options);
options = options || {};
options.type = PersistentSearchControl.OID;
if (options.value) {
if (Buffer.isBuffer(options.value)) {
this.parse(options.value);
} else if (typeof (options.value) === 'object') {
this._value = options.value;
} else {
throw new TypeError('options.value must be a Buffer or Object');
}
options.value = null;
}
Control.call(this, options);
}n/a
function Control(options) {
assert.optionalObject(options);
options = options || {};
assert.optionalString(options.type);
assert.optionalBool(options.criticality);
if (options.value) {
assert.buffer(options.value);
}
this.type = options.type || '';
this.criticality = options.critical || options.criticality || false;
this.value = options.value || null;
}n/a
_json = function (obj) {
obj.controlValue = this.value;
return obj;
}n/a
_toBer = function (ber) {
assert.ok(ber);
if (!this._value)
return;
var writer = new BerWriter();
writer.startSequence();
writer.writeInt(this.value.changeTypes);
writer.writeBoolean(this.value.changesOnly);
writer.writeBoolean(this.value.returnECs);
writer.endSequence();
ber.writeBuffer(writer.buffer, 0x04);
}...
function mixin(target) {
target.prototype.toBer = function toBer(ber) {
if (!ber || !(ber instanceof BerWriter))
throw new TypeError('ber (BerWriter) required');
ber.startSequence(TYPES[this.type]);
ber = this._toBer(ber);
ber.endSequence();
return ber;
};
}
module.exports = {
isFilter: isFilter,
...function parse(buffer) {
assert.ok(buffer);
var ber = new BerReader(buffer);
if (ber.readSequence()) {
this._value = {
changeTypes: ber.readInt(),
changesOnly: ber.readBoolean(),
returnECs: ber.readBoolean()
};
return true;
}
return false;
}n/a
function PresenceFilter(options) {
parents.PresenceFilter.call(this, options);
}n/a
function PresenceFilter(options) {
if (typeof (options) === 'object') {
assert.string(options.attribute, 'options.attribute');
this.attribute = options.attribute;
}
var self = this;
this.__defineGetter__('type', function () { return 'present'; });
this.__defineGetter__('json', function () {
return {
type: 'PresenceMatch',
attribute: self.attribute
};
});
}n/a
_toBer = function (ber) {
assert.ok(ber);
for (var i = 0; i < this.attribute.length; i++)
ber.writeByte(this.attribute.charCodeAt(i));
return ber;
}...
function mixin(target) {
target.prototype.toBer = function toBer(ber) {
if (!ber || !(ber instanceof BerWriter))
throw new TypeError('ber (BerWriter) required');
ber.startSequence(TYPES[this.type]);
ber = this._toBer(ber);
ber.endSequence();
return ber;
};
}
module.exports = {
isFilter: isFilter,
...parse = function (ber) {
assert.ok(ber);
this.attribute =
ber.buffer.slice(0, ber.length).toString('utf8').toLowerCase();
ber._offset += ber.length;
return true;
}n/a
function toBer(ber) {
if (!ber || !(ber instanceof BerWriter))
throw new TypeError('ber (BerWriter) required');
ber.startSequence(TYPES[this.type]);
ber = this._toBer(ber);
ber.endSequence();
return ber;
}n/a
function Filter() {
}n/a
matches = function (target, strictAttrCase) {
assert.object(target, 'target');
var value = helpers.getAttrValue(target, this.attribute, strictAttrCase);
return (value !== undefined && value !== null);
}...
dn: req.dn.toString(),
attributes: {
objectclass: ['organization', 'top'],
o: 'example'
}
};
if (req.filter.matches(obj.attributes))
res.send(obj);
res.end();
});
server.listen(1389, function() {
console.log('ldapjs listening at ' + server.url);
...toString = function () {
return '(' + helpers.escape(this.attribute) + '=*)';
}...
```javascript
var ldap = require('ldapjs');
var server = ldap.createServer();
server.search('dc=example', function(req, res, next) {
var obj = {
dn: req.dn.toString(),
attributes: {
objectclass: ['organization', 'top'],
o: 'example'
}
};
if (req.filter.matches(obj.attributes))
...ProtocolError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
constructor = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function LDAPError(message, dn, caller) {
if (Error.captureStackTrace)
Error.captureStackTrace(this, caller || LDAPError);
this.lde_message = message;
this.lde_dn = dn;
}n/a
ProxiedAuthorizationDeniedError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
constructor = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function LDAPError(message, dn, caller) {
if (Error.captureStackTrace)
Error.captureStackTrace(this, caller || LDAPError);
this.lde_message = message;
this.lde_dn = dn;
}n/a
function RDN(obj) {
var self = this;
this.attrs = {};
if (obj) {
Object.keys(obj).forEach(function (k) {
self.set(k, obj[k]);
});
}
}n/a
function rdnEquals(rdn) {
if (typeof (rdn) !== 'object')
return false;
var ourKeys = Object.keys(this.attrs);
var theirKeys = Object.keys(rdn.attrs);
if (ourKeys.length !== theirKeys.length)
return false;
ourKeys.sort();
theirKeys.sort();
for (var i = 0; i < ourKeys.length; i++) {
if (ourKeys[i] !== theirKeys[i])
return false;
if (this.attrs[ourKeys[i]].value !== rdn.attrs[ourKeys[i]].value)
return false;
}
return true;
}n/a
function rdnFormat(options) {
assert.optionalObject(options, 'options must be an object');
options = options || {};
var self = this;
var str = '';
function escapeValue(val, forceQuote) {
var out = '';
var cur = 0;
var len = val.length;
var quoted = false;
/* BEGIN JSSTYLED */
var escaped = /[\\\"]/;
var special = /[,=+<>#;]/;
/* END JSSTYLED */
if (len > 0) {
// Wrap strings with trailing or leading spaces in quotes
quoted = forceQuote || (val[0] == ' ' || val[len-1] == ' ');
}
while (cur < len) {
if (escaped.test(val[cur]) || (!quoted && special.test(val[cur]))) {
out += '\\';
}
out += val[cur++];
}
if (quoted)
out = '"' + out + '"';
return out;
}
function sortParsed(a, b) {
return self.attrs[a].order - self.attrs[b].order;
}
function sortStandard(a, b) {
var nameCompare = a.localeCompare(b);
if (nameCompare === 0) {
// TODO: Handle binary values
return self.attrs[a].value.localeCompare(self.attrs[b].value);
} else {
return nameCompare;
}
}
var keys = Object.keys(this.attrs);
if (options.keepOrder) {
keys.sort(sortParsed);
} else {
keys.sort(sortStandard);
}
keys.forEach(function (key) {
var attr = self.attrs[key];
if (str.length)
str += '+';
if (options.keepCase) {
str += attr.name;
} else {
if (options.upperName)
str += key.toUpperCase();
else
str += key;
}
str += '=' + escapeValue(attr.value, (options.keepQuote && attr.quoted));
});
return str;
}...
///--- Helpers
// Copied from mcavage/node-assert-plus
function _assert(arg, type, name) {
name = name || type;
throw new assert.AssertionError({
message: util.format('%s (%s) required', name, type),
actual: typeof (arg),
expected: type,
operator: '===',
stackStartFunction: _assert.caller
});
}
...function rdnSet(name, value, opts) {
assert.string(name, 'name (string) required');
assert.string(value, 'value (string) required');
var self = this;
var lname = name.toLowerCase();
this.attrs[lname] = {
value: value,
name: name
};
if (opts && typeof (opts) === 'object') {
Object.keys(opts).forEach(function (k) {
if (k !== 'value')
self.attrs[lname][k] = opts[k];
});
}
}n/a
function rdnToString() {
return this.format();
}...
```javascript
var ldap = require('ldapjs');
var server = ldap.createServer();
server.search('dc=example', function(req, res, next) {
var obj = {
dn: req.dn.toString(),
attributes: {
objectclass: ['organization', 'top'],
o: 'example'
}
};
if (req.filter.matches(obj.attributes))
...ReferralError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
constructor = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function LDAPError(message, dn, caller) {
if (Error.captureStackTrace)
Error.captureStackTrace(this, caller || LDAPError);
this.lde_message = message;
this.lde_dn = dn;
}n/a
SaslBindInProgressError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
constructor = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function LDAPError(message, dn, caller) {
if (Error.captureStackTrace)
Error.captureStackTrace(this, caller || LDAPError);
this.lde_message = message;
this.lde_dn = dn;
}n/a
function SearchEntry(options) {
options = options || {};
assert.object(options);
lassert.optionalStringDN(options.objectName);
options.protocolOp = Protocol.LDAP_REP_SEARCH_ENTRY;
LDAPMessage.call(this, options);
this.objectName = options.objectName || null;
this.setAttributes(options.attributes || []);
}n/a
function LDAPMessage(options) {
assert.object(options);
this.messageID = options.messageID || 0;
this.protocolOp = options.protocolOp || undefined;
this.controls = options.controls ? options.controls.slice(0) : [];
this.log = options.log;
}n/a
_json = function (j) {
assert.ok(j);
j.objectName = this.objectName.toString();
j.attributes = [];
this.attributes.forEach(function (a) {
j.attributes.push(a.json || a);
});
return j;
}n/a
_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;
}n/a
_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;
}...
function mixin(target) {
target.prototype.toBer = function toBer(ber) {
if (!ber || !(ber instanceof BerWriter))
throw new TypeError('ber (BerWriter) required');
ber.startSequence(TYPES[this.type]);
ber = this._toBer(ber);
ber.endSequence();
return ber;
};
}
module.exports = {
isFilter: isFilter,
...addAttribute = function (attr) {
if (!attr || typeof (attr) !== 'object')
throw new TypeError('attr (attribute) required');
this.attributes.push(attr);
}n/a
fromObject = function (obj) {
if (typeof (obj) !== 'object')
throw new TypeError('object required');
var self = this;
if (obj.controls)
this.controls = obj.controls;
if (obj.attributes)
obj = obj.attributes;
this.attributes = [];
Object.keys(obj).forEach(function (k) {
self.attributes.push(new Attribute({type: k, vals: obj[k]}));
});
return true;
}n/a
setAttributes = function (obj) {
if (typeof (obj) !== 'object')
throw new TypeError('object required');
if (Array.isArray(obj)) {
obj.forEach(function (a) {
if (!Attribute.isAttribute(a))
throw new TypeError('entry must be an Array of Attributes');
});
this.attributes = obj;
} else {
var self = this;
self.attributes = [];
Object.keys(obj).forEach(function (k) {
var attr = new Attribute({type: k});
if (Array.isArray(obj[k])) {
obj[k].forEach(function (v) {
attr.addValue(v.toString());
});
} else {
attr.addValue(obj[k].toString());
}
self.attributes.push(attr);
});
}
}n/a
toObject = function () {
return this.object;
}n/a
function SearchReference(options) {
options = options || {};
assert.object(options);
options.protocolOp = Protocol.LDAP_REP_SEARCH_REF;
LDAPMessage.call(this, options);
this.uris = options.uris || [];
}n/a
function LDAPMessage(options) {
assert.object(options);
this.messageID = options.messageID || 0;
this.protocolOp = options.protocolOp || undefined;
this.controls = options.controls ? options.controls.slice(0) : [];
this.log = options.log;
}n/a
_json = function (j) {
assert.ok(j);
j.uris = this.uris.slice();
return j;
}n/a
_parse = function (ber, length) {
assert.ok(ber);
while (ber.offset < length) {
var _url = ber.readString();
parseURL(_url);
this.uris.push(_url);
}
return true;
}n/a
_toBer = function (ber) {
assert.ok(ber);
this.uris.forEach(function (u) {
ber.writeString(u.href || u);
});
return ber;
}...
function mixin(target) {
target.prototype.toBer = function toBer(ber) {
if (!ber || !(ber instanceof BerWriter))
throw new TypeError('ber (BerWriter) required');
ber.startSequence(TYPES[this.type]);
ber = this._toBer(ber);
ber.endSequence();
return ber;
};
}
module.exports = {
isFilter: isFilter,
...fromObject = function (obj) {
if (typeof (obj) !== 'object')
throw new TypeError('object required');
this.uris = obj.uris ? obj.uris.slice() : [];
return true;
}n/a
toObject = function () {
return this.object;
}n/a
function SearchRequest(options) {
options = options || {};
assert.object(options);
options.protocolOp = Protocol.LDAP_REQ_SEARCH;
LDAPMessage.call(this, options);
if (options.baseObject !== undefined) {
this.baseObject = options.baseObject;
} else {
this.baseObject = dn.parse('');
}
this.scope = options.scope || 'base';
this.derefAliases = options.derefAliases || Protocol.NEVER_DEREF_ALIASES;
this.sizeLimit = options.sizeLimit || 0;
this.timeLimit = options.timeLimit || 0;
this.typesOnly = options.typesOnly || false;
this.filter = options.filter || null;
this.attributes = options.attributes ? options.attributes.slice(0) : [];
}n/a
function LDAPMessage(options) {
assert.object(options);
this.messageID = options.messageID || 0;
this.protocolOp = options.protocolOp || undefined;
this.controls = options.controls ? options.controls.slice(0) : [];
this.log = options.log;
}n/a
_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;
}n/a
_parse = function (ber) {
assert.ok(ber);
this.baseObject = 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.peek() === 0x30) {
ber.readSequence();
var end = ber.offset + ber.length;
while (ber.offset < end)
this.attributes.push(ber.readString().toLowerCase());
}
return true;
}n/a
_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);
ber.startSequence(Ber.Sequence | Ber.Constructor);
if (this.attributes && this.attributes.length) {
this.attributes.forEach(function (a) {
ber.writeString(a);
});
}
ber.endSequence();
return ber;
}...
function mixin(target) {
target.prototype.toBer = function toBer(ber) {
if (!ber || !(ber instanceof BerWriter))
throw new TypeError('ber (BerWriter) required');
ber.startSequence(TYPES[this.type]);
ber = this._toBer(ber);
ber.endSequence();
return ber;
};
}
module.exports = {
isFilter: isFilter,
...function SearchResponse(options) {
options = options || {};
assert.object(options);
options.protocolOp = Protocol.LDAP_REP_SEARCH;
LDAPResult.call(this, options);
this.attributes = options.attributes ? options.attributes.slice() : [];
this.notAttributes = [];
this.sentEntries = 0;
}n/a
function LDAPResult(options) {
options = options || {};
assert.object(options);
assert.optionalNumber(options.status);
assert.optionalString(options.matchedDN);
assert.optionalString(options.errorMessage);
assert.optionalArrayOfString(options.referrals);
LDAPMessage.call(this, options);
this.status = options.status || 0; // LDAP SUCCESS
this.matchedDN = options.matchedDN || '';
this.errorMessage = options.errorMessage || '';
this.referrals = options.referrals || [];
this.connection = options.connection || null;
}n/a
createSearchEntry = function (object) {
assert.object(object);
var entry = new SearchEntry({
messageID: this.messageID,
log: this.log,
objectName: object.objectName || object.dn
});
entry.fromObject((object.attributes || object));
return entry;
}n/a
createSearchReference = function (uris) {
if (!uris)
throw new TypeError('uris ([string]) required');
if (!Array.isArray(uris))
uris = [uris];
for (var i = 0; i < uris.length; i++) {
if (typeof (uris[i]) == 'string')
uris[i] = parseURL(uris[i]);
}
var self = this;
return new SearchReference({
messageID: self.messageID,
log: self.log,
uris: uris
});
}n/a
send = function (entry, nofiltering) {
if (!entry || typeof (entry) !== 'object')
throw new TypeError('entry (SearchEntry) required');
if (nofiltering === undefined)
nofiltering = false;
if (typeof (nofiltering) !== 'boolean')
throw new TypeError('noFiltering must be a boolean');
var self = this;
if (entry instanceof SearchEntry || entry instanceof SearchReference) {
if (!entry.messageID)
entry.messageID = this.messageID;
if (entry.messageID !== this.messageID)
throw new Error('SearchEntry messageID mismatch');
} else {
if (!entry.attributes)
throw new Error('entry.attributes required');
var savedAttrs = {};
var all = (self.attributes.indexOf('*') !== -1);
Object.keys(entry.attributes).forEach(function (a) {
var _a = a.toLowerCase();
if (!nofiltering && _a.length && _a[0] === '_') {
savedAttrs[a] = entry.attributes[a];
delete entry.attributes[a];
} else if (!nofiltering && self.notAttributes.indexOf(_a) !== -1) {
savedAttrs[a] = entry.attributes[a];
delete entry.attributes[a];
} else if (all) {
return;
} else if (self.attributes.length && self.attributes.indexOf(_a) === -1) {
savedAttrs[a] = entry.attributes[a];
delete entry.attributes[a];
}
});
var save = entry;
entry = new SearchEntry({
objectName: typeof (save.dn) === 'string' ? parseDN(save.dn) : save.dn,
messageID: self.messageID,
log: self.log
});
entry.fromObject(save);
}
try {
if (this.log.debug())
this.log.debug('%s: sending: %j', this.connection.ldap.id, entry.json);
this.connection.write(entry.toBer());
this.sentEntries++;
if (self._dtraceOp && self._dtraceId) {
dtrace.fire('server-search-entry', function () {
var c = self.connection || {ldap: {}};
return [
self._dtraceId || 0,
(c.remoteAddress || ''),
c.ldap.bindDN ? c.ldap.bindDN.toString() : '',
(self.requestDN ? self.requestDN.toString() : ''),
entry.objectName.toString(),
entry.attributes.length
];
});
}
// Restore attributes
Object.keys(savedAttrs || {}).forEach(function (k) {
save.attributes[k] = savedAttrs[k];
});
} catch (e) {
this.log.warn(e, '%s failure to write message %j',
this.connection.ldap.id, this.json);
}
}...
attributes: {
objectclass: ['organization', 'top'],
o: 'example'
}
};
if (req.filter.matches(obj.attributes))
res.send(obj);
res.end();
});
server.listen(1389, function() {
console.log('ldapjs listening at ' + server.url);
});
...function Server(options) {
if (options) {
if (typeof (options) !== 'object')
throw new TypeError('options (object) required');
if (typeof (options.log) !== 'object')
throw new TypeError('options.log must be an object');
if (options.certificate || options.key) {
if (!(options.certificate && options.key) ||
(typeof (options.certificate) !== 'string' &&
!Buffer.isBuffer(options.certificate)) ||
(typeof (options.key) !== 'string' &&
!Buffer.isBuffer(options.key))) {
throw new TypeError('options.certificate and options.key ' +
'(string or buffer) are both required for TLS');
}
}
} else {
options = {};
}
var self = this;
EventEmitter.call(this, options);
this._chain = [];
this.log = options.log;
this.strictDN = (options.strictDN !== undefined) ? options.strictDN : true;
var log = this.log;
function setupConnection(c) {
assert.ok(c);
if (c.type === 'unix') {
c.remoteAddress = self.server.path;
c.remotePort = c.fd;
} else if (c.socket) {
// TLS
c.remoteAddress = c.socket.remoteAddress;
c.remotePort = c.socket.remotePort;
}
var rdn = new dn.RDN({cn: 'anonymous'});
c.ldap = {
id: c.remoteAddress + ':' + c.remotePort,
config: options,
_bindDN: new DN([rdn])
};
c.addListener('timeout', function () {
log.trace('%s timed out', c.ldap.id);
c.destroy();
});
c.addListener('end', function () {
log.trace('%s shutdown', c.ldap.id);
});
c.addListener('error', function (err) {
log.warn('%s unexpected connection error', c.ldap.id, err);
self.emit('clientError', err);
c.destroy();
});
c.addListener('close', function (had_err) {
log.trace('%s close; had_err=%j', c.ldap.id, had_err);
c.end();
});
c.ldap.__defineGetter__('bindDN', function () {
return c.ldap._bindDN;
});
c.ldap.__defineSetter__('bindDN', function (val) {
if (!(val instanceof DN))
throw new TypeError('DN required');
c.ldap._bindDN = val;
return val;
});
return c;
}
function newConnection(c) {
setupConnection(c);
log.trace('new connection from %s', c.ldap.id);
dtrace.fire('server-connection', function () {
return [c.remoteAddress];
});
c.parser = new Parser({
log: options.log
});
c.parser.on('message', function (req) {
req.connection = c;
req.logId = c.ldap.id + '::' + req.messageID;
req.startTime = new Date().getTime();
if (log.debug())
log.debug('%s: message received: req=%j', c.ldap.id, req.json);
var res = getResponse(req);
if (!res) {
log.warn('Unimplemented server method: %s', req.type);
c.destroy();
return false;
}
// parse string DNs for routing/etc
try {
switch (req.protocolOp) {
case Protocol.LDAP_REQ_BIND:
req.name = dn.parse(req.name);
break;
case Protocol.LDAP_REQ_ADD:
case Protocol.LDAP_REQ_COMPARE:
case Protocol.LDAP_REQ_DELETE:
req.entry = dn.parse(req.entry);
break;
case Protocol.LDAP_REQ_MODIFY:
req.object = dn.parse(req.object);
break;
case Protocol.LDAP_REQ_MODRDN:
req.entry = dn.parse(req.entry);
// TODO: handle newRdn/Superior
break;
case Protocol.LDAP_REQ_SEARCH:
req.baseObject = dn.parse(req.baseObject);
break;
default:
break;
}
} catch (e) {
if (self.strictDN) {
return res.end(errors.LDAP_INVALID_DN_SYNTAX);
}
}
res.connection = c;
res.logId = req.logId;
res.requestDN = req.dn;
var chain = self._getHandlerChain(req, res);
var i = 0;
return function (err) {
function sendError(err) {
res.status = err.code || errors.LDAP_OPERATIONS_ERROR;
res.matchedDN = req.suffix ? req.suffi ...n/a
function EventEmitter() {
EventEmitter.init.call(this);
}n/a
function _getHandlerChain(req, res) {
assert.ok(req);
fireDTraceProbe(req, res);
// check anonymous bind
if (req.protocolOp === Protocol.LDAP_REQ_BIND &&
req.dn.toString() === '' &&
req.credentials === '') {
return {
backend: self,
handlers: [defaultNoOpHandler]
};
}
var op = '0x' + req.protocolOp.toString(16);
var self = this;
var routes = this.routes;
var route;
// Special cases are exops, unbinds and abandons. Handle those first.
if (req.protocolOp === Protocol.LDAP_REQ_EXTENSION) {
route = routes[req.requestName];
if (route) {
return {
backend: route.backend,
handlers: (route[op] ? route[op] : [noExOpHandler])
};
} else {
return {
backend: self,
handlers: [noExOpHandler]
};
}
} else if (req.protocolOp === Protocol.LDAP_REQ_UNBIND) {
route = routes['unbind'];
if (route) {
return {
backend: route.backend,
handlers: route[op]
};
} else {
return {
backend: self,
handlers: [defaultNoOpHandler]
};
}
} else if (req.protocolOp === Protocol.LDAP_REQ_ABANDON) {
return {
backend: self,
handlers: [defaultNoOpHandler]
};
}
// Otherwise, match via DN rules
assert.ok(req.dn);
var keys = this._sortedRouteKeys();
var fallbackHandler = [noSuffixHandler];
// invalid DNs in non-strict mode are routed to the default handler
var testDN = (typeof (req.dn) === 'string') ? '' : req.dn;
for (var i = 0; i < keys.length; i++) {
var suffix = keys[i];
route = routes[suffix];
assert.ok(route.dn);
// Match a valid route or the route wildcard ('')
if (route.dn.equals(testDN) || route.dn.parentOf(testDN) || suffix === '') {
if (route[op]) {
// We should be good to go.
req.suffix = route.dn;
return {
backend: route.backend,
handlers: route[op]
};
} else {
if (suffix === '') {
break;
} else {
// We found a valid suffix but not a valid operation.
// There might be a more generic suffix with a legitimate operation.
fallbackHandler = [defaultHandler];
}
}
}
}
return {
backend: self,
handlers: fallbackHandler
};
}n/a
_getRoute = function (_dn, backend) {
assert.ok(dn);
if (!backend)
backend = this;
var name;
if (_dn instanceof dn.DN) {
name = _dn.toString();
} else {
name = _dn;
}
if (!this.routes[name]) {
this.routes[name] = {};
this.routes[name].backend = backend;
this.routes[name].dn = _dn;
// Force regeneration of the route key cache on next request
this._routeKeyCache = null;
}
return this.routes[name];
}n/a
_mount = function (op, name, argv, notDN) {
assert.ok(op);
assert.ok(name !== undefined);
assert.ok(argv);
if (typeof (name) !== 'string')
throw new TypeError('name (string) required');
if (!argv.length)
throw new Error('at least one handler required');
var backend = this;
var index = 0;
if (typeof (argv[0]) === 'object' && !Array.isArray(argv[0])) {
backend = argv[0];
index = 1;
}
var route = this._getRoute(notDN ? name : dn.parse(name), backend);
var chain = this._chain.slice();
argv.slice(index).forEach(function (a) {
chain.push(a);
});
route['0x' + op.toString(16)] = mergeFunctionArgs(chain);
return this;
}n/a
function _sortedRouteKeys() {
// The filtered/sorted route keys are cached to prevent needlessly
// regenerating the list for every incoming request.
if (!this._routeKeyCache) {
var self = this;
var reversedRDNsToKeys = {};
// Generate mapping of reversedRDNs(DN) -> routeKey
Object.keys(this.routes).forEach(function (key) {
var _dn = self.routes[key].dn;
// Ignore non-DN routes such as exop or unbind
if (_dn instanceof dn.DN) {
var reversed = _dn.clone();
reversed.rdns.reverse();
reversedRDNsToKeys[reversed.format()] = key;
}
});
var output = [];
// Reverse-sort on reversedRDS(DN) in order to output routeKey list.
// This will place more specific DNs in front of their parents:
// 1. dc=test, dc=domain, dc=sub
// 2. dc=test, dc=domain
// 3. dc=other, dc=foobar
Object.keys(reversedRDNsToKeys).sort().reverse().forEach(function (_dn) {
output.push(reversedRDNsToKeys[_dn]);
});
this._routeKeyCache = output;
}
return this._routeKeyCache;
}n/a
add = function (name) {
var args = Array.prototype.slice.call(arguments, 1);
return this._mount(Protocol.LDAP_REQ_ADD, name, args);
}n/a
address = function () {
return this.server.address();
}n/a
after = function () {
if (!this._postChain)
this._postChain = [];
var self = this;
mergeFunctionArgs(arguments).forEach(function (h) {
self._postChain.push(h);
});
}n/a
bind = function (name) {
var args = Array.prototype.slice.call(arguments, 1);
return this._mount(Protocol.LDAP_REQ_BIND, name, args);
}...
}
});
this.finished = false;
this.started = false;
var emitter = new EventEmitter();
emitter.on('searchEntry', this.emit.bind(this, 'searchEntry'));
emitter.on('end', this._onEnd.bind(this));
emitter.on('error', this._onError.bind(this));
this.childEmitter = emitter;
}
util.inherits(SearchPager, EventEmitter);
module.exports = SearchPager;
...close = function () {
return this.server.close();
}n/a
compare = function (name) {
var args = Array.prototype.slice.call(arguments, 1);
return this._mount(Protocol.LDAP_REQ_COMPARE, name, args);
}n/a
del = function (name) {
var args = Array.prototype.slice.call(arguments, 1);
return this._mount(Protocol.LDAP_REQ_DELETE, name, args);
}n/a
exop = function (name) {
var args = Array.prototype.slice.call(arguments, 1);
return this._mount(Protocol.LDAP_REQ_EXTENSION, name, args, true);
}n/a
listen = function (port, host, callback) {
if (typeof (port) !== 'number' && typeof (port) !== 'string')
throw new TypeError('port (number or path) required');
if (typeof (host) === 'function') {
callback = host;
host = '0.0.0.0';
}
if (typeof (port) === 'string' && /^[0-9]+$/.test(port)) {
// Disambiguate between string ports and file paths
port = parseInt(port, 10);
}
var self = this;
function cbListen() {
if (typeof (port) === 'number') {
self.host = self.address().address;
self.port = self.address().port;
} else {
self.host = port;
self.port = self.server.fd;
}
if (typeof (callback) === 'function')
callback();
}
if (typeof (port) === 'number') {
return this.server.listen(port, host, cbListen);
} else {
return this.server.listen(port, cbListen);
}
}...
if (req.filter.matches(obj.attributes))
res.send(obj);
res.end();
});
server.listen(1389, function() {
console.log('ldapjs listening at ' + server.url);
});
```
To run that, assuming you've got the [OpenLDAP](http://www.openldap.org/)
client on your system:
...listenFD = function (fd) {
this.host = 'unix-domain-socket';
this.port = fd;
return this.server.listenFD(fd);
}n/a
modify = function (name) {
var args = Array.prototype.slice.call(arguments, 1);
return this._mount(Protocol.LDAP_REQ_MODIFY, name, args);
}n/a
modifyDN = function (name) {
var args = Array.prototype.slice.call(arguments, 1);
return this._mount(Protocol.LDAP_REQ_MODRDN, name, args);
}n/a
search = function (name) {
var args = Array.prototype.slice.call(arguments, 1);
return this._mount(Protocol.LDAP_REQ_SEARCH, name, args);
}...
For full docs, head on over to <http://ldapjs.org>.
```javascript
var ldap = require('ldapjs');
var server = ldap.createServer();
server.search('dc=example', function(req, res, next) {
var obj = {
dn: req.dn.toString(),
attributes: {
objectclass: ['organization', 'top'],
o: 'example'
}
};
...unbind = function () {
var args = Array.prototype.slice.call(arguments, 0);
return this._mount(Protocol.LDAP_REQ_UNBIND, 'unbind', args, true);
}n/a
function use() {
var args = Array.prototype.slice.call(arguments);
var chain = mergeFunctionArgs(args, 0, args.length);
var self = this;
chain.forEach(function (c) {
self._chain.push(c);
});
}n/a
function ServerSideSortingRequestControl(options) {
assert.optionalObject(options);
options = options || {};
options.type = ServerSideSortingRequestControl.OID;
if (options.value) {
if (Buffer.isBuffer(options.value)) {
this.parse(options.value);
} else if (Array.isArray(options.value)) {
assert.arrayOfObject(options.value, 'options.value must be Objects');
for (var i = 0; i < options.value.length; i++) {
if (!options.value[i].hasOwnProperty('attributeType')) {
throw new Error('Missing required key: attributeType');
}
}
this._value = options.value;
} else if (typeof (options.value) === 'object') {
if (!options.value.hasOwnProperty('attributeType')) {
throw new Error('Missing required key: attributeType');
}
this._value = [options.value];
} else {
throw new TypeError('options.value must be a Buffer, Array or Object');
}
options.value = null;
}
Control.call(this, options);
}n/a
function Control(options) {
assert.optionalObject(options);
options = options || {};
assert.optionalString(options.type);
assert.optionalBool(options.criticality);
if (options.value) {
assert.buffer(options.value);
}
this.type = options.type || '';
this.criticality = options.critical || options.criticality || false;
this.value = options.value || null;
}n/a
_json = function (obj) {
obj.controlValue = this.value;
return obj;
}n/a
_toBer = function (ber) {
assert.ok(ber);
if (!this._value || this.value.length === 0)
return;
var writer = new BerWriter();
writer.startSequence(0x30);
for (var i = 0; i < this.value.length; i++) {
var item = this.value[i];
writer.startSequence(0x30);
if (item.attributeType) {
writer.writeString(item.attributeType, asn1.Ber.OctetString);
}
if (item.orderingRule) {
writer.writeString(item.orderingRule, 0x80);
}
if (item.reverseOrder) {
writer.writeBoolean(item.reverseOrder, 0x81);
}
writer.endSequence();
}
writer.endSequence();
ber.writeBuffer(writer.buffer, 0x04);
}...
function mixin(target) {
target.prototype.toBer = function toBer(ber) {
if (!ber || !(ber instanceof BerWriter))
throw new TypeError('ber (BerWriter) required');
ber.startSequence(TYPES[this.type]);
ber = this._toBer(ber);
ber.endSequence();
return ber;
};
}
module.exports = {
isFilter: isFilter,
...function parse(buffer) {
assert.ok(buffer);
var ber = new BerReader(buffer);
var item;
if (ber.readSequence(0x30)) {
this._value = [];
while (ber.readSequence(0x30)) {
item = {};
item.attributeType = ber.readString(asn1.Ber.OctetString);
if (ber.peek() == 0x80) {
item.orderingRule = ber.readString(0x80);
}
if (ber.peek() == 0x81) {
item.reverseOrder = (ber._readTag(0x81) === 0 ? false : true);
}
this._value.push(item);
}
return true;
}
return false;
}n/a
function ServerSideSortingResponseControl(options) {
assert.optionalObject(options);
options = options || {};
options.type = ServerSideSortingResponseControl.OID;
options.criticality = false;
if (options.value) {
if (Buffer.isBuffer(options.value)) {
this.parse(options.value);
} else if (typeof (options.value) === 'object') {
if (VALID_CODES.indexOf(options.value.result) === -1) {
throw new Error('Invalid result code');
}
if (options.value.failedAttribute &&
typeof (options.value.failedAttribute) !== 'string') {
throw new Error('failedAttribute must be String');
}
this._value = options.value;
} else {
throw new TypeError('options.value must be a Buffer or Object');
}
options.value = null;
}
Control.call(this, options);
}n/a
function Control(options) {
assert.optionalObject(options);
options = options || {};
assert.optionalString(options.type);
assert.optionalBool(options.criticality);
if (options.value) {
assert.buffer(options.value);
}
this.type = options.type || '';
this.criticality = options.critical || options.criticality || false;
this.value = options.value || null;
}n/a
_json = function (obj) {
obj.controlValue = this.value;
return obj;
}n/a
_toBer = function (ber) {
assert.ok(ber);
if (!this._value || this.value.length === 0)
return;
var writer = new BerWriter();
writer.startSequence(0x30);
writer.writeEnumeration(this.value.result);
if (this.value.result !== CODES.LDAP_SUCCESS && this.value.failedAttribute) {
writer.writeString(this.value.failedAttribute, 0x80);
}
writer.endSequence();
ber.writeBuffer(writer.buffer, 0x04);
}...
function mixin(target) {
target.prototype.toBer = function toBer(ber) {
if (!ber || !(ber instanceof BerWriter))
throw new TypeError('ber (BerWriter) required');
ber.startSequence(TYPES[this.type]);
ber = this._toBer(ber);
ber.endSequence();
return ber;
};
}
module.exports = {
isFilter: isFilter,
...function parse(buffer) {
assert.ok(buffer);
var ber = new BerReader(buffer);
if (ber.readSequence(0x30)) {
this._value = {};
this._value.result = ber.readEnumeration();
if (ber.peek() == 0x80) {
this._value.failedAttribute = ber.readString(0x80);
}
return true;
}
return false;
}n/a
SizeLimitExceededError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
constructor = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function LDAPError(message, dn, caller) {
if (Error.captureStackTrace)
Error.captureStackTrace(this, caller || LDAPError);
this.lde_message = message;
this.lde_dn = dn;
}n/a
StrongAuthRequiredError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
constructor = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function LDAPError(message, dn, caller) {
if (Error.captureStackTrace)
Error.captureStackTrace(this, caller || LDAPError);
this.lde_message = message;
this.lde_dn = dn;
}n/a
function SubstringFilter(options) {
parents.SubstringFilter.call(this, options);
}n/a
function SubstringFilter(options) {
if (typeof (options) === 'object') {
assert.string(options.attribute, 'options.attribute');
this.attribute = options.attribute;
this.initial = options.initial;
this.any = options.any ? options.any.slice(0) : [];
this.final = options.final;
} else {
this.any = [];
}
var self = this;
this.__defineGetter__('type', function () { return 'substring'; });
this.__defineGetter__('json', function () {
return {
type: 'SubstringMatch',
initial: self.initial,
any: self.any,
final: self.final
};
});
}n/a
_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;
}...
function mixin(target) {
target.prototype.toBer = function toBer(ber) {
if (!ber || !(ber instanceof BerWriter))
throw new TypeError('ber (BerWriter) required');
ber.startSequence(TYPES[this.type]);
ber = this._toBer(ber);
ber.endSequence();
return ber;
};
}
module.exports = {
isFilter: isFilter,
...parse = function (ber) {
assert.ok(ber);
this.attribute = ber.readString().toLowerCase();
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);
if (this.attribute === 'objectclass')
this.initial = this.initial.toLowerCase();
break;
case 0x81: // Any
var anyVal = ber.readString(tag);
if (this.attribute === 'objectclass')
anyVal = anyVal.toLowerCase();
this.any.push(anyVal);
break;
case 0x82: // Final
this.final = ber.readString(tag);
if (this.attribute === 'objectclass')
this.final = this.final.toLowerCase();
break;
default:
throw new Error('Invalid substrings filter type: 0x' + tag.toString(16));
}
}
return true;
}n/a
function toBer(ber) {
if (!ber || !(ber instanceof BerWriter))
throw new TypeError('ber (BerWriter) required');
ber.startSequence(TYPES[this.type]);
ber = this._toBer(ber);
ber.endSequence();
return ber;
}n/a
function Filter() {
}n/a
matches = function (target, strictAttrCase) {
assert.object(target, 'target');
var tv = helpers.getAttrValue(target, this.attribute, strictAttrCase);
if (tv !== undefined && tv !== null) {
var re = '';
if (this.initial)
re += '^' + escapeRegExp(this.initial) + '.*';
this.any.forEach(function (s) {
re += escapeRegExp(s) + '.*';
});
if (this.final)
re += escapeRegExp(this.final) + '$';
var matcher = new RegExp(re);
return helpers.testValues(function (v) {
return matcher.test(v);
}, tv);
}
return false;
}...
dn: req.dn.toString(),
attributes: {
objectclass: ['organization', 'top'],
o: 'example'
}
};
if (req.filter.matches(obj.attributes))
res.send(obj);
res.end();
});
server.listen(1389, function() {
console.log('ldapjs listening at ' + server.url);
...toString = function () {
var str = '(' + helpers.escape(this.attribute) + '=';
if (this.initial)
str += helpers.escape(this.initial);
str += '*';
this.any.forEach(function (s) {
str += helpers.escape(s) + '*';
});
if (this.final)
str += helpers.escape(this.final);
str += ')';
return str;
}...
```javascript
var ldap = require('ldapjs');
var server = ldap.createServer();
server.search('dc=example', function(req, res, next) {
var obj = {
dn: req.dn.toString(),
attributes: {
objectclass: ['organization', 'top'],
o: 'example'
}
};
if (req.filter.matches(obj.attributes))
...TimeLimitExceededError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
constructor = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function LDAPError(message, dn, caller) {
if (Error.captureStackTrace)
Error.captureStackTrace(this, caller || LDAPError);
this.lde_message = message;
this.lde_dn = dn;
}n/a
function TimeoutError(message) {
LDAPError.call(this, message, null, TimeoutError);
}n/a
function LDAPError(message, dn, caller) {
if (Error.captureStackTrace)
Error.captureStackTrace(this, caller || LDAPError);
this.lde_message = message;
this.lde_dn = dn;
}n/a
UnavailableCriticalExtensionError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
constructor = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function LDAPError(message, dn, caller) {
if (Error.captureStackTrace)
Error.captureStackTrace(this, caller || LDAPError);
this.lde_message = message;
this.lde_dn = dn;
}n/a
UnavailableError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
constructor = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function LDAPError(message, dn, caller) {
if (Error.captureStackTrace)
Error.captureStackTrace(this, caller || LDAPError);
this.lde_message = message;
this.lde_dn = dn;
}n/a
function UnbindRequest(options) {
options = options || {};
assert.object(options);
options.protocolOp = Protocol.LDAP_REQ_UNBIND;
LDAPMessage.call(this, options);
}n/a
function LDAPMessage(options) {
assert.object(options);
this.messageID = options.messageID || 0;
this.protocolOp = options.protocolOp || undefined;
this.controls = options.controls ? options.controls.slice(0) : [];
this.log = options.log;
}n/a
_json = function (j) {
assert.ok(j);
return j;
}n/a
_parse = function (ber) {
assert.ok(ber);
return true;
}n/a
_toBer = function (ber) {
assert.ok(ber);
return ber;
}...
function mixin(target) {
target.prototype.toBer = function toBer(ber) {
if (!ber || !(ber instanceof BerWriter))
throw new TypeError('ber (BerWriter) required');
ber.startSequence(TYPES[this.type]);
ber = this._toBer(ber);
ber.endSequence();
return ber;
};
}
module.exports = {
isFilter: isFilter,
...function UnbindResponse(options) {
options = options || {};
assert.object(options);
options.protocolOp = 0;
LDAPMessage.call(this, options);
}n/a
function LDAPResult(options) {
options = options || {};
assert.object(options);
assert.optionalNumber(options.status);
assert.optionalString(options.matchedDN);
assert.optionalString(options.errorMessage);
assert.optionalArrayOfString(options.referrals);
LDAPMessage.call(this, options);
this.status = options.status || 0; // LDAP SUCCESS
this.matchedDN = options.matchedDN || '';
this.errorMessage = options.errorMessage || '';
this.referrals = options.referrals || [];
this.connection = options.connection || null;
}n/a
_json = function (j) {
return j;
}n/a
end = function (status) {
assert.ok(this.connection);
this.log.trace('%s: unbinding!', this.connection.ldap.id);
this.connection.end();
var self = this;
if (self._dtraceOp && self._dtraceId) {
dtrace.fire('server-' + self._dtraceOp + '-done', function () {
var c = self.connection || {ldap: {}};
return [
self._dtraceId || 0,
(c.remoteAddress || ''),
c.ldap.bindDN ? c.ldap.bindDN.toString() : '',
(self.requestDN ? self.requestDN.toString() : ''),
0,
''
];
});
}
}...
o: 'example'
}
};
if (req.filter.matches(obj.attributes))
res.send(obj);
res.end();
});
server.listen(1389, function() {
console.log('ldapjs listening at ' + server.url);
});
```
...UndefinedAttributeTypeError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
constructor = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function LDAPError(message, dn, caller) {
if (Error.captureStackTrace)
Error.captureStackTrace(this, caller || LDAPError);
this.lde_message = message;
this.lde_dn = dn;
}n/a
UnwillingToPerformError = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
constructor = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]);
}n/a
function LDAPError(message, dn, caller) {
if (Error.captureStackTrace)
Error.captureStackTrace(this, caller || LDAPError);
this.lde_message = message;
this.lde_dn = dn;
}n/a
function optionalArrayOfAttribute(input, name) {
if (input === undefined)
return;
if (!Array.isArray(input) ||
input.some(function (v) { return !isAttribute(v); })) {
_assert(input, 'array of Attribute', name);
}
}n/a
function optionalDN(input, name) {
if (input !== undefined && !isDN(input))
_assert(input, 'DN', name);
}n/a
function optionalStringDN(input, name) {
if (input === undefined || isDN(input) || typeof (input) === 'string')
return;
_assert(input, 'DN or string', name);
}n/a
function stringDN(input, name) {
if (isDN(input) || typeof (input) === 'string')
return;
_assert(input, 'DN or string', name);
}n/a
function DN(rdns) {
assert.optionalArrayOfObject(rdns, '[object] required');
this.rdns = rdns ? rdns.slice() : [];
this._format = {};
}n/a
function RDN(obj) {
var self = this;
this.attrs = {};
if (obj) {
Object.keys(obj).forEach(function (k) {
self.set(k, obj[k]);
});
}
}n/a
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();
var order = 0;
rdn.spLead = trim();
while (cur < len) {
var opts = {
order: order
};
var attr = parseAttrType();
trim();
if (cur >= len || name[cur++] !== '=')
throw invalidDN(name);
trim();
// Parameters about RDN value are set in 'opts' by parseAttrValue
var value = parseAttrValue(opts);
rdn.set(attr, value, opts);
rdn.spTrail = trim();
if (cur >= len || name[cur] !== '+')
break;
++cur;
++order;
}
return rdn;
}
function trim() {
var count = 0;
while ((cur < len) && isWhitespace(name[cur])) {
++cur;
count++;
}
return count;
}
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(opts) {
if (cur < len && name[cur] == '#') {
opts.binary = true;
return parseBinaryAttrValue();
} else if (cur < len && name[cur] == '"') {
opts.quoted = true;
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 str = '';
++cur; // Consume the first quote
while ((cur < len) && name[cur] != '"') {
if (name[cur] === '\\')
cur++;
str += name[cur++];
}
if (cur++ >= len) // no closing quote
throw invalidDN(name);
return str;
}
function parseStringAttrValue() {
var beg = cur;
var str = '';
var esc = -1;
while ((cur < len) && !atTerminator()) {
if (name[cur] === '\\') {
// Consume the backslash and mark its place just in case it's escaping
// whitespace which needs to be preserved.
esc = cur++;
}
if (cur === len) // backslash followed by nothing
throw invalidDN(name);
str += name[cur++];
}
// Trim off (unescaped) trailing whitespace and rewind cursor to the end of
// the AttrValue to record whitespace length.
for (; cur > beg; cur--) {
if (!isWhitespace(name[cur - 1]) || (esc === (cur - 1)))
break;
}
return str.slice(0, cur - beg);
}
function atTerminator() {
return (cur < len &&
(name[cur] === ',' ||
name[cur] === ';' ||
name[cur] === '+'));
}
var rdns = [];
// Short-circuit for empty DNs
if (len === 0)
return new DN(rdns);
rdns.push(parseRdn());
while (cur < len) {
if (name[cur] === ',' || name[cur] === ';') {
++cur;
rdns.push(parseRdn());
} else {
throw invalidDN(name);
}
}
return new DN(rdns);
}n/a
_nextId = function () {
if (DTRACE_ID === MAX_INT)
DTRACE_ID = 0;
return ++DTRACE_ID;
}n/a
escape = function (inp) {
if (typeof (inp) === 'string') {
var esc = '';
for (var i = 0; i < inp.length; i++) {
switch (inp[i]) {
case '*':
esc += '\\2a';
break;
case '(':
esc += '\\28';
break;
case ')':
esc += '\\29';
break;
case '\\':
esc += '\\5c';
break;
case '\0':
esc += '\\00';
break;
default:
esc += inp[i];
break;
}
}
return esc;
} else {
return inp;
}
}n/a
function isFilter(filter) {
if (!filter || typeof (filter) !== 'object') {
return false;
}
// Do our best to duck-type it
if (typeof (filter.toBer) === 'function' &&
typeof (filter.matches) === 'function' &&
TYPES[filter.type] !== undefined) {
return true;
}
return false;
}n/a
function mixin(target) {
target.prototype.toBer = function toBer(ber) {
if (!ber || !(ber instanceof BerWriter))
throw new TypeError('ber (BerWriter) required');
ber.startSequence(TYPES[this.type]);
ber = this._toBer(ber);
ber.endSequence();
return ber;
};
}n/a
function AndFilter(options) {
parents.AndFilter.call(this, options);
}n/a
function ApproximateFilter(options) {
parents.ApproximateFilter.call(this, options);
}n/a
function EqualityFilter(options) {
parents.EqualityFilter.call(this, options);
}n/a
function ExtensibleFilter(options) {
parents.ExtensibleFilter.call(this, options);
}n/a
function GreaterThanEqualsFilter(options) {
parents.GreaterThanEqualsFilter.call(this, options);
}n/a
function LessThanEqualsFilter(options) {
parents.LessThanEqualsFilter.call(this, options);
}n/a
function NotFilter(options) {
parents.NotFilter.call(this, options);
}n/a
function OrFilter(options) {
parents.OrFilter.call(this, options);
}n/a
function PresenceFilter(options) {
parents.PresenceFilter.call(this, options);
}n/a
function SubstringFilter(options) {
parents.SubstringFilter.call(this, options);
}n/a
function isFilter(filter) {
if (!filter || typeof (filter) !== 'object') {
return false;
}
// Do our best to duck-type it
if (typeof (filter.toBer) === 'function' &&
typeof (filter.matches) === 'function' &&
TYPES[filter.type] !== undefined) {
return true;
}
return false;
}n/a
parse = function (ber) {
if (!ber || !(ber instanceof BerReader))
throw new TypeError('ber (BerReader) required');
return _parse(ber);
}n/a
function parseString(str) {
var generic = parents.parse(str);
// The filter object(s) return from ldap-filter.parse lack the toBer/parse
// decoration that native ldapjs filter possess. cloneFilter adds that back.
return cloneFilter(generic);
}n/a
function PersistentSearch() {
this.clientList = [];
}n/a
function checkChangeType(req, requestType) {
return (req.persistentSearch.value.changeTypes &
getOperationType(requestType));
}n/a
function getEntryChangeNotificationControl(req, obj, callback) {
// if we want to return a ECNC
if (req.persistentSearch.value.returnECs) {
var attrs = obj.attributes;
var value = {};
value.changeType = getOperationType(attrs.changetype);
// if it's a modDN request, fill in the previous DN
if (value.changeType === 8 && attrs.previousDN) {
value.previousDN = attrs.previousDN;
}
value.changeNumber = attrs.changenumber;
return new EntryChangeNotificationControl({ value: value });
} else {
return false;
}
}n/a
function SearchPager(opts) {
assert.object(opts);
assert.func(opts.callback);
assert.number(opts.pageSize);
EventEmitter.call(this, {});
this.callback = opts.callback;
this.controls = opts.controls;
this.pageSize = opts.pageSize;
this.pagePause = opts.pagePause;
this.controls.forEach(function (control) {
if (control.type === PagedControl.OID) {
// The point of using SearchPager is not having to do this.
// Toss an error if the pagedResultsControl is present
throw new Error('redundant pagedResultControl');
}
});
this.finished = false;
this.started = false;
var emitter = new EventEmitter();
emitter.on('searchEntry', this.emit.bind(this, 'searchEntry'));
emitter.on('end', this._onEnd.bind(this));
emitter.on('error', this._onError.bind(this));
this.childEmitter = emitter;
}n/a
function EventEmitter() {
EventEmitter.init.call(this);
}n/a
function _nextPage(cookie) {
var controls = this.controls.slice(0);
controls.push(new PagedControl({
value: {
size: this.pageSize,
cookie: cookie
}
}));
this.emit('search', controls, this.childEmitter,
this._sendCallback.bind(this));
}...
module.exports = SearchPager;
/**
* Start the paged search.
*/
SearchPager.prototype.begin = function begin() {
// Starting first page
this._nextPage(null);
};
SearchPager.prototype._onEnd = function _onEnd(res) {
var self = this;
var cookie = null;
res.controls.forEach(function (control) {
if (control.type === PagedControl.OID) {
...function _onEnd(res) {
var self = this;
var cookie = null;
res.controls.forEach(function (control) {
if (control.type === PagedControl.OID) {
cookie = control.value.cookie;
}
});
// Pass a noop callback by default for page events
var nullCb = function () { };
if (cookie === null) {
// paged search not supported
this.finished = true;
this.emit('page', res, nullCb);
var err = new Error('missing paged control');
err.name = 'PagedError';
if (this.listeners('pageError').length > 0) {
this.emit('pageError', err);
// If the consumer as subscribed to pageError, SearchPager is absolved
// from deliverying the fault via the 'error' event. Emitting an 'end'
// event after 'error' breaks the contract that the standard client
// provides, so it's only a possibility if 'pageError' is used instead.
this.emit('end', res);
} else {
this.emit('error', err);
// No end event possible per explaination above.
}
return;
}
if (cookie.length === 0) {
// end of paged results
this.finished = true;
this.emit('page', nullCb);
this.emit('end', res);
} else {
if (this.pagePause) {
// Wait to fetch next page until callback is invoked
// Halt page fetching if called with error
this.emit('page', res, function (err) {
if (!err) {
self._nextPage(cookie);
} else {
// the paged search has been canceled so emit an end
self.emit('end', res);
}
});
} else {
this.emit('page', res, nullCb);
this._nextPage(cookie);
}
}
}n/a
function _onError(err) {
this.finished = true;
this.emit('error', err);
}n/a
function _sendCallback(err, res) {
if (err) {
this.finished = true;
if (!this.started) {
// EmitSend error during the first page, bail via callback
this.callback(err, null);
} else {
this.emit('error', err);
}
} else {
// search successfully send
if (!this.started) {
this.started = true;
// send self as emitter as the client would
this.callback(null, this);
}
}
}n/a
function begin() {
// Starting first page
this._nextPage(null);
}n/a
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)');
u.secure = (u.protocol === 'ldaps:');
if (!u.hostname)
u.hostname = 'localhost';
if (!u.port) {
u.port = (u.secure ? 636 : 389);
} else {
u.port = parseInt(u.port, 10);
}
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 = filter.parseString('(objectclass=*)');
else
u.filter = filter.parseString(u.filter);
}
return u;
}n/a