Merge pull request #620 from ldapjs/next

Release v2!
This commit is contained in:
James Sumners 2020-05-31 09:10:01 -04:00 committed by GitHub
commit e54555ae0a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
166 changed files with 9676 additions and 10624 deletions

View File

@ -1,6 +0,0 @@
((nil . ((indent-tabs-mode . nil)
(tab-width . 8)
(fill-column . 80)))
(js-mode . ((js-indent-level . 2)
(indent-tabs-mode . nil)
)))

41
.github/workflows/integration.yml vendored Normal file
View File

@ -0,0 +1,41 @@
name: 'Integration Tests'
# Notes:
# https://github.community/t5/GitHub-Actions/Github-Actions-services-not-reachable/m-p/30739/highlight/true#M538
on:
pull_request:
branches:
- master
- next
jobs:
baseline:
name: Baseline Tests
runs-on: ubuntu-latest
# services:
# openldap:
# image: docker.pkg.github.com/ldapjs/docker-test-openldap/openldap:1.0
# ports:
# - 389:389
# - 636:636
steps:
- uses: actions/checkout@v1
- uses: actions/setup-node@v1
# Hack way to start service since GitHub doesn't integrate with its own services
- name: Docker login
run: docker login docker.pkg.github.com -u ${GITHUB_ACTOR} -p ${GITHUB_TOKEN}
env:
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
- name: Pull Docker image
run: docker pull "docker.pkg.github.com/ldapjs/docker-test-openldap/openldap:latest"
- name: Start OpenLDAP service
run: docker run -it -d --name openldap -p 389:389 -p 636:636 docker.pkg.github.com/ldapjs/docker-test-openldap/openldap:latest
- name: Install Packages
run: npm install
- name: Run Tests
run: npm run test:integration

51
.github/workflows/main.yml vendored Normal file
View File

@ -0,0 +1,51 @@
name: 'Lint And Test'
on:
pull_request:
branches:
- master
- next
jobs:
lint:
name: Lint Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions/setup-node@v1
- name: Install Packages
run: npm install
- name: Lint Code
run: npm run lint:ci
run_tests:
name: Unit Tests
strategy:
matrix:
os:
- ubuntu-latest
- windows-latest
node:
- 10.13.0
- 10.x
- 12.x
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v1
- uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node }}
- name: Install Packages
run: npm install
- name: Run Tests
run: npm run test:ci
- name: Coveralls Parallel
uses: coverallsapp/github-action@master
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
parallel: true
- name: Coveralls Finished
uses: coverallsapp/github-action@master
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
parallel-finished: true

77
.gitignore vendored
View File

@ -1,8 +1,77 @@
build
node_modules
coverage
*.log
*.ldif *.ldif
*.tar.* *.tar.*
*.tgz *.tgz
# Lock files
pnpm-lock.yaml
shrinkwrap.yaml
package-lock.json
yarn.lock
# Logs
logs
*.log
npm-debug.log*
# Runtime data
pids
*.pid
*.seed
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directory
node_modules
# Optional npm cache directory
.npm
# Optional REPL history
.node_repl_history
# 0x
.__browserify_string_empty.js
profile-*
*.flamegraph
# tap --cov
.nyc_output/
# JetBrains IntelliJ IDEA
.idea/
*.iml
# VS Code
.vscode/
# xcode
build/*
*.mode1
*.mode1v3
*.mode2v3
*.perspective
*.perspectivev3
*.pbxuser
*.xcworkspace
xcuserdata
# macOS
.DS_Store .DS_Store
# keys
*.pem
*.env.json
*.env

9
.gitmodules vendored
View File

@ -1,9 +0,0 @@
[submodule "deps/javascriptlint"]
path = deps/javascriptlint
url = https://github.com/davepacheco/javascriptlint
[submodule "deps/jsstyle"]
path = deps/jsstyle
url = https://github.com/davepacheco/jsstyle
[submodule "deps/restdown"]
path = deps/restdown
url = https://github.com/trentm/restdown

6
.taprc Normal file
View File

@ -0,0 +1,6 @@
esm: false
jsx: false
ts: false
files:
- 'test/**/*.test.js'

View File

@ -1,6 +0,0 @@
language: node_js
node_js:
- "0.10"
- "0.12"
- "4"
- "stable"

View File

@ -1,5 +1,9 @@
# ldapjs Changelog # ldapjs Changelog
## 2.0.0
- Going foward, please see https://github.com/ldapjs/node-ldapjs/releases
## 1.0.2 ## 1.0.2
- Update dtrace-provider dependency - Update dtrace-provider dependency

View File

@ -1,4 +1,4 @@
Copyright (c) 2011 Mark Cavage, All rights reserved. Copyright (c) 2019 LDAPjs, All rights reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@ -1,58 +0,0 @@
#
# Copyright (c) 2012, Joyent, Inc. All rights reserved.
#
# Makefile: basic Makefile for template API service
#
# This Makefile is a template for new repos. It contains only repo-specific
# logic and uses included makefiles to supply common targets (javascriptlint,
# jsstyle, restdown, etc.), which are used by other repos as well. You may well
# need to rewrite most of this file, but you shouldn't need to touch the
# included makefiles.
#
# If you find yourself adding support for new targets that could be useful for
# other projects too, you should add these to the original versions of the
# included Makefiles (in eng.git) so that other teams can use them too.
#
#
# Tools
#
NPM := npm
TAP := ./node_modules/.bin/tap
#
# Files
#
DOC_FILES := $(shell find docs -name '*.md' -printf '%f\n')
RESTDOWN_FLAGS := -b docs/branding/
JS_FILES := $(shell find lib test -name '*.js') $(shell find bin -name 'ldapjs-*')
JSL_CONF_NODE = tools/jsl.node.conf
JSL_FILES_NODE = $(JS_FILES)
JSSTYLE_FILES = $(JS_FILES)
JSSTYLE_FLAGS = -f tools/jsstyle.conf
CLEAN_FILES += node_modules coverage $(SHRINKWRAP) cscope.files
include ./tools/mk/Makefile.defs
# Repo-specific targets
#
.PHONY: all
all: $(TAP) $(REPO_DEPS)
$(NPM) rebuild
$(TAP): | $(NPM_EXEC)
$(NPM) install
CLEAN_FILES += $(TAP) ./node_modules/tap
.PHONY: test
test: $(TAP)
$(NPM) test
.PHONY: cover
cover: test
$(NPM) run report
include ./tools/mk/Makefile.deps
include ./tools/mk/Makefile.targ

View File

@ -1,9 +1,7 @@
# LDAPjs # LDAPjs
[!['Build status'][travis_image_url]][travis_page_url] [![Build Status](https://github.com/ldapjs/node-ldapjs/workflows/.github/workflows/main.yml/badge.svg)]
[![Coverage Status](https://coveralls.io/repos/github/ldapjs/node-ldapjs/badge.svg)]
[travis_image_url]: https://api.travis-ci.org/joyent/node-ldapjs.svg
[travis_page_url]: https://travis-ci.org/joyent/node-ldapjs
LDAPjs makes the LDAP protocol a first class citizen in Node.js. LDAPjs makes the LDAP protocol a first class citizen in Node.js.
@ -45,10 +43,12 @@ client on your system:
npm install ldapjs npm install ldapjs
DTrace support is included in ldapjs. To enable it, `npm install dtrace-provider`.
## License ## License
MIT. MIT.
## Bugs ## Bugs
See <https://github.com/mcavage/node-ldapjs/issues>. See <https://github.com/ldapjs/node-ldapjs/issues>.

View File

@ -1,171 +0,0 @@
#!/usr/bin/env node
// -*- mode: js -*-
// Copyright 2011 Mark Cavage. All rights reserved.
var fs = require('fs');
var path = require('path');
var dashdash = require('dashdash');
var vasync = require('vasync');
var ldap = require('../lib/index');
var Logger = require('bunyan');
///--- Globals
var opts = [
{
names: ['file', 'f'],
type: 'string',
help: 'Input file',
helpArg: 'FILE'
},
{ group: 'General Options' },
{
names: ['help', 'h'],
type: 'bool',
help: 'Print this help and exit.'
},
{
names: ['debug', 'd'],
type: 'integer',
help: 'Set debug level <0-2>',
helpArg: 'LEVEL'
},
{ group: 'Connection Options' },
{
names: ['url', 'u'],
type: 'string',
help: 'LDAP server URL',
helpArg: 'URL',
default: 'ldap://127.0.0.1:389'
},
{
names: ['binddn', 'D'],
type: 'string',
help: 'Bind DN',
helpArg: 'BIND_DN',
default: ''
},
{
names: ['password', 'w'],
type: 'string',
help: 'Bind password',
helpArg: 'PASSWD',
default: ''
},
{
names: ['insecure', 'i'],
type: 'bool',
env: 'LDAPJS_TLS_INSECURE',
help: 'Disable SSL certificate verification',
default: false
}
];
var parser = dashdash.createParser({options: opts});
///--- Helpers
function usage(code, message) {
var msg = (message ? message + '\n' : '') +
'Usage: ' + path.basename(process.argv[1]) + ' [OPTIONS] [JSON]\n\n' +
parser.help({includeEnv: true});
process.stderr.write(msg + '\n');
process.exit(code);
}
function perror(err) {
if (parsed.debug) {
process.stderr.write(err.stack + '\n');
} else {
process.stderr.write(err.message + '\n');
}
process.exit(1);
}
///--- Mainline
var logLevel = 'info';
var parsed;
try {
parsed = parser.parse(process.argv);
if (parsed.file) {
parsed.file = JSON.parse(fs.readFileSync(parsed.file, 'utf8'));
if (!Array.isArray(parsed.file))
parsed.file = [parsed.file];
}
} catch (e) {
usage(1, e.toString());
}
if (parsed.help)
usage(0);
if (!parsed.file) {
parsed.file = [];
parsed._args.forEach(function (a) {
var o = JSON.parse(a);
if (Array.isArray(o)) {
o.forEach(function (i) {
parsed.file.push(o);
});
return;
}
parsed.file.push(o);
});
if (parsed.file.length === 0)
parsed.file = null;
}
if (!parsed.file)
usage(1, 'either -f or arguments must be used for adding objects');
if (parsed.debug)
logLevel = (parsed.debug > 1 ? 'trace' : 'debug');
var log = new Logger({
name: 'ldapjs',
component: 'client',
stream: process.stderr,
level: logLevel
});
var client = ldap.createClient({
url: parsed.url,
log: log,
strictDN: false,
tlsOptions: {
rejectUnauthorized: !parsed.insecure
}
});
client.on('error', function (err) {
perror(err);
});
client.bind(parsed.binddn, parsed.password, function (err, res) {
if (err) {
perror(err);
}
vasync.forEachPipeline({
inputs: parsed.file,
func: function (entry, cb) {
var dn = entry.dn;
delete entry.dn;
client.add(dn, entry, cb);
}
}, function (err2, res2) {
if (err2) {
perror(err2);
}
client.unbind(function () { return; });
});
});

View File

@ -1,167 +0,0 @@
#!/usr/bin/env node
// -*- mode: js -*-
// Copyright 2011 Mark Cavage. All rights reserved.
var fs = require('fs');
var path = require('path');
var dashdash = require('dashdash');
var ldap = require('../lib/index');
var Logger = require('bunyan');
///--- Globals
var opts = [
{
names: ['attribute', 'a'],
type: 'string',
help: 'Comparison attribute',
helpArg: 'ATTR'
},
{
names: ['value', 'v'],
type: 'string',
help: 'Comparison value',
helpArg: 'VAL'
},
{ group: 'General Options' },
{
names: ['help', 'h'],
type: 'bool',
help: 'Print this help and exit.'
},
{
names: ['debug', 'd'],
type: 'integer',
help: 'Set debug level <0-2>',
helpArg: 'LEVEL'
},
{ group: 'Connection Options' },
{
names: ['url', 'u'],
type: 'string',
help: 'LDAP server URL',
helpArg: 'URL',
default: 'ldap://127.0.0.1:389'
},
{
names: ['binddn', 'D'],
type: 'string',
help: 'Bind DN',
helpArg: 'BIND_DN',
default: ''
},
{
names: ['password', 'w'],
type: 'string',
help: 'Bind password',
helpArg: 'PASSWD',
default: ''
},
{
names: ['insecure', 'i'],
type: 'bool',
env: 'LDAPJS_TLS_INSECURE',
help: 'Disable SSL certificate verification',
default: false
}
];
var parser = dashdash.createParser({options: opts});
///--- Helpers
function usage(code, message) {
var msg = (message ? message + '\n' : '') +
'Usage: ' + path.basename(process.argv[1]) + ' [OPTIONS] DN\n\n' +
parser.help({includeEnv: true});
process.stderr.write(msg + '\n');
process.exit(code);
}
function perror(err) {
if (parsed.debug) {
process.stderr.write(err.stack + '\n');
} else {
process.stderr.write(err.message + '\n');
}
process.exit(1);
}
///--- Mainline
var logLevel = 'info';
var parsed;
try {
parsed = parser.parse(process.argv);
} catch (e) {
usage(1, e.toString());
}
if (parsed.help)
usage(0);
if (parsed._args.length < 1)
usage(1, 'DN required');
try {
parsed._args.forEach(function (dn) {
ldap.parseDN(dn);
});
} catch (e) {
usage(1, e.toString());
}
if (!parsed.attribute || typeof (parsed.value) !== 'string')
usage(1, 'attribute and value required');
if (parsed.debug)
logLevel = (parsed.debug > 1 ? 'trace' : 'debug');
var log = new Logger({
name: 'ldapjs',
component: 'client',
stream: process.stderr,
level: logLevel
});
var client = ldap.createClient({
url: parsed.url,
log: log,
strictDN: false,
tlsOptions: {
rejectUnauthorized: !parsed.insecure
}
});
client.on('error', function (err) {
perror(err);
});
client.bind(parsed.binddn, parsed.password, function (err, res) {
if (err)
perror(err);
var finished = 0;
parsed._args.forEach(function (dn) {
client.compare(dn, parsed.attribute, parsed.value, function (err, match) {
if (err)
perror(err);
process.stdout.write(match + '\n');
if (++finished === parsed._args.length) {
client.unbind(function () {
return;
});
}
});
});
});

View File

@ -1,148 +0,0 @@
#!/usr/bin/env node
// -*- mode: js -*-
// Copyright 2011 Mark Cavage. All rights reserved.
var fs = require('fs');
var path = require('path');
var dashdash = require('dashdash');
var ldap = require('../lib/index');
var Logger = require('bunyan');
///--- Globals
var opts = [
{ group: 'General Options' },
{
names: ['help', 'h'],
type: 'bool',
help: 'Print this help and exit.'
},
{
names: ['debug', 'd'],
type: 'integer',
help: 'Set debug level <0-2>',
helpArg: 'LEVEL'
},
{ group: 'Connection Options' },
{
names: ['url', 'u'],
type: 'string',
help: 'LDAP server URL',
helpArg: 'URL',
default: 'ldap://127.0.0.1:389'
},
{
names: ['binddn', 'D'],
type: 'string',
help: 'Bind DN',
helpArg: 'bind_DN',
default: ''
},
{
names: ['password', 'w'],
type: 'string',
help: 'Bind password',
helpArg: 'PASSWD',
default: ''
},
{
names: ['insecure', 'i'],
type: 'bool',
env: 'LDAPJS_TLS_INSECURE',
help: 'Disable SSL certificate verification',
default: false
}
];
var parser = dashdash.createParser({options: opts});
///--- Helpers
function usage(code, message) {
var msg = (message ? message + '\n' : '') +
'Usage: ' + path.basename(process.argv[1]) + ' [OPTIONS] DN\n\n' +
parser.help({includeEnv: true});
process.stderr.write(msg + '\n');
process.exit(code);
}
function perror(err) {
if (parsed.debug) {
process.stderr.write(err.stack + '\n');
} else {
process.stderr.write(err.message + '\n');
}
process.exit(1);
}
///--- Mainline
var logLevel = 'info';
var parsed;
try {
parsed = parser.parse(process.argv);
} catch (e) {
usage(1, e.toString());
}
if (parsed.help)
usage(0);
if (parsed._args.length < 1)
usage(1, 'DN required');
try {
parsed._args.forEach(function (dn) {
ldap.parseDN(dn);
});
} catch (e) {
usage(1, e.toString());
}
if (parsed.debug)
logLevel = (parsed.debug > 1 ? 'trace' : 'debug');
var log = new Logger({
name: 'ldapjs',
component: 'client',
stream: process.stderr,
level: logLevel
});
var client = ldap.createClient({
url: parsed.url,
log: log,
strictDN: false,
tlsOptions: {
rejectUnauthorized: !parsed.insecure
}
});
client.on('error', function (err) {
perror(err);
});
client.bind(parsed.binddn, parsed.password, function (err, res) {
if (err)
perror(err);
var finished = 0;
function callback(err) {
if (err)
perror(err);
if (++finished === parsed._args.length)
client.unbind(function () { return; });
}
parsed._args.forEach(function (dn) {
client.del(dn, callback);
});
});

View File

@ -1,185 +0,0 @@
#!/usr/bin/env node
// -*- mode: js -*-
// Copyright 2011 Mark Cavage. All rights reserved.
var fs = require('fs');
var path = require('path');
var dashdash = require('dashdash');
var ldap = require('../lib/index');
var Logger = require('bunyan');
///--- Globals
var opts = [
{
names: ['attribute', 'a'],
type: 'string',
help: 'Attribute to modify',
helpArg: 'ATTR'
},
{
names: ['value', 'v'],
type: 'arrayOfString',
help: 'Desired value',
helpArg: 'VAL'
},
{
names: ['type', 't'],
type: 'string',
help: 'Attribute type',
helpArg: 'TYPE'
},
{ group: 'General options' },
{
names: ['help', 'h'],
type: 'bool',
help: 'Print this help and exit.'
},
{
names: ['debug', 'd'],
type: 'integer',
help: 'Set debug level <0-2>',
helpArg: 'LEVEL'
},
{ group: 'Connection Options' },
{
names: ['url', 'u'],
type: 'string',
help: 'LDAP server URL',
helpArg: 'URL',
default: 'ldap://127.0.0.1:389'
},
{
names: ['binddn', 'D'],
type: 'string',
help: 'Bind DN',
helpArg: 'BIND_DN',
default: ''
},
{
names: ['password', 'w'],
type: 'string',
help: 'Bind password',
helpArg: 'PASSWD',
default: ''
},
{
names: ['insecure', 'i'],
type: 'bool',
env: 'LDAPJS_TLS_INSECURE',
help: 'Disable SSL certificate verification',
default: false
}
];
var parser = dashdash.createParser({options: opts});
///--- Helpers
function usage(code, message) {
var msg = (message ? message + '\n' : '') +
'Usage: ' + path.basename(process.argv[1]) + ' [OPTIONS] DN\n\n' +
parser.help({includeEnv: true});
process.stderr.write(msg + '\n');
process.exit(code);
}
function perror(err) {
if (parsed.debug) {
process.stderr.write(err.stack + '\n');
} else {
process.stderr.write(err.message + '\n');
}
process.exit(1);
}
///--- Mainline
var logLevel = 'info';
var parsed;
try {
parsed = parser.parse(process.argv);
} catch (e) {
usage(1, e.toString());
}
if (parsed.help)
usage(0);
if (parsed._args.length < 1)
usage(1, 'DN required');
try {
parsed._args.forEach(function (dn) {
ldap.parseDN(dn);
});
} catch (e) {
usage(1, e.toString());
}
if (!parsed.type)
parsed.type = 'replace';
if (!parsed.attribute || !Array.isArray(parsed.value))
usage(1, 'attribute and value required');
if (parsed.debug)
logLevel = (parsed.debug > 1 ? 'trace' : 'debug');
var log = new Logger({
name: 'ldapjs',
component: 'client',
stream: process.stderr,
level: logLevel
});
var client = ldap.createClient({
url: parsed.url,
log: log,
strictDN: false,
tlsOptions: {
rejectUnauthorized: !parsed.insecure
}
});
client.on('error', function (err) {
perror(err);
});
client.bind(parsed.binddn, parsed.password, function (err, res) {
if (err)
perror(err);
var finished = 0;
var mod = {};
mod[parsed.attribute] = [];
parsed.value.forEach(function (v) {
mod[parsed.attribute].push(v);
});
var change = new ldap.Change({
type: parsed.type,
modification: mod
});
function callback(err) {
if (err)
perror(err);
if (++finished === parsed._args.length) {
client.unbind(function () {
return;
});
}
}
parsed._args.forEach(function (dn) {
client.modify(dn, change, callback);
});
});

View File

@ -1,326 +0,0 @@
#!/usr/bin/env node
// -*- mode: js -*-
// Copyright 2011 Mark Cavage. All rights reserved.
var path = require('path');
var dashdash = require('dashdash');
var ldap = require('../lib/index');
var Logger = require('bunyan');
///--- Globals
dashdash.addOptionType({
name: 'ldap.Filter',
takesArg: true,
helpArg: 'LDAP_FILTER',
parseArg: function (option, optstr, arg) {
return ldap.parseFilter(arg);
}
});
dashdash.addOptionType({
name: 'ldap.scope',
takesArg: true,
helpArg: 'SCOPE',
parseArg: function (option, optstr, arg) {
if (!/^base|one|sub$/.test(arg)) {
throw new TypeError('Scope must be <base|one|sub>');
}
return arg;
}
});
dashdash.addOptionType({
name: 'ldap.outputFormat',
takesArg: true,
helpArg: 'FORMAT',
parseArg: function (option, optstr, arg) {
var formats = ['json', 'jsonl', 'jsona'];
if (formats.indexOf(arg) === -1) {
throw new TypeError('Must be valid format type');
}
return arg;
}
});
var opts = [
{
names: ['base', 'b'],
type: 'string',
help: 'Base DN of search',
helpArg: 'BASE_DN',
default: ''
},
{
names: ['scope', 's'],
type: 'ldap.scope',
help: 'Search scope <base|sub|one>',
helpArg: 'SCOPE',
default: 'sub'
},
{
names: ['timeout', 't'],
type: 'integer',
help: 'Search timeout',
helpArg: 'SECS'
},
{
names: ['persistent', 'p'],
type: 'bool',
help: 'Enable persistent search control',
default: false
},
{
names: ['paged', 'g'],
type: 'number',
help: 'Enable paged search result control',
helpArg: 'PAGE_SIZE'
},
{
names: ['control', 'c'],
type: 'arrayOfString',
help: 'Send addition control OID',
helpArg: 'OID',
default: []
},
{ group: 'General Options' },
{
names: ['help', 'h'],
type: 'bool',
help: 'Print this help and exit.'
},
{
names: ['debug', 'd'],
type: 'integer',
help: 'Set debug level <0-2>',
helpArg: 'LEVEL'
},
{ group: 'Output Options' },
{
names: ['format', 'o'],
type: 'ldap.outputFormat',
helpWrap: false,
help: ('Specify and output format. One of:\n' +
' json: JSON objects (default)\n' +
' jsonl: Line-delimited JSON\n' +
' jsona: Array of JSON objects\n'),
default: 'json'
},
{ group: 'Connection Options' },
{
names: ['url', 'u'],
type: 'string',
help: 'LDAP server URL',
helpArg: 'URL',
default: 'ldap://127.0.0.1:389'
},
{
names: ['binddn', 'D'],
type: 'string',
help: 'Bind DN',
helpArg: 'BIND_DN',
default: ''
},
{
names: ['password', 'w'],
type: 'string',
help: 'Bind password',
helpArg: 'PASSWD',
default: ''
},
{
names: ['insecure', 'i'],
type: 'bool',
env: 'LDAPJS_TLS_INSECURE',
help: 'Disable SSL certificate verification',
default: false
}
];
var parser = dashdash.createParser({options: opts});
///--- Helpers
function usage(code, message) {
var msg = (message ? message + '\n' : '') +
'Usage: ' + path.basename(process.argv[1]) +
' [OPTIONS] FILTER [ATTRIBUTES...]\n\n' +
parser.help({includeEnv: true});
process.stderr.write(msg + '\n');
process.exit(code);
}
function perror(err) {
if (parsed.debug) {
process.stderr.write(err.stack + '\n');
} else {
process.stderr.write(err.message + '\n');
}
process.exit(1);
}
function EntryFormatter(fp, format) {
this.format = format;
this.started = false;
this.ended = false;
this.fp = fp;
}
EntryFormatter.prototype.write = function write(entry) {
switch (this.format) {
case 'json':
this.fp.write(JSON.stringify(entry.object, null, 2) + '\n');
break;
case 'jsonl':
this.fp.write(JSON.stringify(entry.object) + '\n');
break;
case 'jsona':
this.fp.write((this.started) ? ',\n' : '[\n');
this.started = true;
// pretty-print with indent
this.fp.write(
JSON.stringify(entry.object, null, 2)
.split('\n')
.map(function (line) { return ' ' + line; })
.join('\n'));
break;
default:
throw new Error('invalid output format');
}
};
EntryFormatter.prototype.end = function end() {
if (this.ended) {
return;
}
this.ended = true;
if (this.format === 'jsona') {
this.fp.write('\n]\n');
}
};
///--- Mainline
var parsed;
process.stdout.on('error', function (err) {
if (err.code === 'EPIPE') {
process.exit(0);
} else {
throw err;
}
});
try {
parsed = parser.parse(process.argv);
} catch (e) {
usage(1, e.toString());
}
if (parsed.help)
usage(0);
if (parsed._args.length < 1)
usage(1, 'filter required');
try {
ldap.parseFilter(parsed._args[0]);
} catch (e) {
usage(1, e.message);
}
var logLevel = 'info';
if (parsed.debug)
logLevel = (parsed.debug > 1 ? 'trace' : 'debug');
var formatter = new EntryFormatter(process.stdout, parsed.format);
var log = new Logger({
name: 'ldapjs',
component: 'client',
stream: process.stderr,
level: logLevel
});
var client = ldap.createClient({
url: parsed.url,
log: log,
strictDN: false,
timeout: parsed.timeout || false,
tlsOptions: {
rejectUnauthorized: !parsed.insecure
}
});
client.on('error', function (err) {
perror(err);
});
client.on('timeout', function (req) {
process.stderr.write('Timeout reached\n');
process.exit(1);
});
client.bind(parsed.binddn, parsed.password, function (err, res) {
if (err)
perror(err);
var controls = [];
parsed.control.forEach(function (c) {
controls.push(new ldap.Control({
type: c,
criticality: true
}));
});
if (parsed.persistent) {
var pCtrl = new ldap.PersistentSearchControl({
type: '2.16.840.1.113730.3.4.3',
value: {
changeTypes: 15,
changesOnly: false,
returnECs: true
}
});
controls.push(pCtrl);
}
var req = {
scope: parsed.scope || 'sub',
filter: parsed._args[0],
attributes: parsed._args.length > 1 ? parsed._args.slice(1) : []
};
if (parsed.paged) {
req.paged = {
pageSize: parsed.paged
};
}
client.search(parsed.base, req, controls, function (err, res) {
if (err)
perror(err);
res.on('searchEntry', function (entry) {
formatter.write(entry);
});
res.on('error', function (err) {
formatter.end();
perror(err);
});
res.on('end', function (res) {
formatter.end();
if (res.status !== 0) {
process.stderr.write(ldap.getMessage(res.status) + '\n');
}
client.unbind(function () {
return;
});
});
});
});

1
deps/javascriptlint vendored

@ -1 +0,0 @@
Subproject commit e1bd0abfd424811af469d1ece3af131d95443924

1
deps/jsstyle vendored

@ -1 +0,0 @@
Subproject commit d75b7ca8308be17c80e2b120f2a01d4a0c20d8a8

1
deps/restdown vendored

@ -1 +0,0 @@
Subproject commit 34a843cfce0ff988bf5073706882722a61036786

8
docker-compose.yml Normal file
View File

@ -0,0 +1,8 @@
version: '3'
services:
openldap:
image: docker.pkg.github.com/ldapjs/docker-test-openldap/openldap:latest
ports:
- 389:389
- 636:636

View File

@ -26,19 +26,40 @@ client is:
|---------------|-----------------------------------------------------------| |---------------|-----------------------------------------------------------|
|url |A valid LDAP URL (proto/host/port only) | |url |A valid LDAP URL (proto/host/port only) |
|socketPath |Socket path if using AF\_UNIX sockets | |socketPath |Socket path if using AF\_UNIX sockets |
|log |Bunyan logger instance (Default: built-in instance) | |log |A compatible logger instance (Default: no-op logger) |
|timeout |Milliseconds client should let operations live for before timing out (Default: Infinity)| |timeout |Milliseconds client should let operations live for before timing out (Default: Infinity)|
|connectTimeout |Milliseconds client should wait before timing out on TCP connections (Default: OS default)| |connectTimeout |Milliseconds client should wait before timing out on TCP connections (Default: OS default)|
|tlsOptions |Additional options passed to TLS connection layer when connecting via `ldaps://` (See: The TLS docs for node.js)| |tlsOptions |Additional options passed to TLS connection layer when connecting via `ldaps://` (See: The TLS docs for node.js)|
|idleTimeout |Milliseconds after last activity before client emits idle event| |idleTimeout |Milliseconds after last activity before client emits idle event|
|strictDN |Force strict DN parsing for client methods (Default is true)| |strictDN |Force strict DN parsing for client methods (Default is true)|
|reconnect |Try to reconnect when the connection gets lost (Default is false)|
### Note On Logger
A passed in logger is expected to conform to the [Bunyan](https://www.npmjs.com/package/bunyan)
API. Specifically, the logger is expected to have a `child()` method. If a logger
is supplied that does not have such a method, then a shim version is added
that merely returns the passed in logger.
Known compatible loggers are:
+ [Bunyan](https://www.npmjs.com/package/bunyan)
+ [Pino](https://www.npmjs.com/package/pino)
## Connection management ## Connection management
As LDAP is a stateful protocol (as opposed to HTTP), having connections torn As LDAP is a stateful protocol (as opposed to HTTP), having connections torn
down from underneath you is can be difficult to deal with. Several mechanisms down from underneath you can be difficult to deal with. Several mechanisms
have been provided to mitigate this trouble. have been provided to mitigate this trouble.
### Reconnect
You can provide a Boolean option indicating if a reconnect should be tried. For
more sophisticated control, you can provide an Object with the properties
`initialDelay` (default: `100`), `maxDelay` (default: `10000`) and
`failAfter` (default: `Infinity`).
After the reconnect you maybe need to [bind](#bind) again.
## Common patterns ## Common patterns
@ -156,9 +177,11 @@ A `Change` object maps to the LDAP protocol of a modify change, and requires you
to set the `operation` and `modification`. The `operation` is a string, and to set the `operation` and `modification`. The `operation` is a string, and
must be one of: must be one of:
||replace||Replaces the attribute referenced in `modification`. If the modification has no values, it is equivalent to a delete.|| | Operation | Description |
||add||Adds the attribute value(s) referenced in `modification`. The attribute may or may not already exist.|| |-----------|-------------|
||delete||Deletes the attribute (and all values) referenced in `modification`.|| | replace | Replaces the attribute referenced in `modification`. If the modification has no values, it is equivalent to a delete. |
| add | Adds the attribute value(s) referenced in `modification`. The attribute may or may not already exist. |
| delete | Deletes the attribute (and all values) referenced in `modification`. |
`modification` is just a plain old JS object with the values you want. `modification` is just a plain old JS object with the values you want.
@ -375,6 +398,13 @@ Example:
Performs an unbind operation against the LDAP server. Performs an unbind operation against the LDAP server.
Note that unbind operation is not an opposite operation
for bind. Unbinding results in disconnecting the client
regardless of whether a bind operation was performed.
The `callback` argument is optional as unbind does
not have a response.
Example: Example:
client.unbind(function(err) { client.unbind(function(err) {

View File

@ -289,7 +289,7 @@ with ldapjs.
var entry = req.toObject().attributes; var entry = req.toObject().attributes;
if (entry.objectclass.indexOf('unixUser') === -1) if (entry.objectclass.indexOf('unixUser') === -1)
return next(new ldap.ConstraintViolation('entry must be a unixUser')); return next(new ldap.ConstraintViolationError('entry must be a unixUser'));
var opts = ['-m']; var opts = ['-m'];
if (entry.description) { if (entry.description) {

View File

@ -409,7 +409,7 @@ the following code in as another handler (you'll need a
var entry = req.toObject().attributes; var entry = req.toObject().attributes;
if (entry.objectclass.indexOf('unixUser') === -1) if (entry.objectclass.indexOf('unixUser') === -1)
return next(new ldap.ConstraintViolation('entry must be a unixUser')); return next(new ldap.ConstraintViolationError('entry must be a unixUser'));
var opts = ['-m']; var opts = ['-m'];
if (entry.description) { if (entry.description) {
@ -496,7 +496,7 @@ As before, here's a breakdown of the code:
var entry = req.toObject().attributes; var entry = req.toObject().attributes;
if (entry.objectclass.indexOf('unixUser') === -1) if (entry.objectclass.indexOf('unixUser') === -1)
return next(new ldap.ConstraintViolation('entry must be a unixUser')); return next(new ldap.ConstraintViolationError('entry must be a unixUser'));
A few new things: A few new things:

View File

@ -16,10 +16,22 @@ The code to create a new server looks like:
The full list of options is: The full list of options is:
||log||You can optionally pass in a bunyan instance the client will use to acquire a logger.|| ||log||You can optionally pass in a Bunyan compatible logger instance the client will use to acquire a child logger.||
||certificate||A PEM-encoded X.509 certificate; will cause this server to run in TLS mode.|| ||certificate||A PEM-encoded X.509 certificate; will cause this server to run in TLS mode.||
||key||A PEM-encoded private key that corresponds to _certificate_ for SSL.|| ||key||A PEM-encoded private key that corresponds to _certificate_ for SSL.||
### Note On Logger
The passed in logger is expected to conform to the Log4j standard API.
Internally, [abstract-logging](https://www.npmjs.com/packages/abstract-logging) is
used to implement the interface. As a result, no log messages will be generated
unless an external logger is supplied.
Known compatible loggers are:
+ [Bunyan](https://www.npmjs.com/package/bunyan)
+ [Pino](https://www.npmjs.com/package/pino)
## Properties on the server object ## Properties on the server object
### maxConnections ### maxConnections
@ -27,9 +39,10 @@ The full list of options is:
Set this property to reject connections when the server's connection count gets Set this property to reject connections when the server's connection count gets
high. high.
### connections (getter only) ### connections (getter only) - DEPRECATED
The number of concurrent connections on the server. The number of concurrent connections on the server. This property is deprecated,
please use server.getConnections() instead.
### url ### url
@ -53,7 +66,7 @@ available.
Example: Example:
server.listen(389, '127.0.0.1', function() { server.listen(389, '127.0.0.1', function() {
console.log(LDAP server listening at: ' + server.url); console.log('LDAP server listening at: ' + server.url);
}); });
@ -84,6 +97,13 @@ This file descriptor must have already had the `bind(2)` and `listen(2)` system
calls invoked on it. Additionally, it must be set non-blocking; try calls invoked on it. Additionally, it must be set non-blocking; try
`fcntl(fd, F_SETFL, O_NONBLOCK)`. `fcntl(fd, F_SETFL, O_NONBLOCK)`.
## Inspecting server state
### server.getConnections(callback)
The LDAP server API mirrors the [Node.js `server.getConnections` API](https://nodejs.org/dist/latest-v12.x/docs/api/net.html#net_server_getconnections_callback). Callback
should take two arguments err and count.
# Routes # Routes
The LDAP server API is meant to be the LDAP-equivalent of the express/restify The LDAP server API is meant to be the LDAP-equivalent of the express/restify

View File

@ -1,199 +1,177 @@
var ldap = require('../lib/index'); var ldap = require('../lib/index')
/// --- Shared handlers
///--- Shared handlers function authorize (req, res, next) {
function authorize(req, res, next) {
/* Any user may search after bind, only cn=root has full power */ /* Any user may search after bind, only cn=root has full power */
var isSearch = (req instanceof ldap.SearchRequest); var isSearch = (req instanceof ldap.SearchRequest)
if (!req.connection.ldap.bindDN.equals('cn=root') && !isSearch) if (!req.connection.ldap.bindDN.equals('cn=root') && !isSearch) { return next(new ldap.InsufficientAccessRightsError()) }
return next(new ldap.InsufficientAccessRightsError());
return next(); return next()
} }
/// --- Globals
///--- Globals var SUFFIX = 'o=smartdc'
var db = {}
var SUFFIX = 'o=smartdc'; var server = ldap.createServer()
var db = {};
var server = ldap.createServer();
server.bind('cn=root', function (req, res, next) { server.bind('cn=root', function (req, res, next) {
if (req.dn.toString() !== 'cn=root' || req.credentials !== 'secret') if (req.dn.toString() !== 'cn=root' || req.credentials !== 'secret') { return next(new ldap.InvalidCredentialsError()) }
return next(new ldap.InvalidCredentialsError());
res.end(); res.end()
return next(); return next()
}); })
server.add(SUFFIX, authorize, function (req, res, next) { server.add(SUFFIX, authorize, function (req, res, next) {
var dn = req.dn.toString(); var dn = req.dn.toString()
if (db[dn]) if (db[dn]) { return next(new ldap.EntryAlreadyExistsError(dn)) }
return next(new ldap.EntryAlreadyExistsError(dn));
db[dn] = req.toObject().attributes; db[dn] = req.toObject().attributes
res.end(); res.end()
return next(); return next()
}); })
server.bind(SUFFIX, function (req, res, next) { server.bind(SUFFIX, function (req, res, next) {
var dn = req.dn.toString(); var dn = req.dn.toString()
if (!db[dn]) if (!db[dn]) { return next(new ldap.NoSuchObjectError(dn)) }
return next(new ldap.NoSuchObjectError(dn));
if (!db[dn].userpassword) if (!db[dn].userpassword) { return next(new ldap.NoSuchAttributeError('userPassword')) }
return next(new ldap.NoSuchAttributeError('userPassword'));
if (db[dn].userpassword.indexOf(req.credentials) === -1) if (db[dn].userpassword.indexOf(req.credentials) === -1) { return next(new ldap.InvalidCredentialsError()) }
return next(new ldap.InvalidCredentialsError());
res.end(); res.end()
return next(); return next()
}); })
server.compare(SUFFIX, authorize, function (req, res, next) { server.compare(SUFFIX, authorize, function (req, res, next) {
var dn = req.dn.toString(); var dn = req.dn.toString()
if (!db[dn]) if (!db[dn]) { return next(new ldap.NoSuchObjectError(dn)) }
return next(new ldap.NoSuchObjectError(dn));
if (!db[dn][req.attribute]) if (!db[dn][req.attribute]) { return next(new ldap.NoSuchAttributeError(req.attribute)) }
return next(new ldap.NoSuchAttributeError(req.attribute));
var matches = false; var matches = false
var vals = db[dn][req.attribute]; var vals = db[dn][req.attribute]
for (var i = 0; i < vals.length; i++) { for (var i = 0; i < vals.length; i++) {
if (vals[i] === req.value) { if (vals[i] === req.value) {
matches = true; matches = true
break; break
} }
} }
res.end(matches); res.end(matches)
return next(); return next()
}); })
server.del(SUFFIX, authorize, function (req, res, next) { server.del(SUFFIX, authorize, function (req, res, next) {
var dn = req.dn.toString(); var dn = req.dn.toString()
if (!db[dn]) if (!db[dn]) { return next(new ldap.NoSuchObjectError(dn)) }
return next(new ldap.NoSuchObjectError(dn));
delete db[dn]; delete db[dn]
res.end(); res.end()
return next(); return next()
}); })
server.modify(SUFFIX, authorize, function (req, res, next) { server.modify(SUFFIX, authorize, function (req, res, next) {
var dn = req.dn.toString(); var dn = req.dn.toString()
if (!req.changes.length) if (!req.changes.length) { return next(new ldap.ProtocolError('changes required')) }
return next(new ldap.ProtocolError('changes required')); if (!db[dn]) { return next(new ldap.NoSuchObjectError(dn)) }
if (!db[dn])
return next(new ldap.NoSuchObjectError(dn));
var entry = db[dn]; var entry = db[dn]
let mod
for (var i = 0; i < req.changes.length; i++) { for (var i = 0; i < req.changes.length; i++) {
mod = req.changes[i].modification; mod = req.changes[i].modification
switch (req.changes[i].operation) { switch (req.changes[i].operation) {
case 'replace': case 'replace':
if (!entry[mod.type]) if (!entry[mod.type]) { return next(new ldap.NoSuchAttributeError(mod.type)) }
return next(new ldap.NoSuchAttributeError(mod.type));
if (!mod.vals || !mod.vals.length) { if (!mod.vals || !mod.vals.length) {
delete entry[mod.type]; delete entry[mod.type]
} else { } else {
entry[mod.type] = mod.vals; entry[mod.type] = mod.vals
} }
break; break
case 'add': case 'add':
if (!entry[mod.type]) { if (!entry[mod.type]) {
entry[mod.type] = mod.vals; entry[mod.type] = mod.vals
} else { } else {
mod.vals.forEach(function (v) { mod.vals.forEach(function (v) {
if (entry[mod.type].indexOf(v) === -1) if (entry[mod.type].indexOf(v) === -1) { entry[mod.type].push(v) }
entry[mod.type].push(v); })
}); }
}
break; break
case 'delete': case 'delete':
if (!entry[mod.type]) if (!entry[mod.type]) { return next(new ldap.NoSuchAttributeError(mod.type)) }
return next(new ldap.NoSuchAttributeError(mod.type));
delete entry[mod.type]; delete entry[mod.type]
break; break
} }
} }
res.end(); res.end()
return next(); return next()
}); })
server.search(SUFFIX, authorize, function (req, res, next) { server.search(SUFFIX, authorize, function (req, res, next) {
var dn = req.dn.toString(); var dn = req.dn.toString()
if (!db[dn]) if (!db[dn]) { return next(new ldap.NoSuchObjectError(dn)) }
return next(new ldap.NoSuchObjectError(dn));
var scopeCheck; var scopeCheck
switch (req.scope) { switch (req.scope) {
case 'base': case 'base':
if (req.filter.matches(db[dn])) { if (req.filter.matches(db[dn])) {
res.send({ res.send({
dn: dn, dn: dn,
attributes: db[dn] attributes: db[dn]
}); })
} }
res.end(); res.end()
return next(); return next()
case 'one': case 'one':
scopeCheck = function (k) { scopeCheck = function (k) {
if (req.dn.equals(k)) if (req.dn.equals(k)) { return true }
return true;
var parent = ldap.parseDN(k).parent(); var parent = ldap.parseDN(k).parent()
return (parent ? parent.equals(req.dn) : false); return (parent ? parent.equals(req.dn) : false)
}; }
break; break
case 'sub': case 'sub':
scopeCheck = function (k) { scopeCheck = function (k) {
return (req.dn.equals(k) || req.dn.parentOf(k)); return (req.dn.equals(k) || req.dn.parentOf(k))
}; }
break; break
} }
Object.keys(db).forEach(function (key) { Object.keys(db).forEach(function (key) {
if (!scopeCheck(key)) if (!scopeCheck(key)) { return }
return;
if (req.filter.matches(db[key])) { if (req.filter.matches(db[key])) {
res.send({ res.send({
dn: key, dn: key,
attributes: db[key] attributes: db[key]
}); })
} }
}); })
res.end(); res.end()
return next(); return next()
}); })
/// --- Fire it up
///--- Fire it up
server.listen(1389, function () { server.listen(1389, function () {
console.log('LDAP server up at: %s', server.url); console.log('LDAP server up at: %s', server.url)
}); })

View File

@ -1,61 +1,54 @@
// Copyright 2015 Joyent, Inc. // Copyright 2015 Joyent, Inc.
var assert = require('assert'); var assert = require('assert')
var util = require('util'); var util = require('util')
var isDN = require('./dn').DN.isDN; var isDN = require('./dn').DN.isDN
var isAttribute = require('./attribute').isAttribute; var isAttribute = require('./attribute').isAttribute
/// --- Helpers
///--- Helpers
// Copied from mcavage/node-assert-plus // Copied from mcavage/node-assert-plus
function _assert(arg, type, name) { function _assert (arg, type, name) {
name = name || type; name = name || type
throw new assert.AssertionError({ throw new assert.AssertionError({
message: util.format('%s (%s) required', name, type), message: util.format('%s (%s) required', name, type),
actual: typeof (arg), actual: typeof (arg),
expected: type, expected: type,
operator: '===', operator: '===',
stackStartFunction: _assert.caller stackStartFunction: _assert.caller
}); })
} }
/// --- API
///--- API function stringDN (input, name) {
if (isDN(input) || typeof (input) === 'string') { return }
function stringDN(input, name) { _assert(input, 'DN or string', name)
if (isDN(input) || typeof (input) === 'string')
return;
_assert(input, 'DN or string', name);
} }
function optionalStringDN(input, name) { function optionalStringDN (input, name) {
if (input === undefined || isDN(input) || typeof (input) === 'string') if (input === undefined || isDN(input) || typeof (input) === 'string') { return }
return; _assert(input, 'DN or string', name)
_assert(input, 'DN or string', name);
} }
function optionalDN(input, name) { function optionalDN (input, name) {
if (input !== undefined && !isDN(input)) if (input !== undefined && !isDN(input)) { _assert(input, 'DN', name) }
_assert(input, 'DN', name);
} }
function optionalArrayOfAttribute(input, name) { function optionalArrayOfAttribute (input, name) {
if (input === undefined) if (input === undefined) { return }
return;
if (!Array.isArray(input) || if (!Array.isArray(input) ||
input.some(function (v) { return !isAttribute(v); })) { input.some(function (v) { return !isAttribute(v) })) {
_assert(input, 'array of Attribute', name); _assert(input, 'array of Attribute', name)
} }
} }
/// --- Exports
///--- Exports
module.exports = { module.exports = {
stringDN: stringDN, stringDN: stringDN,
optionalStringDN: optionalStringDN, optionalStringDN: optionalStringDN,
optionalDN: optionalDN, optionalDN: optionalDN,
optionalArrayOfAttribute: optionalArrayOfAttribute optionalArrayOfAttribute: optionalArrayOfAttribute
}; }

View File

@ -1,174 +1,160 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved. // Copyright 2011 Mark Cavage, Inc. All rights reserved.
var assert = require('assert'); var assert = require('assert')
var asn1 = require('asn1'); var asn1 = require('asn1')
var Protocol = require('./protocol'); var Protocol = require('./protocol')
/// --- API
///--- API function Attribute (options) {
function Attribute(options) {
if (options) { if (options) {
if (typeof (options) !== 'object') if (typeof (options) !== 'object') { throw new TypeError('options must be an object') }
throw new TypeError('options must be an object'); if (options.type && typeof (options.type) !== 'string') { throw new TypeError('options.type must be a string') }
if (options.type && typeof (options.type) !== 'string')
throw new TypeError('options.type must be a string');
} else { } else {
options = {}; options = {}
} }
this.type = options.type || ''; this.type = options.type || ''
this._vals = []; this._vals = []
if (options.vals !== undefined && options.vals !== null) if (options.vals !== undefined && options.vals !== null) { this.vals = options.vals }
this.vals = options.vals;
} }
module.exports = Attribute; module.exports = Attribute
Object.defineProperties(Attribute.prototype, { Object.defineProperties(Attribute.prototype, {
buffers: { buffers: {
get: function getBuffers() { get: function getBuffers () {
return this._vals; return this._vals
}, },
configurable: false configurable: false
}, },
json: { json: {
get: function getJson() { get: function getJson () {
return { return {
type: this.type, type: this.type,
vals: this.vals vals: this.vals
}; }
}, },
configurable: false configurable: false
}, },
vals: { vals: {
get: function getVals() { get: function getVals () {
var eType = _bufferEncoding(this.type); var eType = _bufferEncoding(this.type)
return this._vals.map(function (v) { return this._vals.map(function (v) {
return v.toString(eType); return v.toString(eType)
}); })
}, },
set: function setVals(vals) { set: function setVals (vals) {
var self = this; var self = this
this._vals = []; this._vals = []
if (Array.isArray(vals)) { if (Array.isArray(vals)) {
vals.forEach(function (v) { vals.forEach(function (v) {
self.addValue(v); self.addValue(v)
}); })
} else { } else {
self.addValue(vals); self.addValue(vals)
} }
}, },
configurable: false configurable: false
} }
}); })
Attribute.prototype.addValue = function addValue (val) {
Attribute.prototype.addValue = function addValue(val) {
if (Buffer.isBuffer(val)) { if (Buffer.isBuffer(val)) {
this._vals.push(val); this._vals.push(val)
} else { } else {
this._vals.push(new Buffer(val + '', _bufferEncoding(this.type))); this._vals.push(Buffer.from(val + '', _bufferEncoding(this.type)))
} }
}; }
/* BEGIN JSSTYLED */ /* BEGIN JSSTYLED */
Attribute.compare = function compare(a, b) { Attribute.compare = function compare (a, b) {
if (!(Attribute.isAttribute(a)) || !(Attribute.isAttribute(b))) { if (!(Attribute.isAttribute(a)) || !(Attribute.isAttribute(b))) {
throw new TypeError('can only compare Attributes'); throw new TypeError('can only compare Attributes')
} }
if (a.type < b.type) return -1; if (a.type < b.type) return -1
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
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++) { 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
if (a.vals[i] > b.vals[i]) return 1; if (a.vals[i] > b.vals[i]) return 1
} }
return 0; return 0
}; }
/* END JSSTYLED */ /* END JSSTYLED */
Attribute.prototype.parse = function parse (ber) {
assert.ok(ber)
Attribute.prototype.parse = function parse(ber) { ber.readSequence()
assert.ok(ber); this.type = ber.readString()
ber.readSequence();
this.type = ber.readString();
if (ber.peek() === Protocol.LBER_SET) { if (ber.peek() === Protocol.LBER_SET) {
if (ber.readSequence(Protocol.LBER_SET)) { if (ber.readSequence(Protocol.LBER_SET)) {
var end = ber.offset + ber.length; var end = ber.offset + ber.length
while (ber.offset < end) while (ber.offset < end) { this._vals.push(ber.readString(asn1.Ber.OctetString, true)) }
this._vals.push(ber.readString(asn1.Ber.OctetString, true));
} }
} }
return true; return true
}; }
Attribute.prototype.toBer = function toBer (ber) {
assert.ok(ber)
Attribute.prototype.toBer = function toBer(ber) { ber.startSequence()
assert.ok(ber); ber.writeString(this.type)
ber.startSequence(Protocol.LBER_SET)
ber.startSequence();
ber.writeString(this.type);
ber.startSequence(Protocol.LBER_SET);
if (this._vals.length) { if (this._vals.length) {
this._vals.forEach(function (b) { this._vals.forEach(function (b) {
ber.writeByte(asn1.Ber.OctetString); ber.writeByte(asn1.Ber.OctetString)
ber.writeLength(b.length); ber.writeLength(b.length)
for (var i = 0; i < b.length; i++) for (var i = 0; i < b.length; i++) { ber.writeByte(b[i]) }
ber.writeByte(b[i]); })
});
} else { } else {
ber.writeStringArray([]); ber.writeStringArray([])
} }
ber.endSequence(); ber.endSequence()
ber.endSequence(); ber.endSequence()
return ber;
};
return ber
}
Attribute.prototype.toString = function () { Attribute.prototype.toString = function () {
return JSON.stringify(this.json); return JSON.stringify(this.json)
}; }
Attribute.toBer = function (attr, ber) { Attribute.toBer = function (attr, ber) {
return Attribute.prototype.toBer.call(attr, ber); return Attribute.prototype.toBer.call(attr, ber)
}; }
Attribute.isAttribute = function isAttribute (attr) {
Attribute.isAttribute = function isAttribute(attr) {
if (!attr || typeof (attr) !== 'object') { if (!attr || typeof (attr) !== 'object') {
return false; return false
} }
if (attr instanceof Attribute) { if (attr instanceof Attribute) {
return true; return true
} }
if ((typeof (attr.toBer) === 'function') && if ((typeof (attr.toBer) === 'function') &&
(typeof (attr.type) === 'string') && (typeof (attr.type) === 'string') &&
(Array.isArray(attr.vals)) && (Array.isArray(attr.vals)) &&
(attr.vals.filter(function (item) { (attr.vals.filter(function (item) {
return (typeof (item) === 'string' || return (typeof (item) === 'string' ||
Buffer.isBuffer(item)); Buffer.isBuffer(item))
}).length === attr.vals.length)) { }).length === attr.vals.length)) {
return true; return true
} }
return false; return false
}; }
function _bufferEncoding (type) {
function _bufferEncoding(type) { /* JSSTYLED */
/* JSSTYLED */ return /;binary$/.test(type) ? 'base64' : 'utf8'
return /;binary$/.test(type) ? 'base64' : 'utf8';
} }

View File

@ -1,129 +1,127 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved. // Copyright 2011 Mark Cavage, Inc. All rights reserved.
var assert = require('assert-plus'); var assert = require('assert-plus')
var Attribute = require('./attribute'); var Attribute = require('./attribute')
var Protocol = require('./protocol'); // var Protocol = require('./protocol')
/// --- API
///--- API function Change (options) {
function Change(options) {
if (options) { if (options) {
assert.object(options); assert.object(options)
assert.optionalString(options.operation); assert.optionalString(options.operation)
} else { } else {
options = {}; options = {}
} }
this._modification = false; this._modification = false
this.operation = options.operation || options.type || 'add'; this.operation = options.operation || options.type || 'add'
this.modification = options.modification || {}; this.modification = options.modification || {}
} }
Object.defineProperties(Change.prototype, { Object.defineProperties(Change.prototype, {
operation: { operation: {
get: function getOperation() { get: function getOperation () {
switch (this._operation) { switch (this._operation) {
case 0x00: return 'add'; case 0x00: return 'add'
case 0x01: return 'delete'; case 0x01: return 'delete'
case 0x02: return 'replace'; case 0x02: return 'replace'
default: default:
throw new Error('0x' + this._operation.toString(16) + ' is invalid'); throw new Error('0x' + this._operation.toString(16) + ' is invalid')
} }
}, },
set: function setOperation(val) { set: function setOperation (val) {
assert.string(val); assert.string(val)
switch (val.toLowerCase()) { switch (val.toLowerCase()) {
case 'add': case 'add':
this._operation = 0x00; this._operation = 0x00
break; break
case 'delete': case 'delete':
this._operation = 0x01; this._operation = 0x01
break; break
case 'replace': case 'replace':
this._operation = 0x02; this._operation = 0x02
break; break
default: default:
throw new Error('Invalid operation type: 0x' + val.toString(16)); throw new Error('Invalid operation type: 0x' + val.toString(16))
} }
}, },
configurable: false configurable: false
}, },
modification: { modification: {
get: function getModification() { get: function getModification () {
return this._modification; return this._modification
}, },
set: function setModification(val) { set: function setModification (val) {
if (Attribute.isAttribute(val)) { if (Attribute.isAttribute(val)) {
this._modification = val; this._modification = val
return; return
} }
// Does it have an attribute-like structure // Does it have an attribute-like structure
if (Object.keys(val).length == 2 && if (Object.keys(val).length === 2 &&
typeof (val.type) === 'string' && typeof (val.type) === 'string' &&
Array.isArray(val.vals)) { Array.isArray(val.vals)) {
this._modification = new Attribute({ this._modification = new Attribute({
type: val.type, type: val.type,
vals: val.vals vals: val.vals
}); })
return; return
} }
var keys = Object.keys(val); var keys = Object.keys(val)
if (keys.length > 1) { if (keys.length > 1) {
throw new Error('Only one attribute per Change allowed'); throw new Error('Only one attribute per Change allowed')
} else if (keys.length === 0) { } else if (keys.length === 0) {
return; return
} }
var k = keys[0]; var k = keys[0]
var _attr = new Attribute({type: k}); var _attr = new Attribute({ type: k })
if (Array.isArray(val[k])) { if (Array.isArray(val[k])) {
val[k].forEach(function (v) { val[k].forEach(function (v) {
_attr.addValue(v.toString()); _attr.addValue(v.toString())
}); })
} else { } else if (Buffer.isBuffer(val[k])) {
_attr.addValue(val[k].toString()); _attr.addValue(val[k])
} else if (val[k] !== undefined && val[k] !== null) {
_attr.addValue(val[k].toString())
} }
this._modification = _attr; this._modification = _attr
}, },
configurable: false configurable: false
}, },
json: { json: {
get: function getJSON() { get: function getJSON () {
return { return {
operation: this.operation, operation: this.operation,
modification: this._modification ? this._modification.json : {} modification: this._modification ? this._modification.json : {}
}; }
}, },
configurable: false configurable: false
} }
}); })
Change.isChange = function isChange(change) { Change.isChange = function isChange (change) {
if (!change || typeof (change) !== 'object') { if (!change || typeof (change) !== 'object') {
return false; return false
} }
if ((change instanceof Change) || if ((change instanceof Change) ||
((typeof (change.toBer) === 'function') && ((typeof (change.toBer) === 'function') &&
(change.modification !== undefined) && (change.modification !== undefined) &&
(change.operation !== undefined))) { (change.operation !== undefined))) {
return true; return true
} }
return false; return false
}; }
Change.compare = function (a, b) { Change.compare = function (a, b) {
if (!Change.isChange(a) || !Change.isChange(b)) if (!Change.isChange(a) || !Change.isChange(b)) { throw new TypeError('can only compare Changes') }
throw new TypeError('can only compare Changes');
if (a.operation < b.operation) if (a.operation < b.operation) { return -1 }
return -1; if (a.operation > b.operation) { return 1 }
if (a.operation > b.operation)
return 1;
return Attribute.compare(a.modification, b.modification); return Attribute.compare(a.modification, b.modification)
}; }
/** /**
* Apply a Change to properties of an object. * Apply a Change to properties of an object.
@ -132,86 +130,83 @@ Change.compare = function (a, b) {
* @param {Object} obj the object to apply it to. * @param {Object} obj the object to apply it to.
* @param {Boolean} scalar convert single-item arrays to scalars. Default: false * @param {Boolean} scalar convert single-item arrays to scalars. Default: false
*/ */
Change.apply = function apply(change, obj, scalar) { Change.apply = function apply (change, obj, scalar) {
assert.string(change.operation); assert.string(change.operation)
assert.string(change.modification.type); assert.string(change.modification.type)
assert.ok(Array.isArray(change.modification.vals)); assert.ok(Array.isArray(change.modification.vals))
assert.object(obj); assert.object(obj)
var type = change.modification.type; var type = change.modification.type
var vals = change.modification.vals; var vals = change.modification.vals
var data = obj[type]; var data = obj[type]
if (data !== undefined) { if (data !== undefined) {
if (!Array.isArray(data)) { if (!Array.isArray(data)) {
data = [data]; data = [data]
} }
} else { } else {
data = []; data = []
} }
switch (change.operation) { switch (change.operation) {
case 'replace': case 'replace':
if (vals.length === 0) { if (vals.length === 0) {
// replace empty is a delete // replace empty is a delete
delete obj[type]; delete obj[type]
return obj; return obj
} else { } else {
data = vals; data = vals
} }
break; break
case 'add': case 'add':
// add only new unique entries // add only new unique entries
var newValues = vals.filter(function (entry) { var newValues = vals.filter(function (entry) {
return (data.indexOf(entry) === -1); return (data.indexOf(entry) === -1)
}); })
data = data.concat(newValues); data = data.concat(newValues)
break; break
case 'delete': case 'delete':
data = data.filter(function (entry) { data = data.filter(function (entry) {
return (vals.indexOf(entry) === -1); return (vals.indexOf(entry) === -1)
}); })
if (data.length === 0) { if (data.length === 0) {
// Erase the attribute if empty // Erase the attribute if empty
delete obj[type]; delete obj[type]
return obj; return obj
} }
break; break
default: default:
break; break
} }
if (scalar && data.length === 1) { if (scalar && data.length === 1) {
// store single-value outputs as scalars, if requested // store single-value outputs as scalars, if requested
obj[type] = data[0]; obj[type] = data[0]
} else { } else {
obj[type] = data; obj[type] = data
} }
return obj; return obj
}; }
Change.prototype.parse = function (ber) { Change.prototype.parse = function (ber) {
assert.ok(ber); assert.ok(ber)
ber.readSequence(); ber.readSequence()
this._operation = ber.readEnumeration(); this._operation = ber.readEnumeration()
this._modification = new Attribute(); this._modification = new Attribute()
this._modification.parse(ber); this._modification.parse(ber)
return true;
};
return true
}
Change.prototype.toBer = function (ber) { Change.prototype.toBer = function (ber) {
assert.ok(ber); assert.ok(ber)
ber.startSequence(); ber.startSequence()
ber.writeEnumeration(this._operation); ber.writeEnumeration(this._operation)
ber = this._modification.toBer(ber); ber = this._modification.toBer(ber)
ber.endSequence(); ber.endSequence()
return ber; return ber
}; }
/// --- Exports
///--- Exports module.exports = Change
module.exports = Change;

File diff suppressed because it is too large Load Diff

7
lib/client/constants.js Normal file
View File

@ -0,0 +1,7 @@
'use strict'
module.exports = {
// https://tools.ietf.org/html/rfc4511#section-4.1.1
// Message identifiers are an integer between (0, maxint).
MAX_MSGID: Math.pow(2, 31) - 1
}

View File

@ -1,56 +1,23 @@
// Copyright 2012 Mark Cavage, Inc. All rights reserved. 'use strict'
var assert = require('assert'); const logger = require('../logger')
const Client = require('./client')
var Logger = require('bunyan');
var Client = require('./client');
///--- Globals
var DEF_LOG = new Logger({
name: 'ldapjs',
component: 'client',
stream: process.stderr,
serializers: Logger.stdSerializers
});
///--- Functions
function xor() {
var b = false;
for (var i = 0; i < arguments.length; i++) {
if (arguments[i] && !b) {
b = true;
} else if (arguments[i] && b) {
return false;
}
}
return b;
}
///--- Exports
module.exports = { module.exports = {
Client: Client, Client: Client,
createClient: function createClient(options) { createClient: function createClient (options) {
if (typeof (options) !== 'object') if (isObject(options) === false) throw TypeError('options (object) required')
throw new TypeError('options (object) required'); if (options.url && typeof options.url !== 'string') throw TypeError('options.url (string) required')
if (options.url && typeof (options.url) !== 'string') if (options.socketPath && typeof options.socketPath !== 'string') throw TypeError('options.socketPath must be a string')
throw new TypeError('options.url (string) required'); if ((options.url && options.socketPath) || !(options.url || options.socketPath)) throw TypeError('options.url ^ options.socketPath (String) required')
if (options.socketPath && typeof (options.socketPath) !== 'string') if (!options.log) options.log = logger
throw new TypeError('options.socketPath must be a string'); if (isObject(options.log) !== true) throw TypeError('options.log must be an object')
if (!xor(options.url, options.socketPath)) if (!options.log.child) options.log.child = function () { return options.log }
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); return new Client(options)
} }
}; }
function isObject (input) {
return Object.prototype.toString.apply(input) === '[object Object]'
}

View File

@ -0,0 +1,25 @@
'use strict'
const { MAX_MSGID } = require('../constants')
/**
* Compare a reference id with another id to determine "greater than or equal"
* between the two values according to a sliding window.
*
* @param {integer} ref
* @param {integer} comp
*
* @returns {boolean} `true` if the `comp` value is >= to the `ref` value
* within the computed window, otherwise `false`.
*/
module.exports = function geWindow (ref, comp) {
let max = ref + Math.floor(MAX_MSGID / 2)
const min = ref
if (max >= MAX_MSGID) {
// Handle roll-over
max = max - MAX_MSGID - 1
return ((comp <= max) || (comp >= min))
} else {
return ((comp <= max) && (comp >= min))
}
}

View File

@ -0,0 +1,23 @@
'use strict'
const { MAX_MSGID } = require('../constants')
/**
* Returns a function that generates message identifiers. According to RFC 4511
* the identifers should be `(0, MAX_MSGID)`. The returned function handles
* this and wraps around when the maximum has been reached.
*
* @param {integer} [start=0] Starting number in the identifier sequence.
*
* @returns {function} This function accepts no parameters and returns an
* increasing sequence identifier each invocation until it reaches the maximum
* identifier. At this point the sequence starts over.
*/
module.exports = function idGeneratorFactory (start = 0) {
let currentID = start
return function nextID () {
const nextID = currentID + 1
currentID = (nextID >= MAX_MSGID) ? 1 : nextID
return currentID
}
}

View File

@ -0,0 +1,151 @@
'use strict'
const idGeneratorFactory = require('./id-generator')
const purgeAbandoned = require('./purge-abandoned')
/**
* Returns a message tracker object that keeps track of which message
* identifiers correspond to which message handlers. Also handles keeping track
* of abandoned messages.
*
* @param {object} options
* @param {string} options.id An identifier for the tracker.
* @param {object} options.parser An object that will be used to parse messages.
*
* @returns {MessageTracker}
*/
module.exports = function messageTrackerFactory (options) {
if (Object.prototype.toString.call(options) !== '[object Object]') {
throw Error('options object is required')
}
if (!options.id || typeof options.id !== 'string') {
throw Error('options.id string is required')
}
if (!options.parser || Object.prototype.toString.call(options.parser) !== '[object Object]') {
throw Error('options.parser object is required')
}
let currentID = 0
const nextID = idGeneratorFactory()
const messages = new Map()
const abandoned = new Map()
/**
* @typedef {object} MessageTracker
* @property {string} id The identifier of the tracker as supplied via the options.
* @property {object} parser The parser object given by the the options.
*/
const tracker = {
id: options.id,
parser: options.parser
}
/**
* Count of messages awaiting response.
*
* @alias pending
* @memberof! MessageTracker#
*/
Object.defineProperty(tracker, 'pending', {
get () {
return messages.size
}
})
/**
* Move a specific message to the abanded track.
*
* @param {integer} msgID The identifier for the message to move.
*
* @memberof MessageTracker
* @method abandon
*/
tracker.abandon = function abandonMessage (msgID) {
if (messages.has(msgID) === false) return false
abandoned.set(msgID, {
age: currentID,
cb: messages.get(msgID)
})
return messages.delete(msgID)
}
/**
* Retrieves the message handler for a message. Removes abandoned messages
* that have been given time to be resolved.
*
* @param {integer} msgID The identifier for the message to get the handler for.
*
* @memberof MessageTracker
* @method fetch
*/
tracker.fetch = function fetchMessage (msgID) {
const messageCB = messages.get(msgID)
if (messageCB) {
purgeAbandoned(msgID, abandoned)
return messageCB
}
// We sent an abandon request but the server either wasn't able to process
// it or has not received it yet. Therefore, we received a response for the
// abandoned message. So we must return the abandoned message's callback
// to be processed normally.
const abandonedMsg = abandoned.get(msgID)
if (abandonedMsg) {
return abandonedMsg.cb
}
return null
}
/**
* Removes all message tracks, cleans up the abandoned track, and invokes
* a callback for each message purged.
*
* @param {function} cb A function with the signature `(msgID, handler)`.
*
* @memberof MessageTracker
* @method purge
*/
tracker.purge = function purgeMessages (cb) {
messages.forEach((val, key) => {
purgeAbandoned(key, abandoned)
tracker.remove(key)
cb(key, val)
})
}
/**
* Removes a message from all tracking.
*
* @param {integer} msgID The identifier for the message to remove from tracking.
*
* @memberof MessageTracker
* @method remove
*/
tracker.remove = function removeMessage (msgID) {
if (messages.delete(msgID) === false) {
abandoned.delete(msgID)
}
}
/**
* Add a message handler to be tracked.
*
* @param {object} message The message object to be tracked. This object will
* have a new property added to it: `messageID`.
* @param {function} callback The handler for the message.
*
* @memberof MessageTracker
* @method track
*/
tracker.track = function trackMessage (message, callback) {
currentID = nextID()
// This side effect is not ideal but the client doesn't attach the tracker
// to itself until after the `.connect` method has fired. If this can be
// refactored later, then we can possibly get rid of this side effect.
message.messageID = currentID
messages.set(currentID, callback)
}
return tracker
}

View File

@ -0,0 +1,34 @@
'use strict'
const { AbandonedError } = require('../../errors')
const geWindow = require('./ge-window')
/**
* Given a `msgID` and a set of `abandoned` messages, remove any abandoned
* messages that existed _prior_ to the specified `msgID`. For example, let's
* assume the server has sent 3 messages:
*
* 1. A search message.
* 2. An abandon message for the search message.
* 3. A new search message.
*
* When the response for message #1 comes in, if it does, it will be processed
* normally due to the specification. Message #2 will not receive a response, or
* if the server does send one since the spec sort of allows it, we won't do
* anything with it because we just discard that listener. Now the response
* for message #3 comes in. At this point, we will issue a purge of responses
* by passing in `msgID = 3`. This result is that we will remove the tracking
* for message #1.
*
* @param {integer} msgID An upper bound for the messages to be purged.
* @param {Map} abandoned A set of abandoned messages. Each message is an object
* `{ age: <id>, cb: <func> }` where `age` was the current message id when the
* abandon message was sent.
*/
module.exports = function purgeAbandoned (msgID, abandoned) {
abandoned.forEach((val, key) => {
if (geWindow(val.age, msgID) === false) return
val.cb(new AbandonedError('client request abandoned'))
abandoned.delete(key)
})
}

View File

@ -0,0 +1,36 @@
'use strict'
/**
* Adds requests to the queue. If a timeout has been added to the queue then
* this will freeze the queue with the newly added item, flush it, and then
* unfreeze it when the queue has been cleared.
*
* @param {object} message An LDAP message object.
* @param {object} expect An expectation object.
* @param {object} emitter An event emitter or `null`.
* @param {function} cb A callback to invoke when the request is finished.
*
* @returns {boolean} `true` if the requested was queued. `false` if the queue
* is not accepting any requests.
*/
module.exports = function enqueue (message, expect, emitter, cb) {
if (this._queue.length >= this.size || this._frozen) {
return false
}
this._queue.add({ message, expect, emitter, cb })
if (this.timeout === 0) return true
if (this._timer === null) return true
// A queue can have a specified time allotted for it to be cleared. If that
// time has been reached, reject new entries until the queue has been cleared.
this._timer = setTimeout(queueTimeout.bind(this), this.timeout)
return true
function queueTimeout () {
this.freeze()
this.purge()
}
}

View File

@ -0,0 +1,24 @@
'use strict'
/**
* Invokes all requests in the queue by passing them to the supplied callback
* function and then clears all items from the queue.
*
* @param {function} cb A function used to handle the requests.
*/
module.exports = function flush (cb) {
if (this._timer) {
clearTimeout(this._timer)
this._timer = null
}
// We must get a local copy of the queue and clear it before iterating it.
// The client will invoke this flush function _many_ times. If we try to
// iterate it without a local copy and clearing first then we will overflow
// the stack.
const requests = Array.from(this._queue.values())
this._queue.clear()
for (const req of requests) {
cb(req.message, req.expect, req.emitter, req.cb)
}
}

View File

@ -0,0 +1,39 @@
'use strict'
const enqueue = require('./enqueue')
const flush = require('./flush')
const purge = require('./purge')
/**
* Builds a request queue object and returns it.
*
* @param {object} [options]
* @param {integer} [options.size] Maximum size of the request queue. Must be
* a number greater than `0` if supplied. Default: `Infinity`.
* @param {integer} [options.timeout] Time in milliseconds a queue has to
* complete the requests it contains.
*
* @returns {object} A queue instance.
*/
module.exports = function requestQueueFactory (options) {
const opts = Object.assign({}, options)
const q = {
size: (opts.size > 0) ? opts.size : Infinity,
timeout: (opts.timeout > 0) ? opts.timeout : 0,
_queue: new Set(),
_timer: null,
_frozen: false
}
q.enqueue = enqueue.bind(q)
q.flush = flush.bind(q)
q.purge = purge.bind(q)
q.freeze = function freeze () {
this._frozen = true
}
q.thaw = function thaw () {
this._frozen = false
}
return q
}

View File

@ -0,0 +1,12 @@
'use strict'
const { TimeoutError } = require('../../errors')
/**
* Flushes the queue by rejecting all pending requests with a timeout error.
*/
module.exports = function purge () {
this.flush(function flushCB (a, b, c, cb) {
cb(new TimeoutError('request queue timeout'))
})
}

View File

@ -1,18 +1,16 @@
// Copyright 2014 Joyent, Inc. All rights reserved. 'use strict'
var EventEmitter = require('events').EventEmitter; var EventEmitter = require('events').EventEmitter
var util = require('util'); var util = require('util')
var assert = require('assert-plus'); var assert = require('assert-plus')
var dn = require('../dn'); // var dn = require('../dn')
var messages = require('../messages/index'); // var messages = require('../messages/index')
var Protocol = require('../protocol'); // var Protocol = require('../protocol')
var PagedControl = require('../controls/paged_results_control.js'); var PagedControl = require('../controls/paged_results_control.js')
///--- API
/// --- API
/** /**
* Handler object for paged search operations. * Handler object for paged search operations.
@ -33,140 +31,140 @@ var PagedControl = require('../controls/paged_results_control.js');
* able to emit 'end'. * able to emit 'end'.
* 3. search - Emitted as an internal event to trigger another client search. * 3. search - Emitted as an internal event to trigger another client search.
*/ */
function SearchPager(opts) { function SearchPager (opts) {
assert.object(opts); assert.object(opts)
assert.func(opts.callback); assert.func(opts.callback)
assert.number(opts.pageSize); assert.number(opts.pageSize)
EventEmitter.call(this, {}); EventEmitter.call(this, {})
this.callback = opts.callback; this.callback = opts.callback
this.controls = opts.controls; this.controls = opts.controls
this.pageSize = opts.pageSize; this.pageSize = opts.pageSize
this.pagePause = opts.pagePause; this.pagePause = opts.pagePause
this.controls.forEach(function (control) { this.controls.forEach(function (control) {
if (control.type === PagedControl.OID) { if (control.type === PagedControl.OID) {
// The point of using SearchPager is not having to do this. // The point of using SearchPager is not having to do this.
// Toss an error if the pagedResultsControl is present // Toss an error if the pagedResultsControl is present
throw new Error('redundant pagedResultControl'); throw new Error('redundant pagedResultControl')
} }
}); })
this.finished = false; this.finished = false
this.started = false; this.started = false
var emitter = new EventEmitter(); var emitter = new EventEmitter()
emitter.on('searchEntry', this.emit.bind(this, 'searchEntry')); emitter.on('searchEntry', this.emit.bind(this, 'searchEntry'))
emitter.on('end', this._onEnd.bind(this)); emitter.on('end', this._onEnd.bind(this))
emitter.on('error', this._onError.bind(this)); emitter.on('error', this._onError.bind(this))
this.childEmitter = emitter; this.childEmitter = emitter
} }
util.inherits(SearchPager, EventEmitter); util.inherits(SearchPager, EventEmitter)
module.exports = SearchPager; module.exports = SearchPager
/** /**
* Start the paged search. * Start the paged search.
*/ */
SearchPager.prototype.begin = function begin() { SearchPager.prototype.begin = function begin () {
// Starting first page // Starting first page
this._nextPage(null); this._nextPage(null)
}; }
SearchPager.prototype._onEnd = function _onEnd(res) { SearchPager.prototype._onEnd = function _onEnd (res) {
var self = this; var self = this
var cookie = null; var cookie = null
res.controls.forEach(function (control) { res.controls.forEach(function (control) {
if (control.type === PagedControl.OID) { if (control.type === PagedControl.OID) {
cookie = control.value.cookie; cookie = control.value.cookie
} }
}); })
// Pass a noop callback by default for page events // Pass a noop callback by default for page events
var nullCb = function () { }; var nullCb = function () { }
if (cookie === null) { if (cookie === null) {
// paged search not supported // paged search not supported
this.finished = true; this.finished = true
this.emit('page', res, nullCb); this.emit('page', res, nullCb)
var err = new Error('missing paged control'); var err = new Error('missing paged control')
err.name = 'PagedError'; err.name = 'PagedError'
if (this.listeners('pageError').length > 0) { if (this.listeners('pageError').length > 0) {
this.emit('pageError', err); this.emit('pageError', err)
// If the consumer as subscribed to pageError, SearchPager is absolved // If the consumer as subscribed to pageError, SearchPager is absolved
// from deliverying the fault via the 'error' event. Emitting an 'end' // from deliverying the fault via the 'error' event. Emitting an 'end'
// event after 'error' breaks the contract that the standard client // event after 'error' breaks the contract that the standard client
// provides, so it's only a possibility if 'pageError' is used instead. // provides, so it's only a possibility if 'pageError' is used instead.
this.emit('end', res); this.emit('end', res)
} else { } else {
this.emit('error', err); this.emit('error', err)
// No end event possible per explaination above. // No end event possible per explaination above.
} }
return; return
} }
if (cookie.length === 0) { if (cookie.length === 0) {
// end of paged results // end of paged results
this.finished = true; this.finished = true
this.emit('page', nullCb); this.emit('page', nullCb)
this.emit('end', res); this.emit('end', res)
} else { } else {
if (this.pagePause) { if (this.pagePause) {
// Wait to fetch next page until callback is invoked // Wait to fetch next page until callback is invoked
// Halt page fetching if called with error // Halt page fetching if called with error
this.emit('page', res, function (err) { this.emit('page', res, function (err) {
if (!err) { if (!err) {
self._nextPage(cookie); self._nextPage(cookie)
} else { } else {
// the paged search has been canceled so emit an end // the paged search has been canceled so emit an end
self.emit('end', res); self.emit('end', res)
} }
}); })
} else { } else {
this.emit('page', res, nullCb); this.emit('page', res, nullCb)
this._nextPage(cookie); this._nextPage(cookie)
} }
} }
}; }
SearchPager.prototype._onError = function _onError(err) { SearchPager.prototype._onError = function _onError (err) {
this.finished = true; this.finished = true
this.emit('error', err); this.emit('error', err)
}; }
/** /**
* Initiate a search for the next page using the returned cookie value. * Initiate a search for the next page using the returned cookie value.
*/ */
SearchPager.prototype._nextPage = function _nextPage(cookie) { SearchPager.prototype._nextPage = function _nextPage (cookie) {
var controls = this.controls.slice(0); var controls = this.controls.slice(0)
controls.push(new PagedControl({ controls.push(new PagedControl({
value: { value: {
size: this.pageSize, size: this.pageSize,
cookie: cookie cookie: cookie
} }
})); }))
this.emit('search', controls, this.childEmitter, this.emit('search', controls, this.childEmitter,
this._sendCallback.bind(this)); this._sendCallback.bind(this))
}; }
/** /**
* Callback provided to the client API for successful transmission. * Callback provided to the client API for successful transmission.
*/ */
SearchPager.prototype._sendCallback = function _sendCallback(err, res) { SearchPager.prototype._sendCallback = function _sendCallback (err, res) {
if (err) { if (err) {
this.finished = true; this.finished = true
if (!this.started) { if (!this.started) {
// EmitSend error during the first page, bail via callback // EmitSend error during the first page, bail via callback
this.callback(err, null); this.callback(err, null)
} else { } else {
this.emit('error', err); this.emit('error', err)
} }
} else { } else {
// search successfully send // search successfully send
if (!this.started) { if (!this.started) {
this.started = true; this.started = true
// send self as emitter as the client would // send self as emitter as the client would
this.callback(null, this); this.callback(null, this)
} }
} }
}; }

View File

@ -1,67 +1,61 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved. // Copyright 2011 Mark Cavage, Inc. All rights reserved.
var assert = require('assert-plus'); var assert = require('assert-plus')
var util = require('util');
var asn1 = require('asn1'); // var asn1 = require('asn1')
var Protocol = require('../protocol'); // var Protocol = require('../protocol')
/// --- Globals
///--- Globals // var Ber = asn1.Ber
var Ber = asn1.Ber; /// --- API
function Control (options) {
///--- API assert.optionalObject(options)
options = options || {}
function Control(options) { assert.optionalString(options.type)
assert.optionalObject(options); assert.optionalBool(options.criticality)
options = options || {};
assert.optionalString(options.type);
assert.optionalBool(options.criticality);
if (options.value) { if (options.value) {
assert.buffer(options.value); assert.buffer(options.value)
} }
this.type = options.type || ''; this.type = options.type || ''
this.criticality = options.critical || options.criticality || false; this.criticality = options.critical || options.criticality || false
this.value = options.value || null; this.value = options.value || null
} }
Object.defineProperties(Control.prototype, { Object.defineProperties(Control.prototype, {
json: { json: {
get: function getJson() { get: function getJson () {
var obj = { var obj = {
controlType: this.type, controlType: this.type,
criticality: this.criticality, criticality: this.criticality,
controlValue: this.value controlValue: this.value
}; }
return (typeof (this._json) === 'function' ? this._json(obj) : obj); return (typeof (this._json) === 'function' ? this._json(obj) : obj)
} }
} }
}); })
Control.prototype.toBer = function toBer(ber) { Control.prototype.toBer = function toBer (ber) {
assert.ok(ber); assert.ok(ber)
ber.startSequence(); ber.startSequence()
ber.writeString(this.type || ''); ber.writeString(this.type || '')
ber.writeBoolean(this.criticality); ber.writeBoolean(this.criticality)
if (typeof (this._toBer) === 'function') { if (typeof (this._toBer) === 'function') {
this._toBer(ber); this._toBer(ber)
} else { } else {
if (this.value) if (this.value) { ber.writeString(this.value) }
ber.writeString(this.value);
} }
ber.endSequence(); ber.endSequence()
return; }
};
Control.prototype.toString = function toString() { Control.prototype.toString = function toString () {
return this.json; return this.json
}; }
/// --- Exports
///--- Exports module.exports = Control
module.exports = Control;

View File

@ -1,89 +1,83 @@
var assert = require('assert-plus'); var assert = require('assert-plus')
var util = require('util'); var util = require('util')
var asn1 = require('asn1'); var asn1 = require('asn1')
var Control = require('./control'); var Control = require('./control')
/// --- Globals
///--- Globals var BerReader = asn1.BerReader
var BerWriter = asn1.BerWriter
var BerReader = asn1.BerReader; /// --- API
var BerWriter = asn1.BerWriter;
function EntryChangeNotificationControl (options) {
///--- API assert.optionalObject(options)
options = options || {}
function EntryChangeNotificationControl(options) { options.type = EntryChangeNotificationControl.OID
assert.optionalObject(options);
options = options || {};
options.type = EntryChangeNotificationControl.OID;
if (options.value) { if (options.value) {
if (Buffer.isBuffer(options.value)) { if (Buffer.isBuffer(options.value)) {
this.parse(options.value); this.parse(options.value)
} else if (typeof (options.value) === 'object') { } else if (typeof (options.value) === 'object') {
this._value = options.value; this._value = options.value
} else { } else {
throw new TypeError('options.value must be a Buffer or Object'); throw new TypeError('options.value must be a Buffer or Object')
} }
options.value = null; options.value = null
} }
Control.call(this, options); Control.call(this, options)
} }
util.inherits(EntryChangeNotificationControl, Control); util.inherits(EntryChangeNotificationControl, Control)
Object.defineProperties(EntryChangeNotificationControl.prototype, { Object.defineProperties(EntryChangeNotificationControl.prototype, {
value: { value: {
get: function () { return this._value || {}; }, get: function () { return this._value || {} },
configurable: false configurable: false
} }
}); })
EntryChangeNotificationControl.prototype.parse = function parse(buffer) { EntryChangeNotificationControl.prototype.parse = function parse (buffer) {
assert.ok(buffer); assert.ok(buffer)
var ber = new BerReader(buffer); var ber = new BerReader(buffer)
if (ber.readSequence()) { if (ber.readSequence()) {
this._value = { this._value = {
changeType: ber.readInt() changeType: ber.readInt()
}; }
// if the operation was moddn, then parse the optional previousDN attr // if the operation was moddn, then parse the optional previousDN attr
if (this._value.changeType === 8) if (this._value.changeType === 8) { this._value.previousDN = ber.readString() }
this._value.previousDN = ber.readString();
this._value.changeNumber = ber.readInt(); this._value.changeNumber = ber.readInt()
return true; return true
} }
return false; return false
}; }
EntryChangeNotificationControl.prototype._toBer = function (ber) { EntryChangeNotificationControl.prototype._toBer = function (ber) {
assert.ok(ber); assert.ok(ber)
if (!this._value) if (!this._value) { return }
return;
var writer = new BerWriter(); var writer = new BerWriter()
writer.startSequence(); writer.startSequence()
writer.writeInt(this.value.changeType); writer.writeInt(this.value.changeType)
if (this.value.previousDN) if (this.value.previousDN) { writer.writeString(this.value.previousDN) }
writer.writeString(this.value.previousDN);
writer.writeInt(parseInt(this.value.changeNumber, 10)); writer.writeInt(parseInt(this.value.changeNumber, 10))
writer.endSequence(); writer.endSequence()
ber.writeBuffer(writer.buffer, 0x04); ber.writeBuffer(writer.buffer, 0x04)
}; }
EntryChangeNotificationControl.prototype._json = function (obj) { EntryChangeNotificationControl.prototype._json = function (obj) {
obj.controlValue = this.value; obj.controlValue = this.value
return obj; return obj
}; }
EntryChangeNotificationControl.OID = '2.16.840.1.113730.3.4.7'; EntryChangeNotificationControl.OID = '2.16.840.1.113730.3.4.7'
/// --- Exports
///--- Exports module.exports = EntryChangeNotificationControl
module.exports = EntryChangeNotificationControl;

View File

@ -1,73 +1,78 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved. // Copyright 2011 Mark Cavage, Inc. All rights reserved.
var assert = require('assert'); var assert = require('assert')
var Ber = require('asn1').Ber; var Ber = require('asn1').Ber
var Control = require('./control'); var Control = require('./control')
var EntryChangeNotificationControl = var EntryChangeNotificationControl =
require('./entry_change_notification_control'); require('./entry_change_notification_control')
var PersistentSearchControl = require('./persistent_search_control'); var PersistentSearchControl = require('./persistent_search_control')
var PagedResultsControl = require('./paged_results_control'); var PagedResultsControl = require('./paged_results_control')
var ServerSideSortingRequestControl = var ServerSideSortingRequestControl =
require('./server_side_sorting_request_control.js'); require('./server_side_sorting_request_control.js')
var ServerSideSortingResponseControl = var ServerSideSortingResponseControl =
require('./server_side_sorting_response_control.js'); require('./server_side_sorting_response_control.js')
var VirtualListViewRequestControl =
require('./virtual_list_view_request_control.js')
var VirtualListViewResponseControl =
require('./virtual_list_view_response_control.js')
/// --- API
///--- API
module.exports = { module.exports = {
getControl: function getControl(ber) { getControl: function getControl (ber) {
assert.ok(ber); assert.ok(ber)
if (ber.readSequence() === null) if (ber.readSequence() === null) { return null }
return null;
var type; var type
var opts = { var opts = {
criticality: false, criticality: false,
value: null value: null
}; }
if (ber.length) { if (ber.length) {
var end = ber.offset + ber.length; var end = ber.offset + ber.length
type = ber.readString(); type = ber.readString()
if (ber.offset < end) { if (ber.offset < end) {
if (ber.peek() === Ber.Boolean) if (ber.peek() === Ber.Boolean) { opts.criticality = ber.readBoolean() }
opts.criticality = ber.readBoolean();
} }
if (ber.offset < end) if (ber.offset < end) { opts.value = ber.readString(Ber.OctetString, true) }
opts.value = ber.readString(Ber.OctetString, true);
} }
var control; var control
switch (type) { switch (type) {
case PersistentSearchControl.OID: case PersistentSearchControl.OID:
control = new PersistentSearchControl(opts); control = new PersistentSearchControl(opts)
break; break
case EntryChangeNotificationControl.OID: case EntryChangeNotificationControl.OID:
control = new EntryChangeNotificationControl(opts); control = new EntryChangeNotificationControl(opts)
break; break
case PagedResultsControl.OID: case PagedResultsControl.OID:
control = new PagedResultsControl(opts); control = new PagedResultsControl(opts)
break; break
case ServerSideSortingRequestControl.OID: case ServerSideSortingRequestControl.OID:
control = new ServerSideSortingRequestControl(opts); control = new ServerSideSortingRequestControl(opts)
break; break
case ServerSideSortingResponseControl.OID: case ServerSideSortingResponseControl.OID:
control = new ServerSideSortingResponseControl(opts); control = new ServerSideSortingResponseControl(opts)
break; break
default: case VirtualListViewRequestControl.OID:
opts.type = type; control = new VirtualListViewRequestControl(opts)
control = new Control(opts); break
break; case VirtualListViewResponseControl.OID:
control = new VirtualListViewResponseControl(opts)
break
default:
opts.type = type
control = new Control(opts)
break
} }
return control; return control
}, },
Control: Control, Control: Control,
@ -75,5 +80,7 @@ module.exports = {
PagedResultsControl: PagedResultsControl, PagedResultsControl: PagedResultsControl,
PersistentSearchControl: PersistentSearchControl, PersistentSearchControl: PersistentSearchControl,
ServerSideSortingRequestControl: ServerSideSortingRequestControl, ServerSideSortingRequestControl: ServerSideSortingRequestControl,
ServerSideSortingResponseControl: ServerSideSortingResponseControl ServerSideSortingResponseControl: ServerSideSortingResponseControl,
}; VirtualListViewRequestControl: VirtualListViewRequestControl,
VirtualListViewResponseControl: VirtualListViewResponseControl
}

View File

@ -1,87 +1,82 @@
var assert = require('assert-plus'); var assert = require('assert-plus')
var util = require('util'); var util = require('util')
var asn1 = require('asn1'); var asn1 = require('asn1')
var Control = require('./control'); var Control = require('./control')
/// --- Globals
///--- Globals var BerReader = asn1.BerReader
var BerWriter = asn1.BerWriter
var BerReader = asn1.BerReader; /// --- API
var BerWriter = asn1.BerWriter;
function PagedResultsControl (options) {
///--- API assert.optionalObject(options)
options = options || {}
function PagedResultsControl(options) { options.type = PagedResultsControl.OID
assert.optionalObject(options);
options = options || {};
options.type = PagedResultsControl.OID;
if (options.value) { if (options.value) {
if (Buffer.isBuffer(options.value)) { if (Buffer.isBuffer(options.value)) {
this.parse(options.value); this.parse(options.value)
} else if (typeof (options.value) === 'object') { } else if (typeof (options.value) === 'object') {
this._value = options.value; this._value = options.value
} else { } else {
throw new TypeError('options.value must be a Buffer or Object'); throw new TypeError('options.value must be a Buffer or Object')
} }
options.value = null; options.value = null
} }
Control.call(this, options); Control.call(this, options)
} }
util.inherits(PagedResultsControl, Control); util.inherits(PagedResultsControl, Control)
Object.defineProperties(PagedResultsControl.prototype, { Object.defineProperties(PagedResultsControl.prototype, {
value: { value: {
get: function () { return this._value || {}; }, get: function () { return this._value || {} },
configurable: false configurable: false
} }
}); })
PagedResultsControl.prototype.parse = function parse(buffer) { PagedResultsControl.prototype.parse = function parse (buffer) {
assert.ok(buffer); assert.ok(buffer)
var ber = new BerReader(buffer); var ber = new BerReader(buffer)
if (ber.readSequence()) { if (ber.readSequence()) {
this._value = {}; this._value = {}
this._value.size = ber.readInt(); this._value.size = ber.readInt()
this._value.cookie = ber.readString(asn1.Ber.OctetString, true); this._value.cookie = ber.readString(asn1.Ber.OctetString, true)
//readString returns '' instead of a zero-length buffer // readString returns '' instead of a zero-length buffer
if (!this._value.cookie) if (!this._value.cookie) { this._value.cookie = Buffer.alloc(0) }
this._value.cookie = new Buffer(0);
return true; return true
} }
return false; return false
}; }
PagedResultsControl.prototype._toBer = function (ber) { PagedResultsControl.prototype._toBer = function (ber) {
assert.ok(ber); assert.ok(ber)
if (!this._value) if (!this._value) { return }
return;
var writer = new BerWriter(); var writer = new BerWriter()
writer.startSequence(); writer.startSequence()
writer.writeInt(this.value.size); writer.writeInt(this.value.size)
if (this.value.cookie && this.value.cookie.length > 0) { if (this.value.cookie && this.value.cookie.length > 0) {
writer.writeBuffer(this.value.cookie, asn1.Ber.OctetString); writer.writeBuffer(this.value.cookie, asn1.Ber.OctetString)
} else { } else {
writer.writeString(''); //writeBuffer rejects zero-length buffers writer.writeString('') // writeBuffer rejects zero-length buffers
} }
writer.endSequence(); writer.endSequence()
ber.writeBuffer(writer.buffer, 0x04); ber.writeBuffer(writer.buffer, 0x04)
}; }
PagedResultsControl.prototype._json = function (obj) { PagedResultsControl.prototype._json = function (obj) {
obj.controlValue = this.value; obj.controlValue = this.value
return obj; return obj
}; }
PagedResultsControl.OID = '1.2.840.113556.1.4.319'; PagedResultsControl.OID = '1.2.840.113556.1.4.319'
/// --- Exports
///--- Exports module.exports = PagedResultsControl
module.exports = PagedResultsControl;

View File

@ -1,85 +1,82 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved. // Copyright 2011 Mark Cavage, Inc. All rights reserved.
var assert = require('assert-plus'); var assert = require('assert-plus')
var util = require('util'); var util = require('util')
var asn1 = require('asn1'); var asn1 = require('asn1')
var Control = require('./control'); var Control = require('./control')
/// --- Globals
///--- Globals var BerReader = asn1.BerReader
var BerWriter = asn1.BerWriter
var BerReader = asn1.BerReader; /// --- API
var BerWriter = asn1.BerWriter;
function PersistentSearchControl (options) {
///--- API assert.optionalObject(options)
options = options || {}
function PersistentSearchControl(options) { options.type = PersistentSearchControl.OID
assert.optionalObject(options);
options = options || {};
options.type = PersistentSearchControl.OID;
if (options.value) { if (options.value) {
if (Buffer.isBuffer(options.value)) { if (Buffer.isBuffer(options.value)) {
this.parse(options.value); this.parse(options.value)
} else if (typeof (options.value) === 'object') { } else if (typeof (options.value) === 'object') {
this._value = options.value; this._value = options.value
} else { } else {
throw new TypeError('options.value must be a Buffer or Object'); throw new TypeError('options.value must be a Buffer or Object')
} }
options.value = null; options.value = null
} }
Control.call(this, options); Control.call(this, options)
} }
util.inherits(PersistentSearchControl, Control); util.inherits(PersistentSearchControl, Control)
Object.defineProperties(PersistentSearchControl.prototype, { Object.defineProperties(PersistentSearchControl.prototype, {
value: { value: {
get: function () { return this._value || {}; }, get: function () { return this._value || {} },
configurable: false configurable: false
} }
}); })
PersistentSearchControl.prototype.parse = function parse(buffer) { PersistentSearchControl.prototype.parse = function parse (buffer) {
assert.ok(buffer); assert.ok(buffer)
var ber = new BerReader(buffer); var ber = new BerReader(buffer)
if (ber.readSequence()) { if (ber.readSequence()) {
this._value = { this._value = {
changeTypes: ber.readInt(), changeTypes: ber.readInt(),
changesOnly: ber.readBoolean(), changesOnly: ber.readBoolean(),
returnECs: ber.readBoolean() returnECs: ber.readBoolean()
}; }
return true; return true
} }
return false; return false
}; }
PersistentSearchControl.prototype._toBer = function (ber) { PersistentSearchControl.prototype._toBer = function (ber) {
assert.ok(ber); assert.ok(ber)
if (!this._value) if (!this._value) { return }
return;
var writer = new BerWriter(); var writer = new BerWriter()
writer.startSequence(); writer.startSequence()
writer.writeInt(this.value.changeTypes); writer.writeInt(this.value.changeTypes)
writer.writeBoolean(this.value.changesOnly); writer.writeBoolean(this.value.changesOnly)
writer.writeBoolean(this.value.returnECs); writer.writeBoolean(this.value.returnECs)
writer.endSequence(); writer.endSequence()
ber.writeBuffer(writer.buffer, 0x04); ber.writeBuffer(writer.buffer, 0x04)
}; }
PersistentSearchControl.prototype._json = function (obj) { PersistentSearchControl.prototype._json = function (obj) {
obj.controlValue = this.value; obj.controlValue = this.value
return obj; return obj
}; }
PersistentSearchControl.OID = '2.16.840.1.113730.3.4.3'; PersistentSearchControl.OID = '2.16.840.1.113730.3.4.3'
///--- Exports /// --- Exports
module.exports = PersistentSearchControl; module.exports = PersistentSearchControl

View File

@ -1,112 +1,108 @@
var assert = require('assert-plus'); var assert = require('assert-plus')
var util = require('util'); var util = require('util')
var asn1 = require('asn1'); var asn1 = require('asn1')
var Control = require('./control'); var Control = require('./control')
/// --- Globals
///--- Globals var BerReader = asn1.BerReader
var BerWriter = asn1.BerWriter
var BerReader = asn1.BerReader; /// --- API
var BerWriter = asn1.BerWriter;
function ServerSideSortingRequestControl (options) {
///--- API assert.optionalObject(options)
options = options || {}
function ServerSideSortingRequestControl(options) { options.type = ServerSideSortingRequestControl.OID
assert.optionalObject(options);
options = options || {};
options.type = ServerSideSortingRequestControl.OID;
if (options.value) { if (options.value) {
if (Buffer.isBuffer(options.value)) { if (Buffer.isBuffer(options.value)) {
this.parse(options.value); this.parse(options.value)
} else if (Array.isArray(options.value)) { } else if (Array.isArray(options.value)) {
assert.arrayOfObject(options.value, 'options.value must be Objects'); assert.arrayOfObject(options.value, 'options.value must be Objects')
for (var i = 0; i < options.value.length; i++) { for (var i = 0; i < options.value.length; i++) {
if (!options.value[i].hasOwnProperty('attributeType')) { if (Object.prototype.hasOwnProperty.call(options.value[i], 'attributeType') === false) {
throw new Error('Missing required key: attributeType'); throw new Error('Missing required key: attributeType')
} }
} }
this._value = options.value; this._value = options.value
} else if (typeof (options.value) === 'object') { } else if (typeof (options.value) === 'object') {
if (!options.value.hasOwnProperty('attributeType')) { if (Object.prototype.hasOwnProperty.call(options.value, 'attributeType') === false) {
throw new Error('Missing required key: attributeType'); throw new Error('Missing required key: attributeType')
} }
this._value = [options.value]; this._value = [options.value]
} else { } else {
throw new TypeError('options.value must be a Buffer, Array or Object'); throw new TypeError('options.value must be a Buffer, Array or Object')
} }
options.value = null; options.value = null
} }
Control.call(this, options); Control.call(this, options)
} }
util.inherits(ServerSideSortingRequestControl, Control); util.inherits(ServerSideSortingRequestControl, Control)
Object.defineProperties(ServerSideSortingRequestControl.prototype, { Object.defineProperties(ServerSideSortingRequestControl.prototype, {
value: { value: {
get: function () { return this._value || []; }, get: function () { return this._value || [] },
configurable: false configurable: false
} }
}); })
ServerSideSortingRequestControl.prototype.parse = function parse(buffer) { ServerSideSortingRequestControl.prototype.parse = function parse (buffer) {
assert.ok(buffer); assert.ok(buffer)
var ber = new BerReader(buffer); var ber = new BerReader(buffer)
var item; var item
if (ber.readSequence(0x30)) { if (ber.readSequence(0x30)) {
this._value = []; this._value = []
while (ber.readSequence(0x30)) { while (ber.readSequence(0x30)) {
item = {}; item = {}
item.attributeType = ber.readString(asn1.Ber.OctetString); item.attributeType = ber.readString(asn1.Ber.OctetString)
if (ber.peek() == 0x80) { if (ber.peek() === 0x80) {
item.orderingRule = ber.readString(0x80); item.orderingRule = ber.readString(0x80)
} }
if (ber.peek() == 0x81) { if (ber.peek() === 0x81) {
item.reverseOrder = (ber._readTag(0x81) === 0 ? false : true); item.reverseOrder = (ber._readTag(0x81) !== 0)
} }
this._value.push(item); this._value.push(item)
} }
return true; return true
} }
return false; return false
}; }
ServerSideSortingRequestControl.prototype._toBer = function (ber) { ServerSideSortingRequestControl.prototype._toBer = function (ber) {
assert.ok(ber); assert.ok(ber)
if (!this._value || this.value.length === 0) if (!this._value || this.value.length === 0) { return }
return;
var writer = new BerWriter(); var writer = new BerWriter()
writer.startSequence(0x30); writer.startSequence(0x30)
for (var i = 0; i < this.value.length; i++) { for (var i = 0; i < this.value.length; i++) {
var item = this.value[i]; var item = this.value[i]
writer.startSequence(0x30); writer.startSequence(0x30)
if (item.attributeType) { if (item.attributeType) {
writer.writeString(item.attributeType, asn1.Ber.OctetString); writer.writeString(item.attributeType, asn1.Ber.OctetString)
} }
if (item.orderingRule) { if (item.orderingRule) {
writer.writeString(item.orderingRule, 0x80); writer.writeString(item.orderingRule, 0x80)
} }
if (item.reverseOrder) { if (item.reverseOrder) {
writer.writeBoolean(item.reverseOrder, 0x81); writer.writeBoolean(item.reverseOrder, 0x81)
} }
writer.endSequence(); writer.endSequence()
} }
writer.endSequence(); writer.endSequence()
ber.writeBuffer(writer.buffer, 0x04); ber.writeBuffer(writer.buffer, 0x04)
}; }
ServerSideSortingRequestControl.prototype._json = function (obj) { ServerSideSortingRequestControl.prototype._json = function (obj) {
obj.controlValue = this.value; obj.controlValue = this.value
return obj; return obj
}; }
ServerSideSortingRequestControl.OID = '1.2.840.113556.1.4.473'; ServerSideSortingRequestControl.OID = '1.2.840.113556.1.4.473'
/// ---Exports
///---Exports module.exports = ServerSideSortingRequestControl
module.exports = ServerSideSortingRequestControl;

View File

@ -1,16 +1,15 @@
var assert = require('assert-plus'); var assert = require('assert-plus')
var util = require('util'); var util = require('util')
var asn1 = require('asn1'); var asn1 = require('asn1')
var Control = require('./control'); var Control = require('./control')
var CODES = require('../errors/codes'); var CODES = require('../errors/codes')
/// --- Globals
///--- Globals var BerReader = asn1.BerReader
var BerWriter = asn1.BerWriter
var BerReader = asn1.BerReader;
var BerWriter = asn1.BerWriter;
var VALID_CODES = [ var VALID_CODES = [
CODES.LDAP_SUCCESS, CODES.LDAP_SUCCESS,
@ -24,80 +23,78 @@ var VALID_CODES = [
CODES.LDAP_BUSY, CODES.LDAP_BUSY,
CODES.LDAP_UNWILLING_TO_PERFORM, CODES.LDAP_UNWILLING_TO_PERFORM,
CODES.LDAP_OTHER CODES.LDAP_OTHER
]; ]
function ServerSideSortingResponseControl(options) { function ServerSideSortingResponseControl (options) {
assert.optionalObject(options); assert.optionalObject(options)
options = options || {}; options = options || {}
options.type = ServerSideSortingResponseControl.OID; options.type = ServerSideSortingResponseControl.OID
options.criticality = false; options.criticality = false
if (options.value) { if (options.value) {
if (Buffer.isBuffer(options.value)) { if (Buffer.isBuffer(options.value)) {
this.parse(options.value); this.parse(options.value)
} else if (typeof (options.value) === 'object') { } else if (typeof (options.value) === 'object') {
if (VALID_CODES.indexOf(options.value.result) === -1) { if (VALID_CODES.indexOf(options.value.result) === -1) {
throw new Error('Invalid result code'); throw new Error('Invalid result code')
} }
if (options.value.failedAttribute && if (options.value.failedAttribute &&
typeof (options.value.failedAttribute) !== 'string') { typeof (options.value.failedAttribute) !== 'string') {
throw new Error('failedAttribute must be String'); throw new Error('failedAttribute must be String')
} }
this._value = options.value; this._value = options.value
} else { } else {
throw new TypeError('options.value must be a Buffer or Object'); throw new TypeError('options.value must be a Buffer or Object')
} }
options.value = null; options.value = null
} }
Control.call(this, options); Control.call(this, options)
} }
util.inherits(ServerSideSortingResponseControl, Control); util.inherits(ServerSideSortingResponseControl, Control)
Object.defineProperties(ServerSideSortingResponseControl.prototype, { Object.defineProperties(ServerSideSortingResponseControl.prototype, {
value: { value: {
get: function () { return this._value || {}; }, get: function () { return this._value || {} },
configurable: false configurable: false
} }
}); })
ServerSideSortingResponseControl.prototype.parse = function parse(buffer) { ServerSideSortingResponseControl.prototype.parse = function parse (buffer) {
assert.ok(buffer); assert.ok(buffer)
var ber = new BerReader(buffer); var ber = new BerReader(buffer)
if (ber.readSequence(0x30)) { if (ber.readSequence(0x30)) {
this._value = {}; this._value = {}
this._value.result = ber.readEnumeration(); this._value.result = ber.readEnumeration()
if (ber.peek() == 0x80) { if (ber.peek() === 0x80) {
this._value.failedAttribute = ber.readString(0x80); this._value.failedAttribute = ber.readString(0x80)
} }
return true; return true
} }
return false; return false
}; }
ServerSideSortingResponseControl.prototype._toBer = function (ber) { ServerSideSortingResponseControl.prototype._toBer = function (ber) {
assert.ok(ber); assert.ok(ber)
if (!this._value || this.value.length === 0) if (!this._value || this.value.length === 0) { return }
return;
var writer = new BerWriter(); var writer = new BerWriter()
writer.startSequence(0x30); writer.startSequence(0x30)
writer.writeEnumeration(this.value.result); writer.writeEnumeration(this.value.result)
if (this.value.result !== CODES.LDAP_SUCCESS && this.value.failedAttribute) { if (this.value.result !== CODES.LDAP_SUCCESS && this.value.failedAttribute) {
writer.writeString(this.value.failedAttribute, 0x80); writer.writeString(this.value.failedAttribute, 0x80)
} }
writer.endSequence(); writer.endSequence()
ber.writeBuffer(writer.buffer, 0x04); ber.writeBuffer(writer.buffer, 0x04)
}; }
ServerSideSortingResponseControl.prototype._json = function (obj) { ServerSideSortingResponseControl.prototype._json = function (obj) {
obj.controlValue = this.value; obj.controlValue = this.value
return obj; return obj
}; }
ServerSideSortingResponseControl.OID = '1.2.840.113556.1.4.474'; ServerSideSortingResponseControl.OID = '1.2.840.113556.1.4.474'
/// --- Exports
///--- Exports module.exports = ServerSideSortingResponseControl
module.exports = ServerSideSortingResponseControl;

View File

@ -0,0 +1,94 @@
var assert = require('assert-plus')
var util = require('util')
var asn1 = require('asn1')
var Control = require('./control')
/// --- Globals
var BerReader = asn1.BerReader
var BerWriter = asn1.BerWriter
/// --- API
function VirtualListViewControl (options) {
assert.optionalObject(options)
options = options || {}
options.type = VirtualListViewControl.OID
if (options.value) {
if (Buffer.isBuffer(options.value)) {
this.parse(options.value)
} else if (typeof (options.value) === 'object') {
if (Object.prototype.hasOwnProperty.call(options.value, 'beforeCount') === false) {
throw new Error('Missing required key: beforeCount')
}
if (Object.prototype.hasOwnProperty.call(options.value, 'afterCount') === false) {
throw new Error('Missing required key: afterCount')
}
this._value = options.value
} else {
throw new TypeError('options.value must be a Buffer or Object')
}
options.value = null
}
Control.call(this, options)
}
util.inherits(VirtualListViewControl, Control)
Object.defineProperties(VirtualListViewControl.prototype, {
value: {
get: function () { return this._value || [] },
configurable: false
}
})
VirtualListViewControl.prototype.parse = function parse (buffer) {
assert.ok(buffer)
var ber = new BerReader(buffer)
if (ber.readSequence()) {
this._value = {}
this._value.beforeCount = ber.readInt()
this._value.afterCount = ber.readInt()
if (ber.peek() === 0xa0) {
if (ber.readSequence(0xa0)) {
this._value.targetOffset = ber.readInt()
this._value.contentCount = ber.readInt()
}
}
if (ber.peek() === 0x81) {
this._value.greaterThanOrEqual = ber.readString(0x81)
}
return true
}
return false
}
VirtualListViewControl.prototype._toBer = function (ber) {
assert.ok(ber)
if (!this._value || this.value.length === 0) {
return
}
var writer = new BerWriter()
writer.startSequence(0x30)
writer.writeInt(this.value.beforeCount)
writer.writeInt(this.value.afterCount)
if (this.value.targetOffset !== undefined) {
writer.startSequence(0xa0)
writer.writeInt(this.value.targetOffset)
writer.writeInt(this.value.contentCount)
writer.endSequence()
} else if (this.value.greaterThanOrEqual !== undefined) {
writer.writeString(this.value.greaterThanOrEqual, 0x81)
}
writer.endSequence()
ber.writeBuffer(writer.buffer, 0x04)
}
VirtualListViewControl.prototype._json = function (obj) {
obj.controlValue = this.value
return obj
}
VirtualListViewControl.OID = '2.16.840.1.113730.3.4.9'
/// ---Exports
module.exports = VirtualListViewControl

View File

@ -0,0 +1,112 @@
var assert = require('assert-plus')
var util = require('util')
var asn1 = require('asn1')
var Control = require('./control')
var CODES = require('../errors/codes')
/// --- Globals
var BerReader = asn1.BerReader
var BerWriter = asn1.BerWriter
var VALID_CODES = [
CODES.LDAP_SUCCESS,
CODES.LDAP_OPERATIONS_ERROR,
CODES.LDAP_UNWILLING_TO_PERFORM,
CODES.LDAP_INSUFFICIENT_ACCESS_RIGHTS,
CODES.LDAP_BUSY,
CODES.LDAP_TIME_LIMIT_EXCEEDED,
CODES.LDAP_ADMIN_LIMIT_EXCEEDED,
CODES.LDAP_SORT_CONTROL_MISSING,
CODES.LDAP_INDEX_RANGE_ERROR,
CODES.LDAP_CONTROL_ERROR,
CODES.LDAP_OTHER
]
function VirtualListViewResponseControl (options) {
assert.optionalObject(options)
options = options || {}
options.type = VirtualListViewResponseControl.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')
}
this._value = options.value
} else {
throw new TypeError('options.value must be a Buffer or Object')
}
options.value = null
}
Control.call(this, options)
}
util.inherits(VirtualListViewResponseControl, Control)
Object.defineProperties(VirtualListViewResponseControl.prototype, {
value: {
get: function () { return this._value || {} },
configurable: false
}
})
VirtualListViewResponseControl.prototype.parse = function parse (buffer) {
assert.ok(buffer)
var ber = new BerReader(buffer)
if (ber.readSequence()) {
this._value = {}
if (ber.peek(0x02)) {
this._value.targetPosition = ber.readInt()
}
if (ber.peek(0x02)) {
this._value.contentCount = ber.readInt()
}
this._value.result = ber.readEnumeration()
this._value.cookie = ber.readString(asn1.Ber.OctetString, true)
// readString returns '' instead of a zero-length buffer
if (!this._value.cookie) {
this._value.cookie = Buffer.alloc(0)
}
return true
}
return false
}
VirtualListViewResponseControl.prototype._toBer = function (ber) {
assert.ok(ber)
if (!this._value || this.value.length === 0) {
return
}
var writer = new BerWriter()
writer.startSequence()
if (this.value.targetPosition !== undefined) {
writer.writeInt(this.value.targetPosition)
}
if (this.value.contentCount !== undefined) {
writer.writeInt(this.value.contentCount)
}
writer.writeEnumeration(this.value.result)
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)
}
VirtualListViewResponseControl.prototype._json = function (obj) {
obj.controlValue = this.value
return obj
}
VirtualListViewResponseControl.OID = '2.16.840.1.113730.3.4.10'
/// --- Exports
module.exports = VirtualListViewResponseControl

50
lib/corked_emitter.js Normal file
View File

@ -0,0 +1,50 @@
'use strict'
var EventEmitter = require('events').EventEmitter
/**
* A CorkedEmitter is a variant of an EventEmitter where events emitted
* wait for the appearance of the first listener of any kind. That is,
* a CorkedEmitter will store all .emit()s it receives, to be replayed
* later when an .on() is applied.
* It is meant for situations where the consumers of the emitter are
* unable to register listeners right away, and cannot afford to miss
* any events emitted from the start.
* Note that, whenever the first emitter (for any event) appears,
* the emitter becomes uncorked and works as usual for ALL events, and
* will not cache anything anymore. This is necessary to avoid
* re-ordering emits - either everything is being buffered, or nothing.
*/
function CorkedEmitter () {
var self = this
EventEmitter.call(self)
/**
* An array of arguments objects (array-likes) to emit on open.
*/
self._outstandingEmits = []
/**
* Whether the normal flow of emits is restored yet.
*/
self._opened = false
// When the first listener appears, we enqueue an opening.
// It is not done immediately, so that other listeners can be
// registered in the same critical section.
self.once('newListener', function () {
setImmediate(function releaseStoredEvents () {
self._opened = true
self._outstandingEmits.forEach(function (args) {
self.emit.apply(self, args)
})
})
})
}
CorkedEmitter.prototype = Object.create(EventEmitter.prototype)
CorkedEmitter.prototype.emit = function emit (eventName) {
if (this._opened || eventName === 'newListener') {
EventEmitter.prototype.emit.apply(this, arguments)
} else {
this._outstandingEmits.push(arguments)
}
}
module.exports = CorkedEmitter

525
lib/dn.js
View File

@ -1,336 +1,317 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved. // Copyright 2011 Mark Cavage, Inc. All rights reserved.
var assert = require('assert-plus')
var assert = require('assert-plus'); /// --- Helpers
function invalidDN (name) {
///--- Helpers var e = new Error()
e.name = 'InvalidDistinguishedNameError'
function invalidDN(name) { e.message = name
var e = new Error(); return e
e.name = 'InvalidDistinguishedNameError';
e.message = name;
return e;
} }
function isAlphaNumeric(c) { function isAlphaNumeric (c) {
var re = /[A-Za-z0-9]/; var re = /[A-Za-z0-9]/
return re.test(c); return re.test(c)
} }
function isWhitespace(c) { function isWhitespace (c) {
var re = /\s/; var re = /\s/
return re.test(c); return re.test(c)
} }
function repeatChar(c, n) { function repeatChar (c, n) {
var out = ''; var out = ''
var max = n ? n : 0; var max = n || 0
for (var i = 0; i < max; i++) for (var i = 0; i < max; i++) { out += c }
out += c; return out
return out;
} }
///--- API /// --- API
function RDN(obj) { function RDN (obj) {
var self = this; var self = this
this.attrs = {}; this.attrs = {}
if (obj) { if (obj) {
Object.keys(obj).forEach(function (k) { Object.keys(obj).forEach(function (k) {
self.set(k, obj[k]); self.set(k, obj[k])
}); })
} }
} }
RDN.prototype.set = function rdnSet(name, value, opts) { RDN.prototype.set = function rdnSet (name, value, opts) {
assert.string(name, 'name (string) required'); assert.string(name, 'name (string) required')
assert.string(value, 'value (string) required'); assert.string(value, 'value (string) required')
var self = this; var self = this
var lname = name.toLowerCase(); var lname = name.toLowerCase()
this.attrs[lname] = { this.attrs[lname] = {
value: value, value: value,
name: name name: name
}; }
if (opts && typeof (opts) === 'object') { if (opts && typeof (opts) === 'object') {
Object.keys(opts).forEach(function (k) { Object.keys(opts).forEach(function (k) {
if (k !== 'value') if (k !== 'value') { self.attrs[lname][k] = opts[k] }
self.attrs[lname][k] = opts[k]; })
});
} }
}; }
RDN.prototype.equals = function rdnEquals(rdn) { RDN.prototype.equals = function rdnEquals (rdn) {
if (typeof (rdn) !== 'object') if (typeof (rdn) !== 'object') { return false }
return false;
var ourKeys = Object.keys(this.attrs); var ourKeys = Object.keys(this.attrs)
var theirKeys = Object.keys(rdn.attrs); var theirKeys = Object.keys(rdn.attrs)
if (ourKeys.length !== theirKeys.length) if (ourKeys.length !== theirKeys.length) { return false }
return false;
ourKeys.sort(); ourKeys.sort()
theirKeys.sort(); theirKeys.sort()
for (var i = 0; i < ourKeys.length; i++) { for (var i = 0; i < ourKeys.length; i++) {
if (ourKeys[i] !== theirKeys[i]) if (ourKeys[i] !== theirKeys[i]) { return false }
return false; if (this.attrs[ourKeys[i]].value !== rdn.attrs[ourKeys[i]].value) { return false }
if (this.attrs[ourKeys[i]].value !== rdn.attrs[ourKeys[i]].value)
return false;
} }
return true; return true
}; }
/** /**
* Convert RDN to string according to specified formatting options. * Convert RDN to string according to specified formatting options.
* (see: DN.format for option details) * (see: DN.format for option details)
*/ */
RDN.prototype.format = function rdnFormat(options) { RDN.prototype.format = function rdnFormat (options) {
assert.optionalObject(options, 'options must be an object'); assert.optionalObject(options, 'options must be an object')
options = options || {}; options = options || {}
var self = this; var self = this
var str = ''; var str = ''
function escapeValue(val, forceQuote) { function escapeValue (val, forceQuote) {
var out = ''; var out = ''
var cur = 0; var cur = 0
var len = val.length; var len = val.length
var quoted = false; var quoted = false
/* BEGIN JSSTYLED */ /* BEGIN JSSTYLED */
var escaped = /[\\\"]/; // TODO: figure out what this regex is actually trying to test for and
var special = /[,=+<>#;]/; // fix it to appease the linter.
/* eslint-disable-next-line no-useless-escape */
var escaped = /[\\\"]/
var special = /[,=+<>#;]/
/* END JSSTYLED */ /* END JSSTYLED */
if (len > 0) { if (len > 0) {
// Wrap strings with trailing or leading spaces in quotes // Wrap strings with trailing or leading spaces in quotes
quoted = forceQuote || (val[0] == ' ' || val[len-1] == ' '); quoted = forceQuote || (val[0] === ' ' || val[len - 1] === ' ')
} }
while (cur < len) { while (cur < len) {
if (escaped.test(val[cur]) || (!quoted && special.test(val[cur]))) { if (escaped.test(val[cur]) || (!quoted && special.test(val[cur]))) {
out += '\\'; out += '\\'
} }
out += val[cur++]; out += val[cur++]
} }
if (quoted) if (quoted) { out = '"' + out + '"' }
out = '"' + out + '"'; return out
return out;
} }
function sortParsed(a, b) { function sortParsed (a, b) {
return self.attrs[a].order - self.attrs[b].order; return self.attrs[a].order - self.attrs[b].order
} }
function sortStandard(a, b) { function sortStandard (a, b) {
var nameCompare = a.localeCompare(b); var nameCompare = a.localeCompare(b)
if (nameCompare === 0) { if (nameCompare === 0) {
// TODO: Handle binary values // TODO: Handle binary values
return self.attrs[a].value.localeCompare(self.attrs[b].value); return self.attrs[a].value.localeCompare(self.attrs[b].value)
} else { } else {
return nameCompare; return nameCompare
} }
} }
var keys = Object.keys(this.attrs); var keys = Object.keys(this.attrs)
if (options.keepOrder) { if (options.keepOrder) {
keys.sort(sortParsed); keys.sort(sortParsed)
} else { } else {
keys.sort(sortStandard); keys.sort(sortStandard)
} }
keys.forEach(function (key) { keys.forEach(function (key) {
var attr = self.attrs[key]; var attr = self.attrs[key]
if (str.length) if (str.length) { str += '+' }
str += '+';
if (options.keepCase) { if (options.keepCase) {
str += attr.name; str += attr.name
} else { } else {
if (options.upperName) if (options.upperName) { str += key.toUpperCase() } else { str += key }
str += key.toUpperCase();
else
str += key;
} }
str += '=' + escapeValue(attr.value, (options.keepQuote && attr.quoted)); str += '=' + escapeValue(attr.value, (options.keepQuote && attr.quoted))
}); })
return str; return str
}; }
RDN.prototype.toString = function rdnToString() {
return this.format();
};
RDN.prototype.toString = function rdnToString () {
return this.format()
}
// Thank you OpenJDK! // Thank you OpenJDK!
function parse(name) { function parse (name) {
if (typeof (name) !== 'string') if (typeof (name) !== 'string') { throw new TypeError('name (string) required') }
throw new TypeError('name (string) required');
var cur = 0; var cur = 0
var len = name.length; var len = name.length
function parseRdn() { function parseRdn () {
var rdn = new RDN(); var rdn = new RDN()
var order = 0; var order = 0
rdn.spLead = trim(); rdn.spLead = trim()
while (cur < len) { while (cur < len) {
var opts = { var opts = {
order: order order: order
}; }
var attr = parseAttrType(); var attr = parseAttrType()
trim(); trim()
if (cur >= len || name[cur++] !== '=') if (cur >= len || name[cur++] !== '=') { throw invalidDN(name) }
throw invalidDN(name);
trim(); trim()
// Parameters about RDN value are set in 'opts' by parseAttrValue // Parameters about RDN value are set in 'opts' by parseAttrValue
var value = parseAttrValue(opts); var value = parseAttrValue(opts)
rdn.set(attr, value, opts); rdn.set(attr, value, opts)
rdn.spTrail = trim(); rdn.spTrail = trim()
if (cur >= len || name[cur] !== '+') if (cur >= len || name[cur] !== '+') { break }
break; ++cur
++cur; ++order
++order;
} }
return rdn; return rdn
} }
function trim () {
function trim() { var count = 0
var count = 0;
while ((cur < len) && isWhitespace(name[cur])) { while ((cur < len) && isWhitespace(name[cur])) {
++cur; ++cur
count++; count++
} }
return count; return count
} }
function parseAttrType() { function parseAttrType () {
var beg = cur; var beg = cur
while (cur < len) { while (cur < len) {
var c = name[cur]; var c = name[cur]
if (isAlphaNumeric(c) || if (isAlphaNumeric(c) ||
c == '.' || c === '.' ||
c == '-' || c === '-' ||
c == ' ') { c === ' ') {
++cur; ++cur
} else { } else {
break; break
} }
} }
// Back out any trailing spaces. // Back out any trailing spaces.
while ((cur > beg) && (name[cur - 1] == ' ')) while ((cur > beg) && (name[cur - 1] === ' ')) { --cur }
--cur;
if (beg == cur) if (beg === cur) { throw invalidDN(name) }
throw invalidDN(name);
return name.slice(beg, cur); return name.slice(beg, cur)
} }
function parseAttrValue(opts) { function parseAttrValue (opts) {
if (cur < len && name[cur] == '#') { if (cur < len && name[cur] === '#') {
opts.binary = true; opts.binary = true
return parseBinaryAttrValue(); return parseBinaryAttrValue()
} else if (cur < len && name[cur] == '"') { } else if (cur < len && name[cur] === '"') {
opts.quoted = true; opts.quoted = true
return parseQuotedAttrValue(); return parseQuotedAttrValue()
} else { } else {
return parseStringAttrValue(); return parseStringAttrValue()
} }
} }
function parseBinaryAttrValue() { function parseBinaryAttrValue () {
var beg = cur++; var beg = cur++
while (cur < len && isAlphaNumeric(name[cur])) while (cur < len && isAlphaNumeric(name[cur])) { ++cur }
++cur;
return name.slice(beg, cur); return name.slice(beg, cur)
} }
function parseQuotedAttrValue() { function parseQuotedAttrValue () {
var str = ''; var str = ''
++cur; // Consume the first quote ++cur // Consume the first quote
while ((cur < len) && name[cur] != '"') { while ((cur < len) && name[cur] !== '"') {
if (name[cur] === '\\') if (name[cur] === '\\') { cur++ }
cur++; str += name[cur++]
str += name[cur++]; }
if (cur++ >= len) {
// no closing quote
throw invalidDN(name)
} }
if (cur++ >= len) // no closing quote
throw invalidDN(name);
return str; return str
} }
function parseStringAttrValue() { function parseStringAttrValue () {
var beg = cur; var beg = cur
var str = ''; var str = ''
var esc = -1; var esc = -1
while ((cur < len) && !atTerminator()) { while ((cur < len) && !atTerminator()) {
if (name[cur] === '\\') { if (name[cur] === '\\') {
// Consume the backslash and mark its place just in case it's escaping // Consume the backslash and mark its place just in case it's escaping
// whitespace which needs to be preserved. // whitespace which needs to be preserved.
esc = cur++; esc = cur++
} }
if (cur === len) // backslash followed by nothing if (cur === len) {
throw invalidDN(name); // backslash followed by nothing
str += name[cur++]; throw invalidDN(name)
}
str += name[cur++]
} }
// Trim off (unescaped) trailing whitespace and rewind cursor to the end of // Trim off (unescaped) trailing whitespace and rewind cursor to the end of
// the AttrValue to record whitespace length. // the AttrValue to record whitespace length.
for (; cur > beg; cur--) { for (; cur > beg; cur--) {
if (!isWhitespace(name[cur - 1]) || (esc === (cur - 1))) if (!isWhitespace(name[cur - 1]) || (esc === (cur - 1))) { break }
break;
} }
return str.slice(0, cur - beg); return str.slice(0, cur - beg)
} }
function atTerminator() { function atTerminator () {
return (cur < len && return (cur < len &&
(name[cur] === ',' || (name[cur] === ',' ||
name[cur] === ';' || name[cur] === ';' ||
name[cur] === '+')); name[cur] === '+'))
} }
var rdns = []; var rdns = []
// Short-circuit for empty DNs // Short-circuit for empty DNs
if (len === 0) if (len === 0) { return new DN(rdns) }
return new DN(rdns);
rdns.push(parseRdn()); rdns.push(parseRdn())
while (cur < len) { while (cur < len) {
if (name[cur] === ',' || name[cur] === ';') { if (name[cur] === ',' || name[cur] === ';') {
++cur; ++cur
rdns.push(parseRdn()); rdns.push(parseRdn())
} else { } else {
throw invalidDN(name); throw invalidDN(name)
} }
} }
return new DN(rdns); return new DN(rdns)
} }
function DN (rdns) {
assert.optionalArrayOfObject(rdns, '[object] required')
function DN(rdns) { this.rdns = rdns ? rdns.slice() : []
assert.optionalArrayOfObject(rdns, '[object] required'); this._format = {}
this.rdns = rdns ? rdns.slice() : [];
this._format = {};
} }
Object.defineProperties(DN.prototype, { Object.defineProperties(DN.prototype, {
length: { length: {
get: function getLength() { return this.rdns.length; }, get: function getLength () { return this.rdns.length },
configurable: false configurable: false
} }
}); })
/** /**
* Convert DN to string according to specified formatting options. * Convert DN to string according to specified formatting options.
@ -355,146 +336,138 @@ Object.defineProperties(DN.prototype, {
* - upperName: RDN names will be uppercased instead of lowercased. * - upperName: RDN names will be uppercased instead of lowercased.
* - skipSpace: Disable trailing space after RDN separators * - skipSpace: Disable trailing space after RDN separators
*/ */
DN.prototype.format = function dnFormat(options) { DN.prototype.format = function dnFormat (options) {
assert.optionalObject(options, 'options must be an object'); assert.optionalObject(options, 'options must be an object')
options = options || this._format; options = options || this._format
var str = ''; var str = ''
this.rdns.forEach(function (rdn) { this.rdns.forEach(function (rdn) {
var rdnString = rdn.format(options); var rdnString = rdn.format(options)
if (str.length !== 0) { if (str.length !== 0) {
str += ','; str += ','
} }
if (options.keepSpace) { if (options.keepSpace) {
str += (repeatChar(' ', rdn.spLead) + str += (repeatChar(' ', rdn.spLead) +
rdnString + repeatChar(' ', rdn.spTrail)); rdnString + repeatChar(' ', rdn.spTrail))
} else if (options.skipSpace === true || str.length === 0) { } else if (options.skipSpace === true || str.length === 0) {
str += rdnString; str += rdnString
} else { } else {
str += ' ' + rdnString; str += ' ' + rdnString
} }
}); })
return str; return str
}; }
/** /**
* Set default string formatting options. * Set default string formatting options.
*/ */
DN.prototype.setFormat = function setFormat(options) { DN.prototype.setFormat = function setFormat (options) {
assert.object(options, 'options must be an object'); assert.object(options, 'options must be an object')
this._format = options; this._format = options
}; }
DN.prototype.toString = function dnToString() { DN.prototype.toString = function dnToString () {
return this.format(); return this.format()
}; }
DN.prototype.parentOf = function parentOf(dn) { DN.prototype.parentOf = function parentOf (dn) {
if (typeof (dn) !== 'object') if (typeof (dn) !== 'object') { dn = parse(dn) }
dn = parse(dn);
if (this.rdns.length >= dn.rdns.length) if (this.rdns.length >= dn.rdns.length) { return false }
return false;
var diff = dn.rdns.length - this.rdns.length; var diff = dn.rdns.length - this.rdns.length
for (var i = this.rdns.length - 1; i >= 0; i--) { for (var i = this.rdns.length - 1; i >= 0; i--) {
var myRDN = this.rdns[i]; var myRDN = this.rdns[i]
var theirRDN = dn.rdns[i + diff]; var theirRDN = dn.rdns[i + diff]
if (!myRDN.equals(theirRDN)) if (!myRDN.equals(theirRDN)) { return false }
return false;
} }
return true; return true
}; }
DN.prototype.childOf = function childOf(dn) { DN.prototype.childOf = function childOf (dn) {
if (typeof (dn) !== 'object') if (typeof (dn) !== 'object') { dn = parse(dn) }
dn = parse(dn); return dn.parentOf(this)
return dn.parentOf(this); }
};
DN.prototype.isEmpty = function isEmpty() { DN.prototype.isEmpty = function isEmpty () {
return (this.rdns.length === 0); return (this.rdns.length === 0)
}; }
DN.prototype.equals = function dnEquals(dn) { DN.prototype.equals = function dnEquals (dn) {
if (typeof (dn) !== 'object') if (typeof (dn) !== 'object') { dn = parse(dn) }
dn = parse(dn);
if (this.rdns.length !== dn.rdns.length) if (this.rdns.length !== dn.rdns.length) { return false }
return false;
for (var i = 0; i < this.rdns.length; i++) { for (var i = 0; i < this.rdns.length; i++) {
if (!this.rdns[i].equals(dn.rdns[i])) if (!this.rdns[i].equals(dn.rdns[i])) { return false }
return false;
} }
return true; return true
}; }
DN.prototype.parent = function dnParent() { DN.prototype.parent = function dnParent () {
if (this.rdns.length !== 0) { if (this.rdns.length !== 0) {
var save = this.rdns.shift(); var save = this.rdns.shift()
var dn = new DN(this.rdns); var dn = new DN(this.rdns)
this.rdns.unshift(save); this.rdns.unshift(save)
return dn; return dn
} }
return null; return null
}; }
DN.prototype.clone = function dnClone() { DN.prototype.clone = function dnClone () {
var dn = new DN(this.rdns); var dn = new DN(this.rdns)
dn._format = this._format; dn._format = this._format
return dn; return dn
}; }
DN.prototype.reverse = function dnReverse() { DN.prototype.reverse = function dnReverse () {
this.rdns.reverse(); this.rdns.reverse()
return this; return this
}; }
DN.prototype.pop = function dnPop() { DN.prototype.pop = function dnPop () {
return this.rdns.pop(); return this.rdns.pop()
}; }
DN.prototype.push = function dnPush(rdn) { DN.prototype.push = function dnPush (rdn) {
assert.object(rdn, 'rdn (RDN) required'); assert.object(rdn, 'rdn (RDN) required')
return this.rdns.push(rdn); return this.rdns.push(rdn)
}; }
DN.prototype.shift = function dnShift() { DN.prototype.shift = function dnShift () {
return this.rdns.shift(); return this.rdns.shift()
}; }
DN.prototype.unshift = function dnUnshift(rdn) { DN.prototype.unshift = function dnUnshift (rdn) {
assert.object(rdn, 'rdn (RDN) required'); assert.object(rdn, 'rdn (RDN) required')
return this.rdns.unshift(rdn); return this.rdns.unshift(rdn)
}; }
DN.isDN = function isDN(dn) { DN.isDN = function isDN (dn) {
if (!dn || typeof (dn) !== 'object') { if (!dn || typeof (dn) !== 'object') {
return false; return false
} }
if (dn instanceof DN) { if (dn instanceof DN) {
return true; return true
} }
if (Array.isArray(dn.rdns)) { if (Array.isArray(dn.rdns)) {
// Really simple duck-typing for now // Really simple duck-typing for now
return true; return true
} }
return false; return false
}; }
/// --- Exports
///--- Exports
module.exports = { module.exports = {
parse: parse, parse: parse,
DN: DN, DN: DN,
RDN: RDN RDN: RDN
}; }

View File

@ -1,12 +1,10 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved.s // Copyright 2011 Mark Cavage, Inc. All rights reserved.s
/// --- Globals
var SERVER_PROVIDER
///--- Globals var DTRACE_ID = 0
var MAX_INT = 4294967295
var SERVER_PROVIDER;
var DTRACE_ID = 0;
var MAX_INT = 4294967295;
/* /*
* Args: * Args:
@ -37,7 +35,7 @@ var SERVER_PROBES = {
// 4: attribute, 5: value // 4: attribute, 5: value
'server-compare-start': ['int', 'char *', 'char *', 'char *', 'server-compare-start': ['int', 'char *', 'char *', 'char *',
'char *', 'char *'], 'char *', 'char *'],
'server-compare-done': ['int', 'char *', 'char *', 'char *', 'int', 'char *'], 'server-compare-done': ['int', 'char *', 'char *', 'char *', 'int', 'char *'],
'server-delete-start': ['int', 'char *', 'char *', 'char *'], 'server-delete-start': ['int', 'char *', 'char *', 'char *'],
@ -45,7 +43,7 @@ var SERVER_PROBES = {
// 4: requestName, 5: requestValue // 4: requestName, 5: requestValue
'server-exop-start': ['int', 'char *', 'char *', 'char *', 'char *', 'server-exop-start': ['int', 'char *', 'char *', 'char *', 'char *',
'char *'], 'char *'],
'server-exop-done': ['int', 'char *', 'char *', 'char *', 'int', 'char *'], 'server-exop-done': ['int', 'char *', 'char *', 'char *', 'int', 'char *'],
// 4: changes.length // 4: changes.length
@ -54,13 +52,13 @@ var SERVER_PROBES = {
// 4: newRdn, 5: newSuperior // 4: newRdn, 5: newSuperior
'server-modifydn-start': ['int', 'char *', 'char *', 'char *', 'char *', 'server-modifydn-start': ['int', 'char *', 'char *', 'char *', 'char *',
'char *'], 'char *'],
'server-modifydn-done': ['int', 'char *', 'char *', 'char *', 'int', 'server-modifydn-done': ['int', 'char *', 'char *', 'char *', 'int',
'char *'], 'char *'],
// 4: scope, 5: filter // 4: scope, 5: filter
'server-search-start': ['int', 'char *', 'char *', 'char *', 'char *', 'server-search-start': ['int', 'char *', 'char *', 'char *', 'char *',
'char *'], 'char *'],
'server-search-done': ['int', 'char *', 'char *', 'char *', 'int', 'char *'], 'server-search-done': ['int', 'char *', 'char *', 'char *', 'int', 'char *'],
// Last two are searchEntry.DN and seachEntry.attributes.length // Last two are searchEntry.DN and seachEntry.attributes.length
'server-search-entry': ['int', 'char *', 'char *', 'char *', 'char *', 'int'], 'server-search-entry': ['int', 'char *', 'char *', 'char *', 'char *', 'int'],
@ -73,52 +71,50 @@ var SERVER_PROBES = {
// remote IP // remote IP
'server-connection': ['char *'] 'server-connection': ['char *']
}; }
/// --- API
///--- API module.exports = (function () {
module.exports = function () {
if (!SERVER_PROVIDER) { if (!SERVER_PROVIDER) {
try { try {
var dtrace = require('dtrace-provider'); var dtrace = require('dtrace-provider')
SERVER_PROVIDER = dtrace.createDTraceProvider('ldapjs'); SERVER_PROVIDER = dtrace.createDTraceProvider('ldapjs')
Object.keys(SERVER_PROBES).forEach(function (p) { Object.keys(SERVER_PROBES).forEach(function (p) {
var args = SERVER_PROBES[p].splice(0); var args = SERVER_PROBES[p].splice(0)
args.unshift(p); args.unshift(p)
dtrace.DTraceProvider.prototype.addProbe.apply(SERVER_PROVIDER, args); dtrace.DTraceProvider.prototype.addProbe.apply(SERVER_PROVIDER, args)
}); })
} catch (e) { } catch (e) {
SERVER_PROVIDER = { SERVER_PROVIDER = {
fire: function () { fire: function () {
}, },
enable: function () { enable: function () {
}, },
addProbe: function () { addProbe: function () {
var p = { var p = {
fire: function () { fire: function () {
} }
};
return (p);
},
removeProbe: function () {
},
disable: function () {
} }
}; return (p)
},
removeProbe: function () {
},
disable: function () {
}
}
} }
SERVER_PROVIDER.enable(); SERVER_PROVIDER.enable()
SERVER_PROVIDER._nextId = function () { SERVER_PROVIDER._nextId = function () {
if (DTRACE_ID === MAX_INT) if (DTRACE_ID === MAX_INT) { DTRACE_ID = 0 }
DTRACE_ID = 0;
return ++DTRACE_ID; return ++DTRACE_ID
}; }
} }
return SERVER_PROVIDER; return SERVER_PROVIDER
}(); }())

View File

@ -1,4 +1,4 @@
// Copyright 2014 Joyent, Inc. All rights reserved. 'use strict'
module.exports = { module.exports = {
LDAP_SUCCESS: 0, LDAP_SUCCESS: 0,
@ -32,6 +32,8 @@ module.exports = {
LDAP_UNAVAILABLE: 52, LDAP_UNAVAILABLE: 52,
LDAP_UNWILLING_TO_PERFORM: 53, LDAP_UNWILLING_TO_PERFORM: 53,
LDAP_LOOP_DETECT: 54, LDAP_LOOP_DETECT: 54,
LDAP_SORT_CONTROL_MISSING: 60,
LDAP_INDEX_RANGE_ERROR: 61,
LDAP_NAMING_VIOLATION: 64, LDAP_NAMING_VIOLATION: 64,
LDAP_OBJECTCLASS_VIOLATION: 65, LDAP_OBJECTCLASS_VIOLATION: 65,
LDAP_NOT_ALLOWED_ON_NON_LEAF: 66, LDAP_NOT_ALLOWED_ON_NON_LEAF: 66,
@ -39,6 +41,7 @@ module.exports = {
LDAP_ENTRY_ALREADY_EXISTS: 68, LDAP_ENTRY_ALREADY_EXISTS: 68,
LDAP_OBJECTCLASS_MODS_PROHIBITED: 69, LDAP_OBJECTCLASS_MODS_PROHIBITED: 69,
LDAP_AFFECTS_MULTIPLE_DSAS: 71, LDAP_AFFECTS_MULTIPLE_DSAS: 71,
LDAP_CONTROL_ERROR: 76,
LDAP_OTHER: 80, LDAP_OTHER: 80,
LDAP_PROXIED_AUTHORIZATION_DENIED: 123 LDAP_PROXIED_AUTHORIZATION_DENIED: 123
}; }

View File

@ -1,151 +1,144 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved. 'use strict'
var util = require('util'); var util = require('util')
var assert = require('assert-plus'); var assert = require('assert-plus')
var LDAPResult = require('../messages').LDAPResult; var LDAPResult = require('../messages').LDAPResult
/// --- Globals
///--- Globals var CODES = require('./codes')
var ERRORS = []
var CODES = require('./codes'); /// --- Error Base class
var ERRORS = [];
function LDAPError (message, dn, caller) {
if (Error.captureStackTrace) { Error.captureStackTrace(this, caller || LDAPError) }
///--- Error Base class this.lde_message = message
this.lde_dn = dn
function LDAPError(message, dn, caller) {
if (Error.captureStackTrace)
Error.captureStackTrace(this, caller || LDAPError);
this.lde_message = message;
this.lde_dn = dn;
} }
util.inherits(LDAPError, Error); util.inherits(LDAPError, Error)
Object.defineProperties(LDAPError.prototype, { Object.defineProperties(LDAPError.prototype, {
name: { name: {
get: function getName() { return 'LDAPError'; }, get: function getName () { return 'LDAPError' },
configurable: false configurable: false
}, },
code: { code: {
get: function getCode() { return CODES.LDAP_OTHER; }, get: function getCode () { return CODES.LDAP_OTHER },
configurable: false configurable: false
}, },
message: { message: {
get: function getMessage() { get: function getMessage () {
return this.lde_message || this.name; return this.lde_message || this.name
}, },
configurable: false configurable: false
}, },
dn: { dn: {
get: function getDN() { get: function getDN () {
return (this.lde_dn ? this.lde_dn.toString() : ''); return (this.lde_dn ? this.lde_dn.toString() : '')
}, },
configurable: false configurable: false
} }
}); })
/// --- Exported API
///--- Exported API module.exports = {}
module.exports.LDAPError = LDAPError
module.exports = {};
module.exports.LDAPError = LDAPError;
// Some whacky games here to make sure all the codes are exported // Some whacky games here to make sure all the codes are exported
Object.keys(CODES).forEach(function (code) { Object.keys(CODES).forEach(function (code) {
module.exports[code] = CODES[code]; module.exports[code] = CODES[code]
if (code === 'LDAP_SUCCESS') if (code === 'LDAP_SUCCESS') { return }
return;
var err = ''; var err = ''
var msg = ''; var msg = ''
var pieces = code.split('_').slice(1); var pieces = code.split('_').slice(1)
for (var i = 0; i < pieces.length; i++) { for (var i = 0; i < pieces.length; i++) {
var lc = pieces[i].toLowerCase(); var lc = pieces[i].toLowerCase()
var key = lc.charAt(0).toUpperCase() + lc.slice(1); var key = lc.charAt(0).toUpperCase() + lc.slice(1)
err += key; err += key
msg += key + ((i + 1) < pieces.length ? ' ' : ''); msg += key + ((i + 1) < pieces.length ? ' ' : '')
} }
if (!/\w+Error$/.test(err)) if (!/\w+Error$/.test(err)) { err += 'Error' }
err += 'Error';
// At this point LDAP_OPERATIONS_ERROR is now OperationsError in $err // At this point LDAP_OPERATIONS_ERROR is now OperationsError in $err
// and 'Operations Error' in $msg // and 'Operations Error' in $msg
module.exports[err] = function (message, dn, caller) { module.exports[err] = function (message, dn, caller) {
LDAPError.call(this, message, dn, caller || module.exports[err]); LDAPError.call(this, message, dn, caller || module.exports[err])
}; }
module.exports[err].constructor = module.exports[err]; module.exports[err].constructor = module.exports[err]
util.inherits(module.exports[err], LDAPError); util.inherits(module.exports[err], LDAPError)
Object.defineProperties(module.exports[err].prototype, { Object.defineProperties(module.exports[err].prototype, {
name: { name: {
get: function getName() { return err; }, get: function getName () { return err },
configurable: false configurable: false
}, },
code: { code: {
get: function getCode() { return CODES[code]; }, get: function getCode () { return CODES[code] },
configurable: false configurable: false
} }
}); })
ERRORS[CODES[code]] = { ERRORS[CODES[code]] = {
err: err, err: err,
message: msg message: msg
}; }
}); })
module.exports.getError = function (res) { module.exports.getError = function (res) {
assert.ok(res instanceof LDAPResult, 'res (LDAPResult) required'); assert.ok(res instanceof LDAPResult, 'res (LDAPResult) required')
var errObj = ERRORS[res.status]; var errObj = ERRORS[res.status]
var E = module.exports[errObj.err]; var E = module.exports[errObj.err]
return new E(res.errorMessage || errObj.message, return new E(res.errorMessage || errObj.message,
res.matchedDN || null, res.matchedDN || null,
module.exports.getError); module.exports.getError)
}; }
module.exports.getMessage = function (code) { module.exports.getMessage = function (code) {
assert.number(code, 'code (number) required'); assert.number(code, 'code (number) required')
var errObj = ERRORS[code]; var errObj = ERRORS[code]
return (errObj && errObj.message ? errObj.message : ''); return (errObj && errObj.message ? errObj.message : '')
};
///--- Custom application errors
function ConnectionError(message) {
LDAPError.call(this, message, null, ConnectionError);
} }
util.inherits(ConnectionError, LDAPError);
module.exports.ConnectionError = ConnectionError; /// --- Custom application errors
function ConnectionError (message) {
LDAPError.call(this, message, null, ConnectionError)
}
util.inherits(ConnectionError, LDAPError)
module.exports.ConnectionError = ConnectionError
Object.defineProperties(ConnectionError.prototype, { Object.defineProperties(ConnectionError.prototype, {
name: { name: {
get: function () { return 'ConnectionError'; }, get: function () { return 'ConnectionError' },
configurable: false configurable: false
} }
}); })
function AbandonedError(message) { function AbandonedError (message) {
LDAPError.call(this, message, null, AbandonedError); LDAPError.call(this, message, null, AbandonedError)
} }
util.inherits(AbandonedError, LDAPError); util.inherits(AbandonedError, LDAPError)
module.exports.AbandonedError = AbandonedError; module.exports.AbandonedError = AbandonedError
Object.defineProperties(AbandonedError.prototype, { Object.defineProperties(AbandonedError.prototype, {
name: { name: {
get: function () { return 'AbandonedError'; }, get: function () { return 'AbandonedError' },
configurable: false configurable: false
} }
}); })
function TimeoutError(message) { function TimeoutError (message) {
LDAPError.call(this, message, null, TimeoutError); LDAPError.call(this, message, null, TimeoutError)
} }
util.inherits(TimeoutError, LDAPError); util.inherits(TimeoutError, LDAPError)
module.exports.TimeoutError = TimeoutError; module.exports.TimeoutError = TimeoutError
Object.defineProperties(TimeoutError.prototype, { Object.defineProperties(TimeoutError.prototype, {
name: { name: {
get: function () { return 'TimeoutError'; }, get: function () { return 'TimeoutError' },
configurable: false configurable: false
} }
}); })

View File

@ -1,30 +1,27 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved. // Copyright 2011 Mark Cavage, Inc. All rights reserved.
var assert = require('assert'); var assert = require('assert')
var util = require('util'); var util = require('util')
var parents = require('ldap-filter'); var parents = require('ldap-filter')
var Filter = require('./filter'); var Filter = require('./filter')
/// --- API
function AndFilter (options) {
///--- API parents.AndFilter.call(this, options)
function AndFilter(options) {
parents.AndFilter.call(this, options);
} }
util.inherits(AndFilter, parents.AndFilter); util.inherits(AndFilter, parents.AndFilter)
Filter.mixin(AndFilter); Filter.mixin(AndFilter)
module.exports = AndFilter; module.exports = AndFilter
AndFilter.prototype._toBer = function (ber) { AndFilter.prototype._toBer = function (ber) {
assert.ok(ber); assert.ok(ber)
this.filters.forEach(function (f) { this.filters.forEach(function (f) {
ber = f.toBer(ber); ber = f.toBer(ber)
}); })
return ber; return ber
}; }

View File

@ -1,39 +1,35 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved. // Copyright 2011 Mark Cavage, Inc. All rights reserved.
var assert = require('assert'); var assert = require('assert')
var util = require('util'); var util = require('util')
var parents = require('ldap-filter'); var parents = require('ldap-filter')
var Filter = require('./filter'); var Filter = require('./filter')
/// --- API
function ApproximateFilter (options) {
///--- API parents.ApproximateFilter.call(this, options)
function ApproximateFilter(options) {
parents.ApproximateFilter.call(this, options);
} }
util.inherits(ApproximateFilter, parents.ApproximateFilter); util.inherits(ApproximateFilter, parents.ApproximateFilter)
Filter.mixin(ApproximateFilter); Filter.mixin(ApproximateFilter)
module.exports = ApproximateFilter; module.exports = ApproximateFilter
ApproximateFilter.prototype.parse = function (ber) { ApproximateFilter.prototype.parse = function (ber) {
assert.ok(ber); assert.ok(ber)
this.attribute = ber.readString().toLowerCase(); this.attribute = ber.readString().toLowerCase()
this.value = ber.readString(); this.value = ber.readString()
return true;
};
return true
}
ApproximateFilter.prototype._toBer = function (ber) { ApproximateFilter.prototype._toBer = function (ber) {
assert.ok(ber); assert.ok(ber)
ber.writeString(this.attribute); ber.writeString(this.attribute)
ber.writeString(this.value); ber.writeString(this.value)
return ber; return ber
}; }

View File

@ -1,66 +1,60 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved. // Copyright 2011 Mark Cavage, Inc. All rights reserved.
var assert = require('assert-plus'); var assert = require('assert-plus')
var util = require('util'); var util = require('util')
var ASN1 = require('asn1').Ber; var ASN1 = require('asn1').Ber
var parents = require('ldap-filter'); var parents = require('ldap-filter')
var Filter = require('./filter'); var Filter = require('./filter')
/// --- API
function EqualityFilter (options) {
///--- API parents.EqualityFilter.call(this, options)
function EqualityFilter(options) {
parents.EqualityFilter.call(this, options);
} }
util.inherits(EqualityFilter, parents.EqualityFilter); util.inherits(EqualityFilter, parents.EqualityFilter)
Filter.mixin(EqualityFilter); Filter.mixin(EqualityFilter)
module.exports = EqualityFilter; module.exports = EqualityFilter
EqualityFilter.prototype.matches = function (target, strictAttrCase) { EqualityFilter.prototype.matches = function (target, strictAttrCase) {
assert.object(target, 'target'); assert.object(target, 'target')
var tv = parents.getAttrValue(target, this.attribute, strictAttrCase); var tv = parents.getAttrValue(target, this.attribute, strictAttrCase)
var value = this.value; var value = this.value
if (this.attribute.toLowerCase() === 'objectclass') { if (this.attribute.toLowerCase() === 'objectclass') {
/* /*
* Perform case-insensitive match for objectClass since nearly every LDAP * Perform case-insensitive match for objectClass since nearly every LDAP
* implementation behaves in this manner. * implementation behaves in this manner.
*/ */
value = value.toLowerCase(); value = value.toLowerCase()
return parents.testValues(function (v) { return parents.testValues(function (v) {
return value === v.toLowerCase(); return value === v.toLowerCase()
}, tv); }, tv)
} else { } else {
return parents.testValues(function (v) { return parents.testValues(function (v) {
return value === v; return value === v
}, tv); }, tv)
} }
}; }
EqualityFilter.prototype.parse = function (ber) { EqualityFilter.prototype.parse = function (ber) {
assert.ok(ber); assert.ok(ber)
this.attribute = ber.readString().toLowerCase(); this.attribute = ber.readString().toLowerCase()
this.value = ber.readString(ASN1.OctetString, true); this.value = ber.readString(ASN1.OctetString, true)
if (this.attribute === 'objectclass') if (this.attribute === 'objectclass') { this.value = this.value.toLowerCase() }
this.value = this.value.toLowerCase();
return true;
};
return true
}
EqualityFilter.prototype._toBer = function (ber) { EqualityFilter.prototype._toBer = function (ber) {
assert.ok(ber); assert.ok(ber)
ber.writeString(this.attribute); ber.writeString(this.attribute)
ber.writeBuffer(this.raw, ASN1.OctetString); ber.writeBuffer(this.raw, ASN1.OctetString)
return ber; return ber
}; }

View File

@ -14,32 +14,31 @@
*/ */
exports.escape = function (inp) { exports.escape = function (inp) {
if (typeof (inp) === 'string') { if (typeof (inp) === 'string') {
var esc = ''; var esc = ''
for (var i = 0; i < inp.length; i++) { for (var i = 0; i < inp.length; i++) {
switch (inp[i]) { switch (inp[i]) {
case '*': case '*':
esc += '\\2a'; esc += '\\2a'
break; break
case '(': case '(':
esc += '\\28'; esc += '\\28'
break; break
case ')': case ')':
esc += '\\29'; esc += '\\29'
break; break
case '\\': case '\\':
esc += '\\5c'; esc += '\\5c'
break; break
case '\0': case '\0':
esc += '\\00'; esc += '\\00'
break; break
default: default:
esc += inp[i]; esc += inp[i]
break; break
} }
} }
return esc; return esc
} else { } else {
return inp; return inp
} }
}; }

View File

@ -1,66 +1,59 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved. // Copyright 2011 Mark Cavage, Inc. All rights reserved.
var assert = require('assert'); var assert = require('assert')
var util = require('util'); var util = require('util')
var parents = require('ldap-filter');
var Filter = require('./filter');
var parents = require('ldap-filter')
var Filter = require('./filter')
// THIS IS A STUB! // THIS IS A STUB!
// //
// ldapjs does not support server side extensible matching. // ldapjs does not support server side extensible matching.
// This class exists only for the client to send them. // This class exists only for the client to send them.
///--- API /// --- API
function ExtensibleFilter(options) { function ExtensibleFilter (options) {
parents.ExtensibleFilter.call(this, options); parents.ExtensibleFilter.call(this, options)
} }
util.inherits(ExtensibleFilter, parents.ExtensibleFilter); util.inherits(ExtensibleFilter, parents.ExtensibleFilter)
Filter.mixin(ExtensibleFilter); Filter.mixin(ExtensibleFilter)
module.exports = ExtensibleFilter; module.exports = ExtensibleFilter
ExtensibleFilter.prototype.parse = function (ber) { ExtensibleFilter.prototype.parse = function (ber) {
var end = ber.offset + ber.length; var end = ber.offset + ber.length
while (ber.offset < end) { while (ber.offset < end) {
var tag = ber.peek(); var tag = ber.peek()
switch (tag) { switch (tag) {
case 0x81: case 0x81:
this.rule = ber.readString(tag); this.rule = ber.readString(tag)
break; break
case 0x82: case 0x82:
this.matchType = ber.readString(tag); this.matchType = ber.readString(tag)
break; break
case 0x83: case 0x83:
this.value = ber.readString(tag); this.value = ber.readString(tag)
break; break
case 0x84: case 0x84:
this.dnAttributes = ber.readBoolean(tag); this.dnAttributes = ber.readBoolean(tag)
break; break
default: default:
throw new Error('Invalid ext_match filter type: 0x' + tag.toString(16)); throw new Error('Invalid ext_match filter type: 0x' + tag.toString(16))
} }
} }
return true; return true
}; }
ExtensibleFilter.prototype._toBer = function (ber) { ExtensibleFilter.prototype._toBer = function (ber) {
assert.ok(ber); assert.ok(ber)
if (this.rule) if (this.rule) { ber.writeString(this.rule, 0x81) }
ber.writeString(this.rule, 0x81); if (this.matchType) { ber.writeString(this.matchType, 0x82) }
if (this.matchType)
ber.writeString(this.matchType, 0x82);
ber.writeString(this.value, 0x83); ber.writeString(this.value, 0x83)
if (this.dnAttributes) if (this.dnAttributes) { ber.writeBoolean(this.dnAttributes, 0x84) }
ber.writeBoolean(this.dnAttributes, 0x84);
return ber; return ber
}; }

View File

@ -1,58 +1,55 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved. // Copyright 2011 Mark Cavage, Inc. All rights reserved.
var assert = require('assert'); // var assert = require('assert')
var asn1 = require('asn1'); var asn1 = require('asn1')
var Protocol = require('../protocol'); var Protocol = require('../protocol')
/// --- Globals
///--- Globals var BerWriter = asn1.BerWriter
var BerWriter = asn1.BerWriter;
var TYPES = { var TYPES = {
'and': Protocol.FILTER_AND, and: Protocol.FILTER_AND,
'or': Protocol.FILTER_OR, or: Protocol.FILTER_OR,
'not': Protocol.FILTER_NOT, not: Protocol.FILTER_NOT,
'equal': Protocol.FILTER_EQUALITY, equal: Protocol.FILTER_EQUALITY,
'substring': Protocol.FILTER_SUBSTRINGS, substring: Protocol.FILTER_SUBSTRINGS,
'ge': Protocol.FILTER_GE, ge: Protocol.FILTER_GE,
'le': Protocol.FILTER_LE, le: Protocol.FILTER_LE,
'present': Protocol.FILTER_PRESENT, present: Protocol.FILTER_PRESENT,
'approx': Protocol.FILTER_APPROX, approx: Protocol.FILTER_APPROX,
'ext': Protocol.FILTER_EXT ext: Protocol.FILTER_EXT
}; }
/// --- API
///--- API function isFilter (filter) {
function isFilter(filter) {
if (!filter || typeof (filter) !== 'object') { if (!filter || typeof (filter) !== 'object') {
return false; return false
} }
// Do our best to duck-type it // Do our best to duck-type it
if (typeof (filter.toBer) === 'function' && if (typeof (filter.toBer) === 'function' &&
typeof (filter.matches) === 'function' && typeof (filter.matches) === 'function' &&
TYPES[filter.type] !== undefined) { TYPES[filter.type] !== undefined) {
return true; return true
} }
return false; return false
} }
function mixin(target) { function mixin (target) {
target.prototype.toBer = function toBer(ber) { target.prototype.toBer = function toBer (ber) {
if (!ber || !(ber instanceof BerWriter)) if (!ber || !(ber instanceof BerWriter)) { throw new TypeError('ber (BerWriter) required') }
throw new TypeError('ber (BerWriter) required');
ber.startSequence(TYPES[this.type]); ber.startSequence(TYPES[this.type])
ber = this._toBer(ber); ber = this._toBer(ber)
ber.endSequence(); ber.endSequence()
return ber; return ber
}; }
} }
module.exports = { module.exports = {
isFilter: isFilter, isFilter: isFilter,
mixin: mixin mixin: mixin
}; }

View File

@ -1,38 +1,35 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved. // Copyright 2011 Mark Cavage, Inc. All rights reserved.
var assert = require('assert'); var assert = require('assert')
var util = require('util'); var util = require('util')
var parents = require('ldap-filter'); var parents = require('ldap-filter')
var Filter = require('./filter'); var Filter = require('./filter')
/// --- API
///--- API function GreaterThanEqualsFilter (options) {
parents.GreaterThanEqualsFilter.call(this, options)
function GreaterThanEqualsFilter(options) {
parents.GreaterThanEqualsFilter.call(this, options);
} }
util.inherits(GreaterThanEqualsFilter, parents.GreaterThanEqualsFilter); util.inherits(GreaterThanEqualsFilter, parents.GreaterThanEqualsFilter)
Filter.mixin(GreaterThanEqualsFilter); Filter.mixin(GreaterThanEqualsFilter)
module.exports = GreaterThanEqualsFilter; module.exports = GreaterThanEqualsFilter
GreaterThanEqualsFilter.prototype.parse = function (ber) { GreaterThanEqualsFilter.prototype.parse = function (ber) {
assert.ok(ber); assert.ok(ber)
this.attribute = ber.readString().toLowerCase(); this.attribute = ber.readString().toLowerCase()
this.value = ber.readString(); this.value = ber.readString()
return true;
};
return true
}
GreaterThanEqualsFilter.prototype._toBer = function (ber) { GreaterThanEqualsFilter.prototype._toBer = function (ber) {
assert.ok(ber); assert.ok(ber)
ber.writeString(this.attribute); ber.writeString(this.attribute)
ber.writeString(this.value); ber.writeString(this.value)
return ber; return ber
}; }

View File

@ -1,33 +1,30 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved. // Copyright 2011 Mark Cavage, Inc. All rights reserved.
var assert = require('assert'); var assert = require('assert')
var asn1 = require('asn1'); var asn1 = require('asn1')
var parents = require('ldap-filter'); var parents = require('ldap-filter')
var Protocol = require('../protocol'); var Protocol = require('../protocol')
var Filter = require('./filter'); var Filter = require('./filter')
var AndFilter = require('./and_filter'); var AndFilter = require('./and_filter')
var ApproximateFilter = require('./approx_filter'); var ApproximateFilter = require('./approx_filter')
var EqualityFilter = require('./equality_filter'); var EqualityFilter = require('./equality_filter')
var ExtensibleFilter = require('./ext_filter'); var ExtensibleFilter = require('./ext_filter')
var GreaterThanEqualsFilter = require('./ge_filter'); var GreaterThanEqualsFilter = require('./ge_filter')
var LessThanEqualsFilter = require('./le_filter'); var LessThanEqualsFilter = require('./le_filter')
var NotFilter = require('./not_filter'); var NotFilter = require('./not_filter')
var OrFilter = require('./or_filter'); var OrFilter = require('./or_filter')
var PresenceFilter = require('./presence_filter'); var PresenceFilter = require('./presence_filter')
var SubstringFilter = require('./substr_filter'); var SubstringFilter = require('./substr_filter')
/// --- Globals
var BerReader = asn1.BerReader
///--- Globals /// --- Internal Parsers
var BerReader = asn1.BerReader;
///--- Internal Parsers
/* /*
* A filter looks like this coming in: * A filter looks like this coming in:
@ -62,132 +59,137 @@ var BerReader = asn1.BerReader;
* dnAttributes [4] BOOLEAN DEFAULT FALSE * dnAttributes [4] BOOLEAN DEFAULT FALSE
* } * }
*/ */
function _parse(ber) { function _parse (ber) {
assert.ok(ber); assert.ok(ber)
function parseSet(f) { function parseSet (f) {
var end = ber.offset + ber.length; var end = ber.offset + ber.length
while (ber.offset < end) while (ber.offset < end) { f.addFilter(_parse(ber)) }
f.addFilter(_parse(ber));
} }
var f; var f
var type = ber.readSequence(); var type = ber.readSequence()
switch (type) { switch (type) {
case Protocol.FILTER_AND:
f = new AndFilter()
parseSet(f)
break
case Protocol.FILTER_AND: case Protocol.FILTER_APPROX:
f = new AndFilter(); f = new ApproximateFilter()
parseSet(f); f.parse(ber)
break; break
case Protocol.FILTER_APPROX: case Protocol.FILTER_EQUALITY:
f = new ApproximateFilter(); f = new EqualityFilter()
f.parse(ber); f.parse(ber)
break; return f
case Protocol.FILTER_EQUALITY: case Protocol.FILTER_EXT:
f = new EqualityFilter(); f = new ExtensibleFilter()
f.parse(ber); f.parse(ber)
return f; return f
case Protocol.FILTER_EXT: case Protocol.FILTER_GE:
f = new ExtensibleFilter(); f = new GreaterThanEqualsFilter()
f.parse(ber); f.parse(ber)
return f; return f
case Protocol.FILTER_GE: case Protocol.FILTER_LE:
f = new GreaterThanEqualsFilter(); f = new LessThanEqualsFilter()
f.parse(ber); f.parse(ber)
return f; return f
case Protocol.FILTER_LE: case Protocol.FILTER_NOT:
f = new LessThanEqualsFilter(); var _f = _parse(ber)
f.parse(ber); f = new NotFilter({
return f; filter: _f
})
break
case Protocol.FILTER_NOT: case Protocol.FILTER_OR:
var _f = _parse(ber); f = new OrFilter()
f = new NotFilter({ parseSet(f)
filter: _f break
});
break;
case Protocol.FILTER_OR: case Protocol.FILTER_PRESENT:
f = new OrFilter(); f = new PresenceFilter()
parseSet(f); f.parse(ber)
break; break
case Protocol.FILTER_PRESENT: case Protocol.FILTER_SUBSTRINGS:
f = new PresenceFilter(); f = new SubstringFilter()
f.parse(ber); f.parse(ber)
break; break
case Protocol.FILTER_SUBSTRINGS: default:
f = new SubstringFilter(); throw new Error('Invalid search filter type: 0x' + type.toString(16))
f.parse(ber);
break;
default:
throw new Error('Invalid search filter type: 0x' + type.toString(16));
} }
assert.ok(f)
assert.ok(f); return f
return f;
} }
function cloneFilter (input) {
function cloneFilter(input) { var child
var child;
if (input.type === 'and' || input.type === 'or') { if (input.type === 'and' || input.type === 'or') {
child = input.filters.map(cloneFilter); child = input.filters.map(cloneFilter)
} else if (input.type === 'not') { } else if (input.type === 'not') {
child = cloneFilter(input.filter); child = cloneFilter(input.filter)
} }
switch (input.type) { switch (input.type) {
case 'and': case 'and':
return new AndFilter({filters: child}); return new AndFilter({ filters: child })
case 'or': case 'or':
return new OrFilter({filters: child}); return new OrFilter({ filters: child })
case 'not': case 'not':
return new NotFilter({filter: child}); return new NotFilter({ filter: child })
case 'equal': case 'equal':
return new EqualityFilter(input); return new EqualityFilter(input)
case 'substring': case 'substring':
return new SubstringFilter(input); return new SubstringFilter(input)
case 'ge': case 'ge':
return new GreaterThanEqualsFilter(input); return new GreaterThanEqualsFilter(input)
case 'le': case 'le':
return new LessThanEqualsFilter(input); return new LessThanEqualsFilter(input)
case 'present': case 'present':
return new PresenceFilter(input); return new PresenceFilter(input)
case 'approx': case 'approx':
return new ApproximateFilter(input); return new ApproximateFilter(input)
case 'ext': case 'ext':
return new ExtensibleFilter(input); return new ExtensibleFilter(input)
default: default:
throw new Error('invalid filter type:' + input.type); throw new Error('invalid filter type:' + input.type)
} }
} }
function escapedToHex (str) {
return str.replace(/\\([0-9a-f](?![0-9a-f])|[^0-9a-f]|$)/gi, function (match, p1) {
if (!p1) {
return '\\5c'
}
function parseString(str) { const hexCode = p1.charCodeAt(0).toString(16)
var generic = parents.parse(str); return '\\' + hexCode
// 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);
} }
function parseString (str) {
const hexStr = escapedToHex(str)
const generic = parents.parse(hexStr)
// 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)
}
///--- API /// --- API
module.exports = { module.exports = {
parse: function (ber) { parse: function (ber) {
if (!ber || !(ber instanceof BerReader)) if (!ber || !(ber instanceof BerReader)) { throw new TypeError('ber (BerReader) required') }
throw new TypeError('ber (BerReader) required');
return _parse(ber); return _parse(ber)
}, },
parseString: parseString, parseString: parseString,
@ -204,4 +206,4 @@ module.exports = {
OrFilter: OrFilter, OrFilter: OrFilter,
PresenceFilter: PresenceFilter, PresenceFilter: PresenceFilter,
SubstringFilter: SubstringFilter SubstringFilter: SubstringFilter
}; }

View File

@ -1,38 +1,35 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved. // Copyright 2011 Mark Cavage, Inc. All rights reserved.
var assert = require('assert'); var assert = require('assert')
var util = require('util'); var util = require('util')
var parents = require('ldap-filter'); var parents = require('ldap-filter')
var Filter = require('./filter'); var Filter = require('./filter')
/// --- API
///--- API function LessThanEqualsFilter (options) {
parents.LessThanEqualsFilter.call(this, options)
function LessThanEqualsFilter(options) {
parents.LessThanEqualsFilter.call(this, options);
} }
util.inherits(LessThanEqualsFilter, parents.LessThanEqualsFilter); util.inherits(LessThanEqualsFilter, parents.LessThanEqualsFilter)
Filter.mixin(LessThanEqualsFilter); Filter.mixin(LessThanEqualsFilter)
module.exports = LessThanEqualsFilter; module.exports = LessThanEqualsFilter
LessThanEqualsFilter.prototype.parse = function (ber) { LessThanEqualsFilter.prototype.parse = function (ber) {
assert.ok(ber); assert.ok(ber)
this.attribute = ber.readString().toLowerCase(); this.attribute = ber.readString().toLowerCase()
this.value = ber.readString(); this.value = ber.readString()
return true;
};
return true
}
LessThanEqualsFilter.prototype._toBer = function (ber) { LessThanEqualsFilter.prototype._toBer = function (ber) {
assert.ok(ber); assert.ok(ber)
ber.writeString(this.attribute); ber.writeString(this.attribute)
ber.writeString(this.value); ber.writeString(this.value)
return ber; return ber
}; }

View File

@ -1,25 +1,23 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved. // Copyright 2011 Mark Cavage, Inc. All rights reserved.
var assert = require('assert'); var assert = require('assert')
var util = require('util'); var util = require('util')
var parents = require('ldap-filter'); var parents = require('ldap-filter')
var Filter = require('./filter'); var Filter = require('./filter')
/// --- API
///--- API function NotFilter (options) {
parents.NotFilter.call(this, options)
function NotFilter(options) {
parents.NotFilter.call(this, options);
} }
util.inherits(NotFilter, parents.NotFilter); util.inherits(NotFilter, parents.NotFilter)
Filter.mixin(NotFilter); Filter.mixin(NotFilter)
module.exports = NotFilter; module.exports = NotFilter
NotFilter.prototype._toBer = function (ber) { NotFilter.prototype._toBer = function (ber) {
assert.ok(ber); assert.ok(ber)
return this.filter.toBer(ber); return this.filter.toBer(ber)
}; }

View File

@ -1,29 +1,27 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved. // Copyright 2011 Mark Cavage, Inc. All rights reserved.
var assert = require('assert'); var assert = require('assert')
var util = require('util'); var util = require('util')
var parents = require('ldap-filter'); var parents = require('ldap-filter')
var Filter = require('./filter'); var Filter = require('./filter')
/// --- API
///--- API function OrFilter (options) {
parents.OrFilter.call(this, options)
function OrFilter(options) {
parents.OrFilter.call(this, options);
} }
util.inherits(OrFilter, parents.OrFilter); util.inherits(OrFilter, parents.OrFilter)
Filter.mixin(OrFilter); Filter.mixin(OrFilter)
module.exports = OrFilter; module.exports = OrFilter
OrFilter.prototype._toBer = function (ber) { OrFilter.prototype._toBer = function (ber) {
assert.ok(ber); assert.ok(ber)
this.filters.forEach(function (f) { this.filters.forEach(function (f) {
ber = f.toBer(ber); ber = f.toBer(ber)
}); })
return ber; return ber
}; }

View File

@ -1,40 +1,36 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved. // Copyright 2011 Mark Cavage, Inc. All rights reserved.
var assert = require('assert'); var assert = require('assert')
var util = require('util'); var util = require('util')
var parents = require('ldap-filter'); var parents = require('ldap-filter')
var Filter = require('./filter'); var Filter = require('./filter')
/// --- API
///--- API function PresenceFilter (options) {
parents.PresenceFilter.call(this, options)
function PresenceFilter(options) {
parents.PresenceFilter.call(this, options);
} }
util.inherits(PresenceFilter, parents.PresenceFilter); util.inherits(PresenceFilter, parents.PresenceFilter)
Filter.mixin(PresenceFilter); Filter.mixin(PresenceFilter)
module.exports = PresenceFilter; module.exports = PresenceFilter
PresenceFilter.prototype.parse = function (ber) { PresenceFilter.prototype.parse = function (ber) {
assert.ok(ber); assert.ok(ber)
this.attribute = this.attribute =
ber.buffer.slice(0, ber.length).toString('utf8').toLowerCase(); ber.buffer.slice(0, ber.length).toString('utf8').toLowerCase()
ber._offset += ber.length; ber._offset += ber.length
return true;
};
return true
}
PresenceFilter.prototype._toBer = function (ber) { PresenceFilter.prototype._toBer = function (ber) {
assert.ok(ber); assert.ok(ber)
for (var i = 0; i < this.attribute.length; i++) for (var i = 0; i < this.attribute.length; i++) { ber.writeByte(this.attribute.charCodeAt(i)) }
ber.writeByte(this.attribute.charCodeAt(i));
return ber; return ber
}; }

View File

@ -1,76 +1,69 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved. // Copyright 2011 Mark Cavage, Inc. All rights reserved.
var assert = require('assert'); var assert = require('assert')
var util = require('util'); var util = require('util')
var parents = require('ldap-filter'); var parents = require('ldap-filter')
var Filter = require('./filter'); var Filter = require('./filter')
/// --- API
///--- API function SubstringFilter (options) {
parents.SubstringFilter.call(this, options)
function SubstringFilter(options) {
parents.SubstringFilter.call(this, options);
} }
util.inherits(SubstringFilter, parents.SubstringFilter); util.inherits(SubstringFilter, parents.SubstringFilter)
Filter.mixin(SubstringFilter); Filter.mixin(SubstringFilter)
module.exports = SubstringFilter; module.exports = SubstringFilter
SubstringFilter.prototype.parse = function (ber) { SubstringFilter.prototype.parse = function (ber) {
assert.ok(ber); assert.ok(ber)
this.attribute = ber.readString().toLowerCase(); this.attribute = ber.readString().toLowerCase()
ber.readSequence(); ber.readSequence()
var end = ber.offset + ber.length; var end = ber.offset + ber.length
while (ber.offset < end) { while (ber.offset < end) {
var tag = ber.peek(); var tag = ber.peek()
switch (tag) { switch (tag) {
case 0x80: // Initial case 0x80: // Initial
this.initial = ber.readString(tag); this.initial = ber.readString(tag)
if (this.attribute === 'objectclass') if (this.attribute === 'objectclass') { this.initial = this.initial.toLowerCase() }
this.initial = this.initial.toLowerCase(); break
break; case 0x81: // Any
case 0x81: // Any var anyVal = ber.readString(tag)
var anyVal = ber.readString(tag); if (this.attribute === 'objectclass') { anyVal = anyVal.toLowerCase() }
if (this.attribute === 'objectclass') this.any.push(anyVal)
anyVal = anyVal.toLowerCase(); break
this.any.push(anyVal); case 0x82: // Final
break; this.final = ber.readString(tag)
case 0x82: // Final if (this.attribute === 'objectclass') { this.final = this.final.toLowerCase() }
this.final = ber.readString(tag); break
if (this.attribute === 'objectclass') default:
this.final = this.final.toLowerCase(); throw new Error('Invalid substrings filter type: 0x' + tag.toString(16))
break;
default:
throw new Error('Invalid substrings filter type: 0x' + tag.toString(16));
} }
} }
return true; return true
}; }
SubstringFilter.prototype._toBer = function (ber) { SubstringFilter.prototype._toBer = function (ber) {
assert.ok(ber); assert.ok(ber)
ber.writeString(this.attribute); ber.writeString(this.attribute)
ber.startSequence(); ber.startSequence()
if (this.initial) if (this.initial) { ber.writeString(this.initial, 0x80) }
ber.writeString(this.initial, 0x80);
if (this.any && this.any.length) if (this.any && this.any.length) {
this.any.forEach(function (s) { this.any.forEach(function (s) {
ber.writeString(s, 0x81); ber.writeString(s, 0x81)
}); })
}
if (this.final) if (this.final) { ber.writeString(this.final, 0x82) }
ber.writeString(this.final, 0x82);
ber.endSequence(); ber.endSequence()
return ber; return ber
}; }

View File

@ -1,24 +1,24 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved. // Copyright 2011 Mark Cavage, Inc. All rights reserved.
var Logger = require('bunyan'); var logger = require('./logger')
var client = require('./client'); var client = require('./client')
var Attribute = require('./attribute'); var Attribute = require('./attribute')
var Change = require('./change'); var Change = require('./change')
var Protocol = require('./protocol'); var Protocol = require('./protocol')
var Server = require('./server'); var Server = require('./server')
var assert = require('assert'); var controls = require('./controls')
var controls = require('./controls'); var persistentSearch = require('./persistent_search')
var persistentSearch = require('./persistent_search'); var dn = require('./dn')
var dn = require('./dn'); var errors = require('./errors')
var errors = require('./errors'); var filters = require('./filters')
var filters = require('./filters'); var messages = require('./messages')
var messages = require('./messages'); var url = require('./url')
var url = require('./url');
const hasOwnProperty = (target, val) => Object.prototype.hasOwnProperty.call(target, val)
///--- API /// --- API
module.exports = { module.exports = {
Client: client.Client, Client: client.Client,
@ -26,21 +26,15 @@ module.exports = {
Server: Server, Server: Server,
createServer: function (options) { createServer: function (options) {
if (options === undefined) if (options === undefined) { options = {} }
options = {};
if (typeof (options) !== 'object') if (typeof (options) !== 'object') { throw new TypeError('options (object) required') }
throw new TypeError('options (object) required');
if (!options.log) { if (!options.log) {
options.log = new Logger({ options.log = logger
name: 'ldapjs',
component: 'client',
stream: process.stderr
});
} }
return new Server(options); return new Server(options)
}, },
Attribute: Attribute, Attribute: Attribute,
@ -59,37 +53,32 @@ module.exports = {
url: url, url: url,
parseURL: url.parse parseURL: url.parse
}; }
/// --- Export all the childrenz
///--- Export all the childrenz var k
var k;
for (k in Protocol) { for (k in Protocol) {
if (Protocol.hasOwnProperty(k)) if (hasOwnProperty(Protocol, k)) { module.exports[k] = Protocol[k] }
module.exports[k] = Protocol[k];
} }
for (k in messages) { for (k in messages) {
if (messages.hasOwnProperty(k)) if (hasOwnProperty(messages, k)) { module.exports[k] = messages[k] }
module.exports[k] = messages[k];
} }
for (k in controls) { for (k in controls) {
if (controls.hasOwnProperty(k)) if (hasOwnProperty(controls, k)) { module.exports[k] = controls[k] }
module.exports[k] = controls[k];
} }
for (k in filters) { for (k in filters) {
if (filters.hasOwnProperty(k)) { if (hasOwnProperty(filters, k)) {
if (k !== 'parse' && k !== 'parseString') if (k !== 'parse' && k !== 'parseString') { module.exports[k] = filters[k] }
module.exports[k] = filters[k];
} }
} }
for (k in errors) { for (k in errors) {
if (errors.hasOwnProperty(k)) { if (hasOwnProperty(errors, k)) {
module.exports[k] = errors[k]; module.exports[k] = errors[k]
} }
} }

6
lib/logger.js Normal file
View File

@ -0,0 +1,6 @@
'use strict'
const logger = Object.create(require('abstract-logging'))
logger.child = function () { return logger }
module.exports = logger

View File

@ -1,90 +1,87 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved. // Copyright 2011 Mark Cavage, Inc. All rights reserved.
var assert = require('assert-plus'); var assert = require('assert-plus')
var util = require('util'); var util = require('util')
var LDAPMessage = require('./message'); var LDAPMessage = require('./message')
var Protocol = require('../protocol'); var Protocol = require('../protocol')
/// --- API
///--- API function AbandonRequest (options) {
options = options || {}
assert.object(options)
assert.optionalNumber(options.abandonID)
function AbandonRequest(options) { options.protocolOp = Protocol.LDAP_REQ_ABANDON
options = options || {}; LDAPMessage.call(this, options)
assert.object(options);
assert.optionalNumber(options.abandonID);
options.protocolOp = Protocol.LDAP_REQ_ABANDON; this.abandonID = options.abandonID || 0
LDAPMessage.call(this, options);
this.abandonID = options.abandonID || 0;
} }
util.inherits(AbandonRequest, LDAPMessage); util.inherits(AbandonRequest, LDAPMessage)
Object.defineProperties(AbandonRequest.prototype, { Object.defineProperties(AbandonRequest.prototype, {
type: { type: {
get: function getType() { return 'AbandonRequest'; }, get: function getType () { return 'AbandonRequest' },
configurable: false configurable: false
} }
}); })
AbandonRequest.prototype._parse = function (ber, length) { AbandonRequest.prototype._parse = function (ber, length) {
assert.ok(ber); assert.ok(ber)
assert.ok(length); assert.ok(length)
// What a PITA - have to replicate ASN.1 integer logic to work around the // 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" // way abandon is encoded and the way ldapjs framework handles "normal"
// messages // messages
var buf = ber.buffer; var buf = ber.buffer
var offset = 0; var offset = 0
var value = 0; var value = 0
var fb = buf[offset++]; var fb = buf[offset++]
value = fb & 0x7F; value = fb & 0x7F
for (var i = 1; i < length; i++) { for (var i = 1; i < length; i++) {
value <<= 8; value <<= 8
value |= (buf[offset++] & 0xff); value |= (buf[offset++] & 0xff)
} }
if ((fb & 0x80) == 0x80) if ((fb & 0x80) === 0x80) { value = -value }
value = -value;
ber._offset += length; ber._offset += length
this.abandonID = value; this.abandonID = value
return true; return true
}; }
AbandonRequest.prototype._toBer = function (ber) { AbandonRequest.prototype._toBer = function (ber) {
assert.ok(ber); assert.ok(ber)
var i = this.abandonID; var i = this.abandonID
var sz = 4; var sz = 4
while ((((i & 0xff800000) === 0) || ((i & 0xff800000) === 0xff800000)) && while ((((i & 0xff800000) === 0) || ((i & 0xff800000) === 0xff800000)) &&
(sz > 1)) { (sz > 1)) {
sz--; sz--
i <<= 8; i <<= 8
} }
assert.ok(sz <= 4); assert.ok(sz <= 4)
while (sz-- > 0) { while (sz-- > 0) {
ber.writeByte((i & 0xff000000) >> 24); ber.writeByte((i & 0xff000000) >> 24)
i <<= 8; i <<= 8
} }
return ber; return ber
}; }
AbandonRequest.prototype._json = function (j) { AbandonRequest.prototype._json = function (j) {
assert.ok(j); assert.ok(j)
j.abandonID = this.abandonID; j.abandonID = this.abandonID
return j; return j
}; }
/// --- Exports
///--- Exports module.exports = AbandonRequest
module.exports = AbandonRequest;

View File

@ -1,36 +1,34 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved. // Copyright 2011 Mark Cavage, Inc. All rights reserved.
var assert = require('assert-plus'); var assert = require('assert-plus')
var util = require('util'); var util = require('util')
var LDAPMessage = require('./result'); var LDAPMessage = require('./result')
var Protocol = require('../protocol'); // var Protocol = require('../protocol')
/// --- API
///--- API function AbandonResponse (options) {
options = options || {}
assert.object(options)
function AbandonResponse(options) { options.protocolOp = 0
options = options || {}; LDAPMessage.call(this, options)
assert.object(options);
options.protocolOp = 0;
LDAPMessage.call(this, options);
} }
util.inherits(AbandonResponse, LDAPMessage); util.inherits(AbandonResponse, LDAPMessage)
Object.defineProperties(AbandonResponse.prototype, { Object.defineProperties(AbandonResponse.prototype, {
type: { type: {
get: function getType() { return 'AbandonResponse'; }, get: function getType () { return 'AbandonResponse' },
configurable: false configurable: false
} }
}); })
AbandonResponse.prototype.end = function (status) {}; AbandonResponse.prototype.end = function (status) {}
AbandonResponse.prototype._json = function (j) { AbandonResponse.prototype._json = function (j) {
return j; return j
}; }
/// --- Exports
///--- Exports module.exports = AbandonResponse
module.exports = AbandonResponse;

View File

@ -1,130 +1,122 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved. // Copyright 2011 Mark Cavage, Inc. All rights reserved.
var assert = require('assert-plus'); var assert = require('assert-plus')
var util = require('util'); var util = require('util')
var LDAPMessage = require('./message'); var LDAPMessage = require('./message')
var Attribute = require('../attribute'); var Attribute = require('../attribute')
var Protocol = require('../protocol'); var Protocol = require('../protocol')
var lassert = require('../assert'); var lassert = require('../assert')
/// --- API
///--- API function AddRequest (options) {
options = options || {}
assert.object(options)
lassert.optionalStringDN(options.entry)
lassert.optionalArrayOfAttribute(options.attributes)
function AddRequest(options) { options.protocolOp = Protocol.LDAP_REQ_ADD
options = options || {}; LDAPMessage.call(this, options)
assert.object(options);
lassert.optionalStringDN(options.entry);
lassert.optionalArrayOfAttribute(options.attributes);
options.protocolOp = Protocol.LDAP_REQ_ADD; this.entry = options.entry || null
LDAPMessage.call(this, options); this.attributes = options.attributes ? options.attributes.slice(0) : []
this.entry = options.entry || null;
this.attributes = options.attributes ? options.attributes.slice(0) : [];
} }
util.inherits(AddRequest, LDAPMessage); util.inherits(AddRequest, LDAPMessage)
Object.defineProperties(AddRequest.prototype, { Object.defineProperties(AddRequest.prototype, {
type: { type: {
get: function getType() { return 'AddRequest'; }, get: function getType () { return 'AddRequest' },
configurable: false configurable: false
}, },
_dn: { _dn: {
get: function getDN() { return this.entry; }, get: function getDN () { return this.entry },
configurable: false configurable: false
} }
}); })
AddRequest.prototype._parse = function (ber) { AddRequest.prototype._parse = function (ber) {
assert.ok(ber); assert.ok(ber)
this.entry = ber.readString(); this.entry = ber.readString()
ber.readSequence(); ber.readSequence()
var end = ber.offset + ber.length; var end = ber.offset + ber.length
while (ber.offset < end) { while (ber.offset < end) {
var a = new Attribute(); var a = new Attribute()
a.parse(ber); a.parse(ber)
a.type = a.type.toLowerCase(); a.type = a.type.toLowerCase()
if (a.type === 'objectclass') { if (a.type === 'objectclass') {
for (var i = 0; i < a.vals.length; i++) for (var i = 0; i < a.vals.length; i++) { a.vals[i] = a.vals[i].toLowerCase() }
a.vals[i] = a.vals[i].toLowerCase();
} }
this.attributes.push(a); this.attributes.push(a)
} }
this.attributes.sort(Attribute.compare); this.attributes.sort(Attribute.compare)
return true; return true
}; }
AddRequest.prototype._toBer = function (ber) { AddRequest.prototype._toBer = function (ber) {
assert.ok(ber); assert.ok(ber)
ber.writeString(this.entry.toString()); ber.writeString(this.entry.toString())
ber.startSequence(); ber.startSequence()
this.attributes.forEach(function (a) { this.attributes.forEach(function (a) {
a.toBer(ber); a.toBer(ber)
}); })
ber.endSequence(); ber.endSequence()
return ber; return ber
}; }
AddRequest.prototype._json = function (j) { AddRequest.prototype._json = function (j) {
assert.ok(j); assert.ok(j)
j.entry = this.entry.toString(); j.entry = this.entry.toString()
j.attributes = []; j.attributes = []
this.attributes.forEach(function (a) { this.attributes.forEach(function (a) {
j.attributes.push(a.json); j.attributes.push(a.json)
}); })
return j; return j
}; }
AddRequest.prototype.indexOf = function (attr) { AddRequest.prototype.indexOf = function (attr) {
if (!attr || typeof (attr) !== 'string') if (!attr || typeof (attr) !== 'string') { throw new TypeError('attr (string) required') }
throw new TypeError('attr (string) required');
for (var i = 0; i < this.attributes.length; i++) { for (var i = 0; i < this.attributes.length; i++) {
if (this.attributes[i].type === attr) if (this.attributes[i].type === attr) { return i }
return i;
} }
return -1; return -1
}; }
AddRequest.prototype.attributeNames = function () { AddRequest.prototype.attributeNames = function () {
var attrs = []; var attrs = []
for (var i = 0; i < this.attributes.length; i++) for (var i = 0; i < this.attributes.length; i++) { attrs.push(this.attributes[i].type.toLowerCase()) }
attrs.push(this.attributes[i].type.toLowerCase());
return attrs; return attrs
}; }
AddRequest.prototype.getAttribute = function (name) { AddRequest.prototype.getAttribute = function (name) {
if (!name || typeof (name) !== 'string') if (!name || typeof (name) !== 'string') { throw new TypeError('attribute name (string) required') }
throw new TypeError('attribute name (string) required');
name = name.toLowerCase(); name = name.toLowerCase()
for (var i = 0; i < this.attributes.length; i++) { for (var i = 0; i < this.attributes.length; i++) {
if (this.attributes[i].type === name) if (this.attributes[i].type === name) { return this.attributes[i] }
return this.attributes[i];
} }
return null; return null
}; }
AddRequest.prototype.addAttribute = function (attr) { AddRequest.prototype.addAttribute = function (attr) {
if (!(attr instanceof Attribute)) if (!(attr instanceof Attribute)) { throw new TypeError('attribute (Attribute) required') }
throw new TypeError('attribute (Attribute) required');
return this.attributes.push(attr); return this.attributes.push(attr)
}; }
/** /**
* Returns a "pure" JS representation of this object. * Returns a "pure" JS representation of this object.
@ -142,30 +134,26 @@ AddRequest.prototype.addAttribute = function (attr) {
* @return {Object} that looks like the above. * @return {Object} that looks like the above.
*/ */
AddRequest.prototype.toObject = function () { AddRequest.prototype.toObject = function () {
var self = this; var self = this
var obj = { var obj = {
dn: self.entry ? self.entry.toString() : '', dn: self.entry ? self.entry.toString() : '',
attributes: {} attributes: {}
}; }
if (!this.attributes || !this.attributes.length) if (!this.attributes || !this.attributes.length) { return obj }
return obj;
this.attributes.forEach(function (a) { this.attributes.forEach(function (a) {
if (!obj.attributes[a.type]) if (!obj.attributes[a.type]) { obj.attributes[a.type] = [] }
obj.attributes[a.type] = [];
a.vals.forEach(function (v) { a.vals.forEach(function (v) {
if (obj.attributes[a.type].indexOf(v) === -1) if (obj.attributes[a.type].indexOf(v) === -1) { obj.attributes[a.type].push(v) }
obj.attributes[a.type].push(v); })
}); })
});
return obj; return obj
}; }
/// --- Exports
///--- Exports module.exports = AddRequest
module.exports = AddRequest;

View File

@ -1,24 +1,22 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved. // Copyright 2011 Mark Cavage, Inc. All rights reserved.
var assert = require('assert-plus'); var assert = require('assert-plus')
var util = require('util'); var util = require('util')
var LDAPResult = require('./result'); var LDAPResult = require('./result')
var Protocol = require('../protocol'); var Protocol = require('../protocol')
/// --- API
///--- API function AddResponse (options) {
options = options || {}
assert.object(options)
function AddResponse(options) { options.protocolOp = Protocol.LDAP_REP_ADD
options = options || {}; LDAPResult.call(this, options)
assert.object(options);
options.protocolOp = Protocol.LDAP_REP_ADD;
LDAPResult.call(this, options);
} }
util.inherits(AddResponse, LDAPResult); util.inherits(AddResponse, LDAPResult)
/// --- Exports
///--- Exports module.exports = AddResponse
module.exports = AddResponse;

View File

@ -1,88 +1,84 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved. // Copyright 2011 Mark Cavage, Inc. All rights reserved.
var assert = require('assert-plus'); var assert = require('assert-plus')
var util = require('util'); var util = require('util')
var asn1 = require('asn1'); var asn1 = require('asn1')
var LDAPMessage = require('./message'); var LDAPMessage = require('./message')
var Protocol = require('../protocol'); var Protocol = require('../protocol')
/// --- Globals
///--- Globals var Ber = asn1.Ber
var LDAP_BIND_SIMPLE = 'simple'
// var LDAP_BIND_SASL = 'sasl'
var Ber = asn1.Ber; /// --- API
var LDAP_BIND_SIMPLE = 'simple';
var LDAP_BIND_SASL = 'sasl';
function BindRequest (options) {
options = options || {}
assert.object(options)
///--- API options.protocolOp = Protocol.LDAP_REQ_BIND
LDAPMessage.call(this, options)
function BindRequest(options) { this.version = options.version || 0x03
options = options || {}; this.name = options.name || null
assert.object(options); this.authentication = options.authentication || LDAP_BIND_SIMPLE
this.credentials = options.credentials || ''
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 || '';
} }
util.inherits(BindRequest, LDAPMessage); util.inherits(BindRequest, LDAPMessage)
Object.defineProperties(BindRequest.prototype, { Object.defineProperties(BindRequest.prototype, {
type: { type: {
get: function getType() { return 'BindRequest'; }, get: function getType () { return 'BindRequest' },
configurable: false configurable: false
}, },
_dn: { _dn: {
get: function getDN() { return this.name; }, get: function getDN () { return this.name },
configurable: false configurable: false
} }
}); })
BindRequest.prototype._parse = function (ber) { BindRequest.prototype._parse = function (ber) {
assert.ok(ber); assert.ok(ber)
this.version = ber.readInt(); this.version = ber.readInt()
this.name = ber.readString(); this.name = ber.readString()
var t = ber.peek(); var t = ber.peek()
// TODO add support for SASL et al // TODO add support for SASL et al
if (t !== Ber.Context) if (t !== Ber.Context) { throw new Error('authentication 0x' + t.toString(16) + ' not supported') }
throw new Error('authentication 0x' + t.toString(16) + ' not supported');
this.authentication = LDAP_BIND_SIMPLE; this.authentication = LDAP_BIND_SIMPLE
this.credentials = ber.readString(Ber.Context); this.credentials = ber.readString(Ber.Context)
return true; return true
}; }
BindRequest.prototype._toBer = function (ber) { BindRequest.prototype._toBer = function (ber) {
assert.ok(ber); assert.ok(ber)
ber.writeInt(this.version); ber.writeInt(this.version)
ber.writeString((this.name || '').toString()); ber.writeString((this.name || '').toString())
// TODO add support for SASL et al // TODO add support for SASL et al
ber.writeString((this.credentials || ''), Ber.Context); ber.writeString((this.credentials || ''), Ber.Context)
return ber; return ber
}; }
BindRequest.prototype._json = function (j) { BindRequest.prototype._json = function (j) {
assert.ok(j); assert.ok(j)
j.version = this.version; j.version = this.version
j.name = this.name; j.name = this.name
j.authenticationType = this.authentication; j.authenticationType = this.authentication
j.credentials = this.credentials; j.credentials = this.credentials
return j; return j
}; }
/// --- Exports
///--- Exports module.exports = BindRequest
module.exports = BindRequest;

View File

@ -1,24 +1,22 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved. // Copyright 2011 Mark Cavage, Inc. All rights reserved.
var assert = require('assert-plus'); var assert = require('assert-plus')
var util = require('util'); var util = require('util')
var LDAPResult = require('./result'); var LDAPResult = require('./result')
var Protocol = require('../protocol'); var Protocol = require('../protocol')
/// --- API
///--- API function BindResponse (options) {
options = options || {}
assert.object(options)
function BindResponse(options) { options.protocolOp = Protocol.LDAP_REP_BIND
options = options || {}; LDAPResult.call(this, options)
assert.object(options);
options.protocolOp = Protocol.LDAP_REP_BIND;
LDAPResult.call(this, options);
} }
util.inherits(BindResponse, LDAPResult); util.inherits(BindResponse, LDAPResult)
/// --- Exports
///--- Exports module.exports = BindResponse
module.exports = BindResponse;

View File

@ -1,76 +1,74 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved. // Copyright 2011 Mark Cavage, Inc. All rights reserved.
var assert = require('assert-plus'); var assert = require('assert-plus')
var util = require('util'); var util = require('util')
var LDAPMessage = require('./message'); var LDAPMessage = require('./message')
var Protocol = require('../protocol'); var Protocol = require('../protocol')
var lassert = require('../assert'); var lassert = require('../assert')
/// --- API
///--- API function CompareRequest (options) {
options = options || {}
assert.object(options)
assert.optionalString(options.attribute)
assert.optionalString(options.value)
lassert.optionalStringDN(options.entry)
function CompareRequest(options) { options.protocolOp = Protocol.LDAP_REQ_COMPARE
options = options || {}; LDAPMessage.call(this, options)
assert.object(options);
assert.optionalString(options.attribute);
assert.optionalString(options.value);
lassert.optionalStringDN(options.entry);
options.protocolOp = Protocol.LDAP_REQ_COMPARE; this.entry = options.entry || null
LDAPMessage.call(this, options); this.attribute = options.attribute || ''
this.value = options.value || ''
this.entry = options.entry || null;
this.attribute = options.attribute || '';
this.value = options.value || '';
} }
util.inherits(CompareRequest, LDAPMessage); util.inherits(CompareRequest, LDAPMessage)
Object.defineProperties(CompareRequest.prototype, { Object.defineProperties(CompareRequest.prototype, {
type: { type: {
get: function getType() { return 'CompareRequest'; }, get: function getType () { return 'CompareRequest' },
configurable: false configurable: false
}, },
_dn: { _dn: {
get: function getDN() { return this.entry; }, get: function getDN () { return this.entry },
configurable: false configurable: false
} }
}); })
CompareRequest.prototype._parse = function (ber) { CompareRequest.prototype._parse = function (ber) {
assert.ok(ber); assert.ok(ber)
this.entry = ber.readString(); this.entry = ber.readString()
ber.readSequence(); ber.readSequence()
this.attribute = ber.readString().toLowerCase(); this.attribute = ber.readString().toLowerCase()
this.value = ber.readString(); this.value = ber.readString()
return true; return true
}; }
CompareRequest.prototype._toBer = function (ber) { CompareRequest.prototype._toBer = function (ber) {
assert.ok(ber); assert.ok(ber)
ber.writeString(this.entry.toString()); ber.writeString(this.entry.toString())
ber.startSequence(); ber.startSequence()
ber.writeString(this.attribute); ber.writeString(this.attribute)
ber.writeString(this.value); ber.writeString(this.value)
ber.endSequence(); ber.endSequence()
return ber; return ber
}; }
CompareRequest.prototype._json = function (j) { CompareRequest.prototype._json = function (j) {
assert.ok(j); assert.ok(j)
j.entry = this.entry.toString(); j.entry = this.entry.toString()
j.attribute = this.attribute; j.attribute = this.attribute
j.value = this.value; j.value = this.value
return j; return j
}; }
/// --- Exports
///--- Exports module.exports = CompareRequest
module.exports = CompareRequest;

View File

@ -1,36 +1,33 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved. // Copyright 2011 Mark Cavage, Inc. All rights reserved.
var assert = require('assert-plus'); var assert = require('assert-plus')
var util = require('util'); var util = require('util')
var LDAPResult = require('./result'); var LDAPResult = require('./result')
var Protocol = require('../protocol'); var Protocol = require('../protocol')
/// --- API
///--- API function CompareResponse (options) {
options = options || {}
assert.object(options)
function CompareResponse(options) { options.protocolOp = Protocol.LDAP_REP_COMPARE
options = options || {}; LDAPResult.call(this, options)
assert.object(options);
options.protocolOp = Protocol.LDAP_REP_COMPARE;
LDAPResult.call(this, options);
} }
util.inherits(CompareResponse, LDAPResult); util.inherits(CompareResponse, LDAPResult)
CompareResponse.prototype.end = function (matches) { CompareResponse.prototype.end = function (matches) {
var status = 0x06; var status = 0x06
if (typeof (matches) === 'boolean') { if (typeof (matches) === 'boolean') {
if (!matches) if (!matches) { status = 0x05 } // Compare false
status = 0x05; // Compare false
} else { } else {
status = matches; status = matches
} }
return LDAPResult.prototype.end.call(this, status); return LDAPResult.prototype.end.call(this, status)
}; }
/// --- Exports
///--- Exports module.exports = CompareResponse
module.exports = CompareResponse;

View File

@ -1,65 +1,62 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved. // Copyright 2011 Mark Cavage, Inc. All rights reserved.
var assert = require('assert-plus'); var assert = require('assert-plus')
var util = require('util'); var util = require('util')
var LDAPMessage = require('./message'); var LDAPMessage = require('./message')
var Protocol = require('../protocol'); var Protocol = require('../protocol')
var lassert = require('../assert'); var lassert = require('../assert')
/// --- API
///--- API function DeleteRequest (options) {
options = options || {}
assert.object(options)
lassert.optionalStringDN(options.entry)
function DeleteRequest(options) { options.protocolOp = Protocol.LDAP_REQ_DELETE
options = options || {}; LDAPMessage.call(this, options)
assert.object(options);
lassert.optionalStringDN(options.entry);
options.protocolOp = Protocol.LDAP_REQ_DELETE; this.entry = options.entry || null
LDAPMessage.call(this, options);
this.entry = options.entry || null;
} }
util.inherits(DeleteRequest, LDAPMessage); util.inherits(DeleteRequest, LDAPMessage)
Object.defineProperties(DeleteRequest.prototype, { Object.defineProperties(DeleteRequest.prototype, {
type: { type: {
get: function getType() { return 'DeleteRequest'; }, get: function getType () { return 'DeleteRequest' },
configurable: false configurable: false
}, },
_dn: { _dn: {
get: function getDN() { return this.entry; }, get: function getDN () { return this.entry },
configurable: false configurable: false
} }
}); })
DeleteRequest.prototype._parse = function (ber, length) { DeleteRequest.prototype._parse = function (ber, length) {
assert.ok(ber); assert.ok(ber)
this.entry = ber.buffer.slice(0, length).toString('utf8'); this.entry = ber.buffer.slice(0, length).toString('utf8')
ber._offset += ber.length; ber._offset += ber.length
return true; return true
}; }
DeleteRequest.prototype._toBer = function (ber) { DeleteRequest.prototype._toBer = function (ber) {
assert.ok(ber); assert.ok(ber)
var buf = new Buffer(this.entry.toString()); var buf = Buffer.from(this.entry.toString())
for (var i = 0; i < buf.length; i++) for (var i = 0; i < buf.length; i++) { ber.writeByte(buf[i]) }
ber.writeByte(buf[i]);
return ber; return ber
}; }
DeleteRequest.prototype._json = function (j) { DeleteRequest.prototype._json = function (j) {
assert.ok(j); assert.ok(j)
j.entry = this.entry; j.entry = this.entry
return j; return j
}; }
/// --- Exports
///--- Exports module.exports = DeleteRequest
module.exports = DeleteRequest;

View File

@ -1,24 +1,22 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved. // Copyright 2011 Mark Cavage, Inc. All rights reserved.
var assert = require('assert-plus'); var assert = require('assert-plus')
var util = require('util'); var util = require('util')
var LDAPResult = require('./result'); var LDAPResult = require('./result')
var Protocol = require('../protocol'); var Protocol = require('../protocol')
/// --- API
///--- API function DeleteResponse (options) {
options = options || {}
assert.object(options)
function DeleteResponse(options) { options.protocolOp = Protocol.LDAP_REP_DELETE
options = options || {}; LDAPResult.call(this, options)
assert.object(options);
options.protocolOp = Protocol.LDAP_REP_DELETE;
LDAPResult.call(this, options);
} }
util.inherits(DeleteResponse, LDAPResult); util.inherits(DeleteResponse, LDAPResult)
/// --- Exports
///--- Exports module.exports = DeleteResponse
module.exports = DeleteResponse;

View File

@ -1,98 +1,116 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved. // Copyright 2011 Mark Cavage, Inc. All rights reserved.
var assert = require('assert-plus'); var assert = require('assert-plus')
var util = require('util'); var util = require('util')
var LDAPMessage = require('./message'); var LDAPMessage = require('./message')
var Protocol = require('../protocol'); var Protocol = require('../protocol')
/// --- API
///--- API function ExtendedRequest (options) {
options = options || {}
function ExtendedRequest(options) { assert.object(options)
options = options || {}; assert.optionalString(options.requestName)
assert.object(options);
assert.optionalString(options.requestName);
if (options.requestValue && if (options.requestValue &&
!(Buffer.isBuffer(options.requestValue) || !(Buffer.isBuffer(options.requestValue) ||
typeof (options.requestValue) === 'string')) { typeof (options.requestValue) === 'string')) {
throw new TypeError('options.requestValue must be a buffer or a string'); throw new TypeError('options.requestValue must be a buffer or a string')
} }
options.protocolOp = Protocol.LDAP_REQ_EXTENSION; options.protocolOp = Protocol.LDAP_REQ_EXTENSION
LDAPMessage.call(this, options); LDAPMessage.call(this, options)
this.requestName = options.requestName || ''; this.requestName = options.requestName || ''
this.requestValue = options.requestValue; this.requestValue = options.requestValue
if (Buffer.isBuffer(this.requestValue)) {
this.requestValueBuffer = this.requestValue
} else {
this.requestValueBuffer = Buffer.from(this.requestValue || '', 'utf8')
}
} }
util.inherits(ExtendedRequest, LDAPMessage); util.inherits(ExtendedRequest, LDAPMessage)
Object.defineProperties(ExtendedRequest.prototype, { Object.defineProperties(ExtendedRequest.prototype, {
type: { type: {
get: function getType() { return 'ExtendedRequest'; }, get: function getType () { return 'ExtendedRequest' },
configurable: false configurable: false
}, },
_dn: { _dn: {
get: function getDN() { return this.requestName; }, get: function getDN () { return this.requestName },
configurable: false configurable: false
}, },
name: { name: {
get: function getName() { return this.requestName; }, get: function getName () { return this.requestName },
set: function setName(val) { set: function setName (val) {
assert.string(val); assert.string(val)
this.requestName = val; this.requestName = val
}, },
configurable: false configurable: false
}, },
value: { value: {
get: function getValue() { return this.requestValue; }, get: function getValue () { return this.requestValue },
set: function setValue(val) { set: function setValue (val) {
if (!(Buffer.isBuffer(val) || typeof (val) === 'string')) if (!(Buffer.isBuffer(val) || typeof (val) === 'string')) { throw new TypeError('value must be a buffer or a string') }
throw new TypeError('value must be a buffer or a string');
this.requestValue = val; if (Buffer.isBuffer(val)) {
this.requestValueBuffer = val
} else {
this.requestValueBuffer = Buffer.from(val, 'utf8')
}
this.requestValue = val
},
configurable: false
},
valueBuffer: {
get: function getValueBuffer () {
return this.requestValueBuffer
},
set: function setValueBuffer (val) {
if (!Buffer.isBuffer(val)) { throw new TypeError('valueBuffer must be a buffer') }
this.value = val
}, },
configurable: false configurable: false
} }
}); })
ExtendedRequest.prototype._parse = function (ber) { ExtendedRequest.prototype._parse = function (ber) {
assert.ok(ber); assert.ok(ber)
this.requestName = ber.readString(0x80); this.requestName = ber.readString(0x80)
if (ber.peek() === 0x81) if (ber.peek() === 0x81) {
try { this.requestValueBuffer = ber.readString(0x81, true)
this.requestValue = ber.readString(0x81); this.requestValue = this.requestValueBuffer.toString('utf8')
} catch (e) {
this.requestValue = ber.readBuffer(0x81);
}
return true;
};
ExtendedRequest.prototype._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; return true
}; }
ExtendedRequest.prototype._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
}
ExtendedRequest.prototype._json = function (j) { ExtendedRequest.prototype._json = function (j) {
assert.ok(j); assert.ok(j)
j.requestName = this.requestName; j.requestName = this.requestName
j.requestValue = (Buffer.isBuffer(this.requestValue)) ? j.requestValue = (Buffer.isBuffer(this.requestValue))
this.requestValue.toString('hex') : this.requestValue; ? this.requestValue.toString('hex') : this.requestValue
return j; return j
}; }
/// --- Exports
///--- Exports module.exports = ExtendedRequest
module.exports = ExtendedRequest;

View File

@ -1,94 +1,86 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved. // Copyright 2011 Mark Cavage, Inc. All rights reserved.
var assert = require('assert-plus'); var assert = require('assert-plus')
var util = require('util'); var util = require('util')
var LDAPResult = require('./result'); var LDAPResult = require('./result')
var Protocol = require('../protocol'); var Protocol = require('../protocol')
/// --- API
///--- API function ExtendedResponse (options) {
options = options || {}
assert.object(options)
assert.optionalString(options.responseName)
assert.optionalString(options.responsevalue)
function ExtendedResponse(options) { this.responseName = options.responseName || undefined
options = options || {}; this.responseValue = options.responseValue || undefined
assert.object(options);
assert.optionalString(options.responseName);
assert.optionalString(options.responsevalue);
this.responseName = options.responseName || undefined; options.protocolOp = Protocol.LDAP_REP_EXTENSION
this.responseValue = options.responseValue || undefined; LDAPResult.call(this, options)
options.protocolOp = Protocol.LDAP_REP_EXTENSION;
LDAPResult.call(this, options);
} }
util.inherits(ExtendedResponse, LDAPResult); util.inherits(ExtendedResponse, LDAPResult)
Object.defineProperties(ExtendedResponse.prototype, { Object.defineProperties(ExtendedResponse.prototype, {
type: { type: {
get: function getType() { return 'ExtendedResponse'; }, get: function getType () { return 'ExtendedResponse' },
configurable: false configurable: false
}, },
_dn: { _dn: {
get: function getDN() { return this.responseName; }, get: function getDN () { return this.responseName },
configurable: false configurable: false
}, },
name: { name: {
get: function getName() { return this.responseName; }, get: function getName () { return this.responseName },
set: function setName(val) { set: function setName (val) {
assert.string(val); assert.string(val)
this.responseName = val; this.responseName = val
}, },
configurable: false configurable: false
}, },
value: { value: {
get: function getValue() { return this.responseValue; }, get: function getValue () { return this.responseValue },
set: function (val) { set: function (val) {
assert.string(val); assert.string(val)
this.responseValue = val; this.responseValue = val
}, },
configurable: false configurable: false
} }
}); })
ExtendedResponse.prototype._parse = function (ber) { ExtendedResponse.prototype._parse = function (ber) {
assert.ok(ber); assert.ok(ber)
if (!LDAPResult.prototype._parse.call(this, ber)) if (!LDAPResult.prototype._parse.call(this, ber)) { return false }
return false;
if (ber.peek() === 0x8a) if (ber.peek() === 0x8a) { this.responseName = ber.readString(0x8a) }
this.responseName = ber.readString(0x8a); if (ber.peek() === 0x8b) { this.responseValue = ber.readString(0x8b) }
if (ber.peek() === 0x8b)
this.responseValue = ber.readString(0x8b);
return true; return true
}; }
ExtendedResponse.prototype._toBer = function (ber) { ExtendedResponse.prototype._toBer = function (ber) {
assert.ok(ber); assert.ok(ber)
if (!LDAPResult.prototype._toBer.call(this, ber)) if (!LDAPResult.prototype._toBer.call(this, ber)) { return false }
return false;
if (this.responseName) if (this.responseName) { ber.writeString(this.responseName, 0x8a) }
ber.writeString(this.responseName, 0x8a); if (this.responseValue) { ber.writeString(this.responseValue, 0x8b) }
if (this.responseValue)
ber.writeString(this.responseValue, 0x8b);
return ber; return ber
}; }
ExtendedResponse.prototype._json = function (j) { ExtendedResponse.prototype._json = function (j) {
assert.ok(j); assert.ok(j)
j = LDAPResult.prototype._json.call(this, j); j = LDAPResult.prototype._json.call(this, j)
j.responseName = this.responseName; j.responseName = this.responseName
j.responseValue = this.responseValue; j.responseValue = this.responseValue
return j; return j
}; }
/// --- Exports
///--- Exports module.exports = ExtendedResponse
module.exports = ExtendedResponse;

View File

@ -1,34 +1,33 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved. // Copyright 2011 Mark Cavage, Inc. All rights reserved.
var LDAPMessage = require('./message'); var LDAPMessage = require('./message')
var LDAPResult = require('./result'); var LDAPResult = require('./result')
var Parser = require('./parser'); var Parser = require('./parser')
var AbandonRequest = require('./abandon_request'); var AbandonRequest = require('./abandon_request')
var AbandonResponse = require('./abandon_response'); var AbandonResponse = require('./abandon_response')
var AddRequest = require('./add_request'); var AddRequest = require('./add_request')
var AddResponse = require('./add_response'); var AddResponse = require('./add_response')
var BindRequest = require('./bind_request'); var BindRequest = require('./bind_request')
var BindResponse = require('./bind_response'); var BindResponse = require('./bind_response')
var CompareRequest = require('./compare_request'); var CompareRequest = require('./compare_request')
var CompareResponse = require('./compare_response'); var CompareResponse = require('./compare_response')
var DeleteRequest = require('./del_request'); var DeleteRequest = require('./del_request')
var DeleteResponse = require('./del_response'); var DeleteResponse = require('./del_response')
var ExtendedRequest = require('./ext_request'); var ExtendedRequest = require('./ext_request')
var ExtendedResponse = require('./ext_response'); var ExtendedResponse = require('./ext_response')
var ModifyRequest = require('./modify_request'); var ModifyRequest = require('./modify_request')
var ModifyResponse = require('./modify_response'); var ModifyResponse = require('./modify_response')
var ModifyDNRequest = require('./moddn_request'); var ModifyDNRequest = require('./moddn_request')
var ModifyDNResponse = require('./moddn_response'); var ModifyDNResponse = require('./moddn_response')
var SearchRequest = require('./search_request'); var SearchRequest = require('./search_request')
var SearchEntry = require('./search_entry'); var SearchEntry = require('./search_entry')
var SearchReference = require('./search_reference'); var SearchReference = require('./search_reference')
var SearchResponse = require('./search_response'); var SearchResponse = require('./search_response')
var UnbindRequest = require('./unbind_request'); var UnbindRequest = require('./unbind_request')
var UnbindResponse = require('./unbind_response'); var UnbindResponse = require('./unbind_response')
/// --- API
///--- API
module.exports = { module.exports = {
@ -59,4 +58,4 @@ module.exports = {
UnbindRequest: UnbindRequest, UnbindRequest: UnbindRequest,
UnbindResponse: UnbindResponse UnbindResponse: UnbindResponse
}; }

View File

@ -1,50 +1,48 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved. // Copyright 2011 Mark Cavage, Inc. All rights reserved.
var assert = require('assert-plus'); var assert = require('assert-plus')
var util = require('util'); var util = require('util')
var asn1 = require('asn1'); var asn1 = require('asn1')
var Control = require('../controls').Control; var logger = require('../logger')
var Protocol = require('../protocol'); // var Control = require('../controls').Control
// var Protocol = require('../protocol')
/// --- Globals
///--- Globals // var Ber = asn1.Ber
// var BerReader = asn1.BerReader
var Ber = asn1.Ber; var BerWriter = asn1.BerWriter
var BerReader = asn1.BerReader; var getControl = require('../controls').getControl
var BerWriter = asn1.BerWriter;
var getControl = require('../controls').getControl;
///--- API
/// --- API
/** /**
* LDAPMessage structure. * LDAPMessage structure.
* *
* @param {Object} options stuff. * @param {Object} options stuff.
*/ */
function LDAPMessage(options) { function LDAPMessage (options) {
assert.object(options); assert.object(options)
this.messageID = options.messageID || 0; this.messageID = options.messageID || 0
this.protocolOp = options.protocolOp || undefined; this.protocolOp = options.protocolOp || undefined
this.controls = options.controls ? options.controls.slice(0) : []; this.controls = options.controls ? options.controls.slice(0) : []
this.log = options.log; this.log = options.log || logger
} }
Object.defineProperties(LDAPMessage.prototype, { Object.defineProperties(LDAPMessage.prototype, {
id: { id: {
get: function getId() { return this.messageID; }, get: function getId () { return this.messageID },
configurable: false configurable: false
}, },
dn: { dn: {
get: function getDN() { return this._dn || ''; }, get: function getDN () { return this._dn || '' },
configurable: false configurable: false
}, },
type: { type: {
get: function getType() { return 'LDAPMessage'; }, get: function getType () { return 'LDAPMessage' },
configurable: false configurable: false
}, },
json: { json: {
@ -52,66 +50,61 @@ Object.defineProperties(LDAPMessage.prototype, {
var out = this._json({ var out = this._json({
messageID: this.messageID, messageID: this.messageID,
protocolOp: this.type protocolOp: this.type
}); })
out.controls = this.controls; out.controls = this.controls
return out; return out
}, },
configurable: false configurable: false
} }
}); })
LDAPMessage.prototype.toString = function () { LDAPMessage.prototype.toString = function () {
return JSON.stringify(this.json); return JSON.stringify(this.json)
}; }
LDAPMessage.prototype.parse = function (ber) { LDAPMessage.prototype.parse = function (ber) {
assert.ok(ber); assert.ok(ber)
if (this.log.trace()) if (this.log.trace()) { this.log.trace('parse: data=%s', util.inspect(ber.buffer)) }
this.log.trace('parse: data=%s', util.inspect(ber.buffer));
// Delegate off to the specific type to parse // Delegate off to the specific type to parse
this._parse(ber, ber.length); this._parse(ber, ber.length)
// Look for controls // Look for controls
if (ber.peek() === 0xa0) { if (ber.peek() === 0xa0) {
ber.readSequence(); ber.readSequence()
var end = ber.offset + ber.length; var end = ber.offset + ber.length
while (ber.offset < end) { while (ber.offset < end) {
var c = getControl(ber); var c = getControl(ber)
if (c) if (c) { this.controls.push(c) }
this.controls.push(c);
} }
} }
if (this.log.trace()) if (this.log.trace()) { this.log.trace('Parsing done: %j', this.json) }
this.log.trace('Parsing done: %j', this.json); return true
return true; }
};
LDAPMessage.prototype.toBer = function () { LDAPMessage.prototype.toBer = function () {
var writer = new BerWriter(); var writer = new BerWriter()
writer.startSequence(); writer.startSequence()
writer.writeInt(this.messageID); writer.writeInt(this.messageID)
writer.startSequence(this.protocolOp); writer.startSequence(this.protocolOp)
if (this._toBer) if (this._toBer) { writer = this._toBer(writer) }
writer = this._toBer(writer); writer.endSequence()
writer.endSequence();
if (this.controls && this.controls.length) { if (this.controls && this.controls.length) {
writer.startSequence(0xa0); writer.startSequence(0xa0)
this.controls.forEach(function (c) { this.controls.forEach(function (c) {
c.toBer(writer); c.toBer(writer)
}); })
writer.endSequence(); writer.endSequence()
} }
writer.endSequence(); writer.endSequence()
return writer.buffer; return writer.buffer
}; }
/// --- Exports
///--- Exports module.exports = LDAPMessage
module.exports = LDAPMessage;

View File

@ -1,88 +1,85 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved. // Copyright 2011 Mark Cavage, Inc. All rights reserved.
var assert = require('assert-plus'); var assert = require('assert-plus')
var util = require('util'); var util = require('util')
var LDAPMessage = require('./message'); var LDAPMessage = require('./message')
var Protocol = require('../protocol'); var Protocol = require('../protocol')
var dn = require('../dn'); var dn = require('../dn')
var lassert = require('../assert'); var lassert = require('../assert')
/// --- API
///--- API function ModifyDNRequest (options) {
options = options || {}
assert.object(options)
assert.optionalBool(options.deleteOldRdn)
lassert.optionalStringDN(options.entry)
lassert.optionalDN(options.newRdn)
lassert.optionalDN(options.newSuperior)
function ModifyDNRequest(options) { options.protocolOp = Protocol.LDAP_REQ_MODRDN
options = options || {}; LDAPMessage.call(this, 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; this.entry = options.entry || null
LDAPMessage.call(this, options); this.newRdn = options.newRdn || null
this.deleteOldRdn = options.deleteOldRdn || true
this.entry = options.entry || null; this.newSuperior = options.newSuperior || null
this.newRdn = options.newRdn || null;
this.deleteOldRdn = options.deleteOldRdn || true;
this.newSuperior = options.newSuperior || null;
} }
util.inherits(ModifyDNRequest, LDAPMessage); util.inherits(ModifyDNRequest, LDAPMessage)
Object.defineProperties(ModifyDNRequest.prototype, { Object.defineProperties(ModifyDNRequest.prototype, {
type: { type: {
get: function getType() { return 'ModifyDNRequest'; }, get: function getType () { return 'ModifyDNRequest' },
configurable: false configurable: false
}, },
_dn: { _dn: {
get: function getDN() { return this.entry; }, get: function getDN () { return this.entry },
configurable: false configurable: false
} }
}); })
ModifyDNRequest.prototype._parse = function (ber) { ModifyDNRequest.prototype._parse = function (ber) {
assert.ok(ber); assert.ok(ber)
this.entry = ber.readString(); this.entry = ber.readString()
this.newRdn = dn.parse(ber.readString()); this.newRdn = dn.parse(ber.readString())
this.deleteOldRdn = ber.readBoolean(); this.deleteOldRdn = ber.readBoolean()
if (ber.peek() === 0x80) if (ber.peek() === 0x80) { this.newSuperior = dn.parse(ber.readString(0x80)) }
this.newSuperior = dn.parse(ber.readString(0x80));
return true; return true
}; }
ModifyDNRequest.prototype._toBer = function (ber) { ModifyDNRequest.prototype._toBer = function (ber) {
//assert.ok(ber); // assert.ok(ber);
ber.writeString(this.entry.toString()); ber.writeString(this.entry.toString())
ber.writeString(this.newRdn.toString()); ber.writeString(this.newRdn.toString())
ber.writeBoolean(this.deleteOldRdn); ber.writeBoolean(this.deleteOldRdn)
if (this.newSuperior) { if (this.newSuperior) {
var s = this.newSuperior.toString(); var s = this.newSuperior.toString()
var len = Buffer.byteLength(s); var len = Buffer.byteLength(s)
ber.writeByte(0x80); // MODIFY_DN_REQUEST_NEW_SUPERIOR_TAG ber.writeByte(0x80) // MODIFY_DN_REQUEST_NEW_SUPERIOR_TAG
ber.writeByte(len); ber.writeByte(len)
ber._ensure(len); ber._ensure(len)
ber._buf.write(s, ber._offset); ber._buf.write(s, ber._offset)
ber._offset += len; ber._offset += len
} }
return ber; return ber
}; }
ModifyDNRequest.prototype._json = function (j) { ModifyDNRequest.prototype._json = function (j) {
assert.ok(j); assert.ok(j)
j.entry = this.entry.toString(); j.entry = this.entry.toString()
j.newRdn = this.newRdn.toString(); j.newRdn = this.newRdn.toString()
j.deleteOldRdn = this.deleteOldRdn; j.deleteOldRdn = this.deleteOldRdn
j.newSuperior = this.newSuperior ? this.newSuperior.toString() : ''; j.newSuperior = this.newSuperior ? this.newSuperior.toString() : ''
return j; return j
}; }
/// --- Exports
///--- Exports module.exports = ModifyDNRequest
module.exports = ModifyDNRequest;

View File

@ -1,24 +1,22 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved. // Copyright 2011 Mark Cavage, Inc. All rights reserved.
var assert = require('assert-plus'); var assert = require('assert-plus')
var util = require('util'); var util = require('util')
var LDAPResult = require('./result'); var LDAPResult = require('./result')
var Protocol = require('../protocol'); var Protocol = require('../protocol')
/// --- API
///--- API function ModifyDNResponse (options) {
options = options || {}
assert.object(options)
function ModifyDNResponse(options) { options.protocolOp = Protocol.LDAP_REP_MODRDN
options = options || {}; LDAPResult.call(this, options)
assert.object(options);
options.protocolOp = Protocol.LDAP_REP_MODRDN;
LDAPResult.call(this, options);
} }
util.inherits(ModifyDNResponse, LDAPResult); util.inherits(ModifyDNResponse, LDAPResult)
/// --- Exports
///--- Exports module.exports = ModifyDNResponse
module.exports = ModifyDNResponse;

View File

@ -1,85 +1,83 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved. // Copyright 2011 Mark Cavage, Inc. All rights reserved.
var assert = require('assert-plus'); var assert = require('assert-plus')
var util = require('util'); var util = require('util')
var LDAPMessage = require('./message'); var LDAPMessage = require('./message')
var Change = require('../change'); var Change = require('../change')
var Protocol = require('../protocol'); var Protocol = require('../protocol')
var lassert = require('../assert'); var lassert = require('../assert')
/// --- API
///--- API function ModifyRequest (options) {
options = options || {}
assert.object(options)
lassert.optionalStringDN(options.object)
lassert.optionalArrayOfAttribute(options.attributes)
function ModifyRequest(options) { options.protocolOp = Protocol.LDAP_REQ_MODIFY
options = options || {}; LDAPMessage.call(this, options)
assert.object(options);
lassert.optionalStringDN(options.object);
lassert.optionalArrayOfAttribute(options.attributes);
options.protocolOp = Protocol.LDAP_REQ_MODIFY; this.object = options.object || null
LDAPMessage.call(this, options); this.changes = options.changes ? options.changes.slice(0) : []
this.object = options.object || null;
this.changes = options.changes ? options.changes.slice(0) : [];
} }
util.inherits(ModifyRequest, LDAPMessage); util.inherits(ModifyRequest, LDAPMessage)
Object.defineProperties(ModifyRequest.prototype, { Object.defineProperties(ModifyRequest.prototype, {
type: { type: {
get: function getType() { return 'ModifyRequest'; }, get: function getType () { return 'ModifyRequest' },
configurable: false configurable: false
}, },
_dn: { _dn: {
get: function getDN() { return this.object; }, get: function getDN () { return this.object },
configurable: false configurable: false
} }
}); })
ModifyRequest.prototype._parse = function (ber) { ModifyRequest.prototype._parse = function (ber) {
assert.ok(ber); assert.ok(ber)
this.object = ber.readString(); this.object = ber.readString()
ber.readSequence(); ber.readSequence()
var end = ber.offset + ber.length; var end = ber.offset + ber.length
while (ber.offset < end) { while (ber.offset < end) {
var c = new Change(); var c = new Change()
c.parse(ber); c.parse(ber)
c.modification.type = c.modification.type.toLowerCase(); c.modification.type = c.modification.type.toLowerCase()
this.changes.push(c); this.changes.push(c)
} }
this.changes.sort(Change.compare); this.changes.sort(Change.compare)
return true; return true
}; }
ModifyRequest.prototype._toBer = function (ber) { ModifyRequest.prototype._toBer = function (ber) {
assert.ok(ber); assert.ok(ber)
ber.writeString(this.object.toString()); ber.writeString(this.object.toString())
ber.startSequence(); ber.startSequence()
this.changes.forEach(function (c) { this.changes.forEach(function (c) {
c.toBer(ber); c.toBer(ber)
}); })
ber.endSequence(); ber.endSequence()
return ber; return ber
}; }
ModifyRequest.prototype._json = function (j) { ModifyRequest.prototype._json = function (j) {
assert.ok(j); assert.ok(j)
j.object = this.object; j.object = this.object
j.changes = []; j.changes = []
this.changes.forEach(function (c) { this.changes.forEach(function (c) {
j.changes.push(c.json); j.changes.push(c.json)
}); })
return j; return j
}; }
/// --- Exports
///--- Exports module.exports = ModifyRequest
module.exports = ModifyRequest;

View File

@ -1,24 +1,22 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved. // Copyright 2011 Mark Cavage, Inc. All rights reserved.
var assert = require('assert-plus'); var assert = require('assert-plus')
var util = require('util'); var util = require('util')
var LDAPResult = require('./result'); var LDAPResult = require('./result')
var Protocol = require('../protocol'); var Protocol = require('../protocol')
/// --- API
///--- API function ModifyResponse (options) {
options = options || {}
assert.object(options)
function ModifyResponse(options) { options.protocolOp = Protocol.LDAP_REP_MODIFY
options = options || {}; LDAPResult.call(this, options)
assert.object(options);
options.protocolOp = Protocol.LDAP_REP_MODIFY;
LDAPResult.call(this, options);
} }
util.inherits(ModifyResponse, LDAPResult); util.inherits(ModifyResponse, LDAPResult)
/// --- Exports
///--- Exports module.exports = ModifyResponse
module.exports = ModifyResponse;

View File

@ -1,228 +1,221 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved. // Copyright 2011 Mark Cavage, Inc. All rights reserved.
var EventEmitter = require('events').EventEmitter; var EventEmitter = require('events').EventEmitter
var util = require('util'); var util = require('util')
var assert = require('assert-plus'); var assert = require('assert-plus')
var asn1 = require('asn1'); var asn1 = require('asn1')
var VError = require('verror').VError; // var VError = require('verror').VError
var logger = require('../logger')
var AbandonRequest = require('./abandon_request'); var AbandonRequest = require('./abandon_request')
var AddRequest = require('./add_request'); var AddRequest = require('./add_request')
var AddResponse = require('./add_response'); var AddResponse = require('./add_response')
var BindRequest = require('./bind_request'); var BindRequest = require('./bind_request')
var BindResponse = require('./bind_response'); var BindResponse = require('./bind_response')
var CompareRequest = require('./compare_request'); var CompareRequest = require('./compare_request')
var CompareResponse = require('./compare_response'); var CompareResponse = require('./compare_response')
var DeleteRequest = require('./del_request'); var DeleteRequest = require('./del_request')
var DeleteResponse = require('./del_response'); var DeleteResponse = require('./del_response')
var ExtendedRequest = require('./ext_request'); var ExtendedRequest = require('./ext_request')
var ExtendedResponse = require('./ext_response'); var ExtendedResponse = require('./ext_response')
var ModifyRequest = require('./modify_request'); var ModifyRequest = require('./modify_request')
var ModifyResponse = require('./modify_response'); var ModifyResponse = require('./modify_response')
var ModifyDNRequest = require('./moddn_request'); var ModifyDNRequest = require('./moddn_request')
var ModifyDNResponse = require('./moddn_response'); var ModifyDNResponse = require('./moddn_response')
var SearchRequest = require('./search_request'); var SearchRequest = require('./search_request')
var SearchEntry = require('./search_entry'); var SearchEntry = require('./search_entry')
var SearchReference = require('./search_reference'); var SearchReference = require('./search_reference')
var SearchResponse = require('./search_response'); var SearchResponse = require('./search_response')
var UnbindRequest = require('./unbind_request'); var UnbindRequest = require('./unbind_request')
var UnbindResponse = require('./unbind_response'); // var UnbindResponse = require('./unbind_response')
var LDAPResult = require('./result'); var LDAPResult = require('./result')
var Message = require('./message'); // var Message = require('./message')
var Protocol = require('../protocol'); var Protocol = require('../protocol')
/// --- Globals
///--- Globals // var Ber = asn1.Ber
var BerReader = asn1.BerReader
var Ber = asn1.Ber; /// --- API
var BerReader = asn1.BerReader;
function Parser (options = {}) {
assert.object(options)
///--- API EventEmitter.call(this)
function Parser(options) { this.buffer = null
assert.object(options); this.log = options.log || logger
assert.object(options.log);
EventEmitter.call(this);
this.buffer = null;
this.log = options.log;
} }
util.inherits(Parser, EventEmitter); util.inherits(Parser, EventEmitter)
Parser.prototype.write = function (data) { Parser.prototype.write = function (data) {
if (!data || !Buffer.isBuffer(data)) if (!data || !Buffer.isBuffer(data)) { throw new TypeError('data (buffer) required') }
throw new TypeError('data (buffer) required');
var nextMessage = null; var nextMessage = null
var self = this; var self = this
function end() { function end () {
if (nextMessage) if (nextMessage) { return self.write(nextMessage) }
return self.write(nextMessage);
return true; return true
} }
self.buffer = (self.buffer ? Buffer.concat([self.buffer, data]) : data); self.buffer = (self.buffer ? Buffer.concat([self.buffer, data]) : data)
var ber = new BerReader(self.buffer); var ber = new BerReader(self.buffer)
var foundSeq = false; var foundSeq = false
try { try {
foundSeq = ber.readSequence(); foundSeq = ber.readSequence()
} catch (e) { } catch (e) {
this.emit('error', e); this.emit('error', e)
} }
if (!foundSeq || ber.remain < ber.length) { if (!foundSeq || ber.remain < ber.length) {
// ENOTENOUGH // ENOTENOUGH
return false; return false
} else if (ber.remain > ber.length) { } else if (ber.remain > ber.length) {
// ETOOMUCH // ETOOMUCH
// This is sort of ugly, but allows us to make miminal copies // This is sort of ugly, but allows us to make miminal copies
nextMessage = self.buffer.slice(ber.offset + ber.length); nextMessage = self.buffer.slice(ber.offset + ber.length)
ber._size = ber.offset + ber.length; ber._size = ber.offset + ber.length
assert.equal(ber.remain, ber.length); assert.equal(ber.remain, ber.length)
} }
// If we're here, ber holds the message, and nextMessage is temporarily // If we're here, ber holds the message, and nextMessage is temporarily
// pointing at the next sequence of data (if it exists) // pointing at the next sequence of data (if it exists)
self.buffer = null; self.buffer = null
var message; var message
try { try {
// Bail here if peer isn't speaking protocol at all // Bail here if peer isn't speaking protocol at all
message = this.getMessage(ber); message = this.getMessage(ber)
if (!message) { if (!message) {
return end(); return end()
} }
message.parse(ber); message.parse(ber)
} catch (e) { } catch (e) {
this.emit('error', e, message); this.emit('error', e, message)
return false; return false
} }
this.emit('message', message); this.emit('message', message)
return end(); return end()
}; }
Parser.prototype.getMessage = function (ber) { Parser.prototype.getMessage = function (ber) {
assert.ok(ber); assert.ok(ber)
var self = this; var self = this
var messageID = ber.readInt(); var messageID = ber.readInt()
var type = ber.readSequence(); var type = ber.readSequence()
var Message; var Message
switch (type) { switch (type) {
case Protocol.LDAP_REQ_ABANDON:
Message = AbandonRequest
break
case Protocol.LDAP_REQ_ABANDON: case Protocol.LDAP_REQ_ADD:
Message = AbandonRequest; Message = AddRequest
break; break
case Protocol.LDAP_REQ_ADD: case Protocol.LDAP_REP_ADD:
Message = AddRequest; Message = AddResponse
break; break
case Protocol.LDAP_REP_ADD: case Protocol.LDAP_REQ_BIND:
Message = AddResponse; Message = BindRequest
break; break
case Protocol.LDAP_REQ_BIND: case Protocol.LDAP_REP_BIND:
Message = BindRequest; Message = BindResponse
break; break
case Protocol.LDAP_REP_BIND: case Protocol.LDAP_REQ_COMPARE:
Message = BindResponse; Message = CompareRequest
break; break
case Protocol.LDAP_REQ_COMPARE: case Protocol.LDAP_REP_COMPARE:
Message = CompareRequest; Message = CompareResponse
break; break
case Protocol.LDAP_REP_COMPARE: case Protocol.LDAP_REQ_DELETE:
Message = CompareResponse; Message = DeleteRequest
break; break
case Protocol.LDAP_REQ_DELETE: case Protocol.LDAP_REP_DELETE:
Message = DeleteRequest; Message = DeleteResponse
break; break
case Protocol.LDAP_REP_DELETE: case Protocol.LDAP_REQ_EXTENSION:
Message = DeleteResponse; Message = ExtendedRequest
break; break
case Protocol.LDAP_REQ_EXTENSION: case Protocol.LDAP_REP_EXTENSION:
Message = ExtendedRequest; Message = ExtendedResponse
break; break
case Protocol.LDAP_REP_EXTENSION: case Protocol.LDAP_REQ_MODIFY:
Message = ExtendedResponse; Message = ModifyRequest
break; break
case Protocol.LDAP_REQ_MODIFY: case Protocol.LDAP_REP_MODIFY:
Message = ModifyRequest; Message = ModifyResponse
break; break
case Protocol.LDAP_REP_MODIFY: case Protocol.LDAP_REQ_MODRDN:
Message = ModifyResponse; Message = ModifyDNRequest
break; break
case Protocol.LDAP_REQ_MODRDN: case Protocol.LDAP_REP_MODRDN:
Message = ModifyDNRequest; Message = ModifyDNResponse
break; break
case Protocol.LDAP_REP_MODRDN: case Protocol.LDAP_REQ_SEARCH:
Message = ModifyDNResponse; Message = SearchRequest
break; break
case Protocol.LDAP_REQ_SEARCH: case Protocol.LDAP_REP_SEARCH_ENTRY:
Message = SearchRequest; Message = SearchEntry
break; break
case Protocol.LDAP_REP_SEARCH_ENTRY: case Protocol.LDAP_REP_SEARCH_REF:
Message = SearchEntry; Message = SearchReference
break; break
case Protocol.LDAP_REP_SEARCH_REF: case Protocol.LDAP_REP_SEARCH:
Message = SearchReference; Message = SearchResponse
break; break
case Protocol.LDAP_REP_SEARCH: case Protocol.LDAP_REQ_UNBIND:
Message = SearchResponse; Message = UnbindRequest
break; break
case Protocol.LDAP_REQ_UNBIND: default:
Message = UnbindRequest; this.emit('error',
break; new Error('Op 0x' + (type ? type.toString(16) : '??') +
default:
this.emit('error',
new Error('Op 0x' + (type ? type.toString(16) : '??') +
' not supported'), ' not supported'),
new LDAPResult({ new LDAPResult({
messageID: messageID, messageID: messageID,
protocolOp: type || Protocol.LDAP_REP_EXTENSION protocolOp: type || Protocol.LDAP_REP_EXTENSION
})); }))
return false; return false
} }
return new Message({ return new Message({
messageID: messageID, messageID: messageID,
log: self.log log: self.log
}); })
}; }
/// --- Exports
///--- Exports module.exports = Parser
module.exports = Parser;

View File

@ -1,65 +1,61 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved. // Copyright 2011 Mark Cavage, Inc. All rights reserved.
var assert = require('assert-plus'); var assert = require('assert-plus')
var util = require('util'); var util = require('util')
var asn1 = require('asn1'); // var asn1 = require('asn1')
var dtrace = require('../dtrace'); var dtrace = require('../dtrace')
var LDAPMessage = require('./message'); var LDAPMessage = require('./message')
var Protocol = require('../protocol'); var Protocol = require('../protocol')
/// --- Globals
///--- Globals // var Ber = asn1.Ber
// var BerWriter = asn1.BerWriter
var Ber = asn1.Ber; /// --- API
var BerWriter = asn1.BerWriter;
function LDAPResult (options) {
options = options || {}
assert.object(options)
assert.optionalNumber(options.status)
assert.optionalString(options.matchedDN)
assert.optionalString(options.errorMessage)
assert.optionalArrayOfString(options.referrals)
///--- API LDAPMessage.call(this, options)
function LDAPResult(options) { this.status = options.status || 0 // LDAP SUCCESS
options = options || {}; this.matchedDN = options.matchedDN || ''
assert.object(options); this.errorMessage = options.errorMessage || ''
assert.optionalNumber(options.status); this.referrals = options.referrals || []
assert.optionalString(options.matchedDN);
assert.optionalString(options.errorMessage);
assert.optionalArrayOfString(options.referrals);
LDAPMessage.call(this, options); this.connection = options.connection || null
this.status = options.status || 0; // LDAP SUCCESS
this.matchedDN = options.matchedDN || '';
this.errorMessage = options.errorMessage || '';
this.referrals = options.referrals || [];
this.connection = options.connection || null;
} }
util.inherits(LDAPResult, LDAPMessage); util.inherits(LDAPResult, LDAPMessage)
Object.defineProperties(LDAPResult.prototype, { Object.defineProperties(LDAPResult.prototype, {
type: { type: {
get: function getType() { return 'LDAPResult'; }, get: function getType () { return 'LDAPResult' },
configurable: false configurable: false
} }
}); })
LDAPResult.prototype.end = function (status) { LDAPResult.prototype.end = function (status) {
assert.ok(this.connection); assert.ok(this.connection)
if (typeof (status) === 'number') if (typeof (status) === 'number') { this.status = status }
this.status = status;
var ber = this.toBer(); var ber = this.toBer()
if (this.log.debug()) if (this.log.debug()) { this.log.debug('%s: sending: %j', this.connection.ldap.id, this.json) }
this.log.debug('%s: sending: %j', this.connection.ldap.id, this.json);
try { try {
var self = this; var self = this
this.connection.write(ber); this.connection.write(ber)
if (self._dtraceOp && self._dtraceId) { if (self._dtraceOp && self._dtraceId) {
dtrace.fire('server-' + self._dtraceOp + '-done', function () { dtrace.fire('server-' + self._dtraceOp + '-done', function () {
var c = self.connection || {ldap: {}}; var c = self.connection || { ldap: {} }
return [ return [
self._dtraceId || 0, self._dtraceId || 0,
(c.remoteAddress || ''), (c.remoteAddress || ''),
@ -67,63 +63,59 @@ LDAPResult.prototype.end = function (status) {
(self.requestDN ? self.requestDN.toString() : ''), (self.requestDN ? self.requestDN.toString() : ''),
status || self.status, status || self.status,
self.errorMessage self.errorMessage
]; ]
}); })
} }
} catch (e) { } catch (e) {
this.log.warn(e, '%s failure to write message %j', this.log.warn(e, '%s failure to write message %j',
this.connection.ldap.id, this.json); this.connection.ldap.id, this.json)
} }
}
};
LDAPResult.prototype._parse = function (ber) { LDAPResult.prototype._parse = function (ber) {
assert.ok(ber); assert.ok(ber)
this.status = ber.readEnumeration(); this.status = ber.readEnumeration()
this.matchedDN = ber.readString(); this.matchedDN = ber.readString()
this.errorMessage = ber.readString(); this.errorMessage = ber.readString()
var t = ber.peek(); var t = ber.peek()
if (t === Protocol.LDAP_REP_REFERRAL) { if (t === Protocol.LDAP_REP_REFERRAL) {
var end = ber.offset + ber.length; var end = ber.offset + ber.length
while (ber.offset < end) while (ber.offset < end) { this.referrals.push(ber.readString()) }
this.referrals.push(ber.readString());
} }
return true; return true
}; }
LDAPResult.prototype._toBer = function (ber) { LDAPResult.prototype._toBer = function (ber) {
assert.ok(ber); assert.ok(ber)
ber.writeEnumeration(this.status); ber.writeEnumeration(this.status)
ber.writeString(this.matchedDN || ''); ber.writeString(this.matchedDN || '')
ber.writeString(this.errorMessage || ''); ber.writeString(this.errorMessage || '')
if (this.referrals.length) { if (this.referrals.length) {
ber.startSequence(Protocol.LDAP_REP_REFERRAL); ber.startSequence(Protocol.LDAP_REP_REFERRAL)
ber.writeStringArray(this.referrals); ber.writeStringArray(this.referrals)
ber.endSequence(); ber.endSequence()
} }
return ber; return ber
}; }
LDAPResult.prototype._json = function (j) { LDAPResult.prototype._json = function (j) {
assert.ok(j); assert.ok(j)
j.status = this.status; j.status = this.status
j.matchedDN = this.matchedDN; j.matchedDN = this.matchedDN
j.errorMessage = this.errorMessage; j.errorMessage = this.errorMessage
j.referrals = this.referrals; j.referrals = this.referrals
return j; return j
}; }
/// --- Exports
///--- Exports module.exports = LDAPResult
module.exports = LDAPResult;

View File

@ -1,196 +1,187 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved. // Copyright 2011 Mark Cavage, Inc. All rights reserved.
var assert = require('assert-plus'); var assert = require('assert-plus')
var util = require('util'); var util = require('util')
var asn1 = require('asn1'); // var asn1 = require('asn1')
var LDAPMessage = require('./message'); var LDAPMessage = require('./message')
var Attribute = require('../attribute'); var Attribute = require('../attribute')
var Protocol = require('../protocol'); var Protocol = require('../protocol')
var lassert = require('../assert'); var lassert = require('../assert')
/// --- Globals
///--- Globals // var BerWriter = asn1.BerWriter
var BerWriter = asn1.BerWriter; /// --- API
function SearchEntry (options) {
options = options || {}
assert.object(options)
lassert.optionalStringDN(options.objectName)
///--- API options.protocolOp = Protocol.LDAP_REP_SEARCH_ENTRY
LDAPMessage.call(this, options)
function SearchEntry(options) { this.objectName = options.objectName || null
options = options || {}; this.setAttributes(options.attributes || [])
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 || []);
} }
util.inherits(SearchEntry, LDAPMessage); util.inherits(SearchEntry, LDAPMessage)
Object.defineProperties(SearchEntry.prototype, { Object.defineProperties(SearchEntry.prototype, {
type: { type: {
get: function getType() { return 'SearchEntry'; }, get: function getType () { return 'SearchEntry' },
configurable: false configurable: false
}, },
_dn: { _dn: {
get: function getDN() { return this.objectName; }, get: function getDN () { return this.objectName },
configurable: false configurable: false
}, },
object: { object: {
get: function getObject() { get: function getObject () {
var obj = { var obj = {
dn: this.dn.toString(), dn: this.dn.toString(),
controls: [] controls: []
}; }
this.attributes.forEach(function (a) { this.attributes.forEach(function (a) {
if (a.vals && a.vals.length) { if (a.vals && a.vals.length) {
if (a.vals.length > 1) { if (a.vals.length > 1) {
obj[a.type] = a.vals.slice(); obj[a.type] = a.vals.slice()
} else { } else {
obj[a.type] = a.vals[0]; obj[a.type] = a.vals[0]
} }
} else { } else {
obj[a.type] = []; obj[a.type] = []
} }
}); })
this.controls.forEach(function (element, index, array) { this.controls.forEach(function (element, index, array) {
obj.controls.push(element.json); obj.controls.push(element.json)
}); })
return obj; return obj
}, },
configurable: false configurable: false
}, },
raw: { raw: {
get: function getRaw() { get: function getRaw () {
var obj = { var obj = {
dn: this.dn.toString(), dn: this.dn.toString(),
controls: [] controls: []
}; }
this.attributes.forEach(function (a) { this.attributes.forEach(function (a) {
if (a.buffers && a.buffers.length) { if (a.buffers && a.buffers.length) {
if (a.buffers.length > 1) { if (a.buffers.length > 1) {
obj[a.type] = a.buffers.slice(); obj[a.type] = a.buffers.slice()
} else { } else {
obj[a.type] = a.buffers[0]; obj[a.type] = a.buffers[0]
} }
} else { } else {
obj[a.type] = []; obj[a.type] = []
} }
}); })
this.controls.forEach(function (element, index, array) { this.controls.forEach(function (element, index, array) {
obj.controls.push(element.json); obj.controls.push(element.json)
}); })
return obj; return obj
}, },
configurable: false configurable: false
} }
}); })
SearchEntry.prototype.addAttribute = function (attr) { SearchEntry.prototype.addAttribute = function (attr) {
if (!attr || typeof (attr) !== 'object') if (!attr || typeof (attr) !== 'object') { throw new TypeError('attr (attribute) required') }
throw new TypeError('attr (attribute) required');
this.attributes.push(attr); this.attributes.push(attr)
}; }
SearchEntry.prototype.toObject = function () { SearchEntry.prototype.toObject = function () {
return this.object; return this.object
}; }
SearchEntry.prototype.fromObject = function (obj) { SearchEntry.prototype.fromObject = function (obj) {
if (typeof (obj) !== 'object') if (typeof (obj) !== 'object') { throw new TypeError('object required') }
throw new TypeError('object required');
var self = this; var self = this
if (obj.controls) if (obj.controls) { this.controls = obj.controls }
this.controls = obj.controls;
if (obj.attributes) if (obj.attributes) { obj = obj.attributes }
obj = obj.attributes; this.attributes = []
this.attributes = [];
Object.keys(obj).forEach(function (k) { Object.keys(obj).forEach(function (k) {
self.attributes.push(new Attribute({type: k, vals: obj[k]})); self.attributes.push(new Attribute({ type: k, vals: obj[k] }))
}); })
return true; return true
}; }
SearchEntry.prototype.setAttributes = function (obj) { SearchEntry.prototype.setAttributes = function (obj) {
if (typeof (obj) !== 'object') if (typeof (obj) !== 'object') { throw new TypeError('object required') }
throw new TypeError('object required');
if (Array.isArray(obj)) { if (Array.isArray(obj)) {
obj.forEach(function (a) { obj.forEach(function (a) {
if (!Attribute.isAttribute(a)) if (!Attribute.isAttribute(a)) { throw new TypeError('entry must be an Array of Attributes') }
throw new TypeError('entry must be an Array of Attributes'); })
}); this.attributes = obj
this.attributes = obj;
} else { } else {
var self = this; var self = this
self.attributes = []; self.attributes = []
Object.keys(obj).forEach(function (k) { Object.keys(obj).forEach(function (k) {
var attr = new Attribute({type: k}); var attr = new Attribute({ type: k })
if (Array.isArray(obj[k])) { if (Array.isArray(obj[k])) {
obj[k].forEach(function (v) { obj[k].forEach(function (v) {
attr.addValue(v.toString()); attr.addValue(v.toString())
}); })
} else { } else {
attr.addValue(obj[k].toString()); attr.addValue(obj[k].toString())
} }
self.attributes.push(attr); self.attributes.push(attr)
}); })
} }
}; }
SearchEntry.prototype._json = function (j) { SearchEntry.prototype._json = function (j) {
assert.ok(j); assert.ok(j)
j.objectName = this.objectName.toString(); j.objectName = this.objectName.toString()
j.attributes = []; j.attributes = []
this.attributes.forEach(function (a) { this.attributes.forEach(function (a) {
j.attributes.push(a.json || a); j.attributes.push(a.json || a)
}); })
return j; return j
}; }
SearchEntry.prototype._parse = function (ber) { SearchEntry.prototype._parse = function (ber) {
assert.ok(ber); assert.ok(ber)
this.objectName = ber.readString(); this.objectName = ber.readString()
assert.ok(ber.readSequence()); assert.ok(ber.readSequence())
var end = ber.offset + ber.length; var end = ber.offset + ber.length
while (ber.offset < end) { while (ber.offset < end) {
var a = new Attribute(); var a = new Attribute()
a.parse(ber); a.parse(ber)
this.attributes.push(a); this.attributes.push(a)
} }
return true; return true
}; }
SearchEntry.prototype._toBer = function (ber) { SearchEntry.prototype._toBer = function (ber) {
assert.ok(ber); assert.ok(ber)
ber.writeString(this.objectName.toString()); ber.writeString(this.objectName.toString())
ber.startSequence(); ber.startSequence()
this.attributes.forEach(function (a) { this.attributes.forEach(function (a) {
// This may or may not be an attribute // This may or may not be an attribute
ber = Attribute.toBer(a, ber); ber = Attribute.toBer(a, ber)
}); })
ber.endSequence(); ber.endSequence()
return ber; return ber
}; }
/// --- Exports
///--- Exports module.exports = SearchEntry
module.exports = SearchEntry;

View File

@ -1,105 +1,101 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved. // Copyright 2011 Mark Cavage, Inc. All rights reserved.
var assert = require('assert-plus'); var assert = require('assert-plus')
var util = require('util'); var util = require('util')
var asn1 = require('asn1'); // var asn1 = require('asn1')
var LDAPMessage = require('./message'); var LDAPMessage = require('./message')
var Protocol = require('../protocol'); var Protocol = require('../protocol')
var dn = require('../dn'); var dn = require('../dn')
var url = require('../url'); var url = require('../url')
/// --- Globals
///--- Globals // var BerWriter = asn1.BerWriter
var parseURL = url.parse
var BerWriter = asn1.BerWriter; /// --- API
var parseURL = url.parse;
function SearchReference (options) {
options = options || {}
assert.object(options)
///--- API options.protocolOp = Protocol.LDAP_REP_SEARCH_REF
LDAPMessage.call(this, options)
function SearchReference(options) { this.uris = options.uris || []
options = options || {};
assert.object(options);
options.protocolOp = Protocol.LDAP_REP_SEARCH_REF;
LDAPMessage.call(this, options);
this.uris = options.uris || [];
} }
util.inherits(SearchReference, LDAPMessage); util.inherits(SearchReference, LDAPMessage)
Object.defineProperties(SearchReference.prototype, { Object.defineProperties(SearchReference.prototype, {
type: { type: {
get: function getType() { return 'SearchReference'; }, get: function getType () { return 'SearchReference' },
configurable: false configurable: false
}, },
_dn: { _dn: {
get: function getDN() { return new dn.DN(''); }, get: function getDN () { return new dn.DN('') },
configurable: false configurable: false
}, },
object: { object: {
get: function getObject() { get: function getObject () {
return { return {
dn: this.dn.toString(), dn: this.dn.toString(),
uris: this.uris.slice() uris: this.uris.slice()
}; }
}, },
configurable: false configurable: false
}, },
urls: { urls: {
get: function getUrls() { return this.uris; }, get: function getUrls () { return this.uris },
set: function setUrls(val) { set: function setUrls (val) {
assert.ok(val); assert.ok(val)
assert.ok(Array.isArray(val)); assert.ok(Array.isArray(val))
this.uris = val.slice(); this.uris = val.slice()
}, },
configurable: false configurable: false
} }
}); })
SearchReference.prototype.toObject = function () { SearchReference.prototype.toObject = function () {
return this.object; return this.object
}; }
SearchReference.prototype.fromObject = function (obj) { SearchReference.prototype.fromObject = function (obj) {
if (typeof (obj) !== 'object') if (typeof (obj) !== 'object') { throw new TypeError('object required') }
throw new TypeError('object required');
this.uris = obj.uris ? obj.uris.slice() : []; this.uris = obj.uris ? obj.uris.slice() : []
return true; return true
}; }
SearchReference.prototype._json = function (j) { SearchReference.prototype._json = function (j) {
assert.ok(j); assert.ok(j)
j.uris = this.uris.slice(); j.uris = this.uris.slice()
return j; return j
}; }
SearchReference.prototype._parse = function (ber, length) { SearchReference.prototype._parse = function (ber, length) {
assert.ok(ber); assert.ok(ber)
while (ber.offset < length) { while (ber.offset < length) {
var _url = ber.readString(); var _url = ber.readString()
parseURL(_url); parseURL(_url)
this.uris.push(_url); this.uris.push(_url)
} }
return true; return true
}; }
SearchReference.prototype._toBer = function (ber) { SearchReference.prototype._toBer = function (ber) {
assert.ok(ber); assert.ok(ber)
this.uris.forEach(function (u) { this.uris.forEach(function (u) {
ber.writeString(u.href || u); ber.writeString(u.href || u)
}); })
return ber; return ber
}; }
/// --- Exports
///--- Exports module.exports = SearchReference
module.exports = SearchReference;

View File

@ -1,150 +1,152 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved. // Copyright 2011 Mark Cavage, Inc. All rights reserved.
var assert = require('assert-plus'); var assert = require('assert-plus')
var util = require('util'); var util = require('util')
var asn1 = require('asn1'); var asn1 = require('asn1')
var LDAPMessage = require('./message'); var LDAPMessage = require('./message')
var LDAPResult = require('./result'); // var LDAPResult = require('./result')
var dn = require('../dn'); var dn = require('../dn')
var filters = require('../filters'); var filters = require('../filters')
var Protocol = require('../protocol'); var Protocol = require('../protocol')
/// --- Globals
///--- Globals var Ber = asn1.Ber
var Ber = asn1.Ber; /// --- API
function SearchRequest (options) {
options = options || {}
assert.object(options)
///--- API options.protocolOp = Protocol.LDAP_REQ_SEARCH
LDAPMessage.call(this, options)
function SearchRequest(options) {
options = options || {};
assert.object(options);
options.protocolOp = Protocol.LDAP_REQ_SEARCH;
LDAPMessage.call(this, options);
if (options.baseObject !== undefined) { if (options.baseObject !== undefined) {
this.baseObject = options.baseObject; this.baseObject = options.baseObject
} else { } else {
this.baseObject = dn.parse(''); this.baseObject = dn.parse('')
} }
this.scope = options.scope || 'base'; this.scope = options.scope || 'base'
this.derefAliases = options.derefAliases || Protocol.NEVER_DEREF_ALIASES; this.derefAliases = options.derefAliases || Protocol.NEVER_DEREF_ALIASES
this.sizeLimit = options.sizeLimit || 0; this.sizeLimit = options.sizeLimit || 0
this.timeLimit = options.timeLimit || 0; this.timeLimit = options.timeLimit || 0
this.typesOnly = options.typesOnly || false; this.typesOnly = options.typesOnly || false
this.filter = options.filter || null; this.filter = options.filter || null
this.attributes = options.attributes ? options.attributes.slice(0) : []; this.attributes = options.attributes ? options.attributes.slice(0) : []
} }
util.inherits(SearchRequest, LDAPMessage); util.inherits(SearchRequest, LDAPMessage)
Object.defineProperties(SearchRequest.prototype, { Object.defineProperties(SearchRequest.prototype, {
type: { type: {
get: function getType() { return 'SearchRequest'; }, get: function getType () { return 'SearchRequest' },
configurable: false configurable: false
}, },
_dn: { _dn: {
get: function getDN() { return this.baseObject; }, get: function getDN () { return this.baseObject },
configurable: false configurable: false
}, },
scope: { scope: {
get: function getScope() { get: function getScope () {
switch (this._scope) { switch (this._scope) {
case Protocol.SCOPE_BASE_OBJECT: return 'base'; case Protocol.SCOPE_BASE_OBJECT: return 'base'
case Protocol.SCOPE_ONE_LEVEL: return 'one'; case Protocol.SCOPE_ONE_LEVEL: return 'one'
case Protocol.SCOPE_SUBTREE: return 'sub'; case Protocol.SCOPE_SUBTREE: return 'sub'
default: default:
throw new Error(this._scope + ' is an invalid search scope'); throw new Error(this._scope + ' is an invalid search scope')
} }
}, },
set: function setScope(val) { set: function setScope (val) {
if (typeof (val) === 'string') { if (typeof (val) === 'string') {
switch (val) { switch (val) {
case 'base': case 'base':
this._scope = Protocol.SCOPE_BASE_OBJECT; this._scope = Protocol.SCOPE_BASE_OBJECT
break; break
case 'one': case 'one':
this._scope = Protocol.SCOPE_ONE_LEVEL; this._scope = Protocol.SCOPE_ONE_LEVEL
break; break
case 'sub': case 'sub':
this._scope = Protocol.SCOPE_SUBTREE; this._scope = Protocol.SCOPE_SUBTREE
break; break
default: default:
throw new Error(val + ' is an invalid search scope'); throw new Error(val + ' is an invalid search scope')
} }
} else { } else {
this._scope = val; this._scope = val
} }
}, },
configurable: false configurable: false
} }
}); })
SearchRequest.prototype._parse = function (ber) { SearchRequest.prototype._parse = function (ber) {
assert.ok(ber); assert.ok(ber)
this.baseObject = ber.readString(); this.baseObject = ber.readString()
this.scope = ber.readEnumeration(); this.scope = ber.readEnumeration()
this.derefAliases = ber.readEnumeration(); this.derefAliases = ber.readEnumeration()
this.sizeLimit = ber.readInt(); this.sizeLimit = ber.readInt()
this.timeLimit = ber.readInt(); this.timeLimit = ber.readInt()
this.typesOnly = ber.readBoolean(); this.typesOnly = ber.readBoolean()
this.filter = filters.parse(ber); this.filter = filters.parse(ber)
// look for attributes // look for attributes
if (ber.peek() === 0x30) { if (ber.peek() === 0x30) {
ber.readSequence(); ber.readSequence()
var end = ber.offset + ber.length; var end = ber.offset + ber.length
while (ber.offset < end) while (ber.offset < end) { this.attributes.push(ber.readString().toLowerCase()) }
this.attributes.push(ber.readString().toLowerCase());
} }
return true; return true
}; }
SearchRequest.prototype._toBer = function (ber) { SearchRequest.prototype._toBer = function (ber) {
assert.ok(ber); assert.ok(ber)
ber.writeString(this.baseObject.toString()); // Format only with commas, since that is what RFC 4514 mandates.
ber.writeEnumeration(this._scope); // There's a gotcha here: even though it's called baseObject,
ber.writeEnumeration(this.derefAliases); // it can be a string or a DN object.
ber.writeInt(this.sizeLimit); var formattedDN = dn.DN.isDN(this.baseObject)
ber.writeInt(this.timeLimit); ? this.baseObject.format({ skipSpace: true })
ber.writeBoolean(this.typesOnly); : this.baseObject.toString()
ber.writeString(formattedDN)
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'}); var f = this.filter || new filters.PresenceFilter({ attribute: 'objectclass' })
ber = f.toBer(ber); ber = f.toBer(ber)
ber.startSequence(Ber.Sequence | Ber.Constructor); ber.startSequence(Ber.Sequence | Ber.Constructor)
if (this.attributes && this.attributes.length) { if (this.attributes && this.attributes.length) {
this.attributes.forEach(function (a) { this.attributes.forEach(function (a) {
ber.writeString(a); ber.writeString(a)
}); })
} }
ber.endSequence(); ber.endSequence()
return ber; return ber
}; }
SearchRequest.prototype._json = function (j) { SearchRequest.prototype._json = function (j) {
assert.ok(j); assert.ok(j)
j.baseObject = this.baseObject; j.baseObject = this.baseObject
j.scope = this.scope; j.scope = this.scope
j.derefAliases = this.derefAliases; j.derefAliases = this.derefAliases
j.sizeLimit = this.sizeLimit; j.sizeLimit = this.sizeLimit
j.timeLimit = this.timeLimit; j.timeLimit = this.timeLimit
j.typesOnly = this.typesOnly; j.typesOnly = this.typesOnly
j.filter = this.filter.toString(); j.filter = this.filter.toString()
j.attributes = this.attributes; j.attributes = this.attributes
return j; return j
}; }
/// --- Exports
///--- Exports module.exports = SearchRequest
module.exports = SearchRequest;

View File

@ -1,32 +1,31 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved. // Copyright 2011 Mark Cavage, Inc. All rights reserved.
var assert = require('assert-plus'); var assert = require('assert-plus')
var util = require('util'); var util = require('util')
var LDAPResult = require('./result'); var LDAPResult = require('./result')
var SearchEntry = require('./search_entry'); var SearchEntry = require('./search_entry')
var SearchReference = require('./search_reference'); var SearchReference = require('./search_reference')
var dtrace = require('../dtrace'); var dtrace = require('../dtrace')
var parseDN = require('../dn').parse; var parseDN = require('../dn').parse
var parseURL = require('../url').parse; var parseURL = require('../url').parse
var Protocol = require('../protocol'); var Protocol = require('../protocol')
/// --- API
///--- API function SearchResponse (options) {
options = options || {}
assert.object(options)
function SearchResponse(options) { options.protocolOp = Protocol.LDAP_REP_SEARCH
options = options || {}; LDAPResult.call(this, options)
assert.object(options);
options.protocolOp = Protocol.LDAP_REP_SEARCH; this.attributes = options.attributes ? options.attributes.slice() : []
LDAPResult.call(this, options); this.notAttributes = []
this.sentEntries = 0
this.attributes = options.attributes ? options.attributes.slice() : [];
this.notAttributes = [];
this.sentEntries = 0;
} }
util.inherits(SearchResponse, LDAPResult); util.inherits(SearchResponse, LDAPResult)
/** /**
* Allows you to send a SearchEntry back to the client. * Allows you to send a SearchEntry back to the client.
@ -36,61 +35,54 @@ util.inherits(SearchResponse, LDAPResult);
* Defaults to 'false'. * Defaults to 'false'.
*/ */
SearchResponse.prototype.send = function (entry, nofiltering) { SearchResponse.prototype.send = function (entry, nofiltering) {
if (!entry || typeof (entry) !== 'object') if (!entry || typeof (entry) !== 'object') { throw new TypeError('entry (SearchEntry) required') }
throw new TypeError('entry (SearchEntry) required'); if (nofiltering === undefined) { nofiltering = false }
if (nofiltering === undefined) if (typeof (nofiltering) !== 'boolean') { throw new TypeError('noFiltering must be a boolean') }
nofiltering = false;
if (typeof (nofiltering) !== 'boolean')
throw new TypeError('noFiltering must be a boolean');
var self = this; var self = this
if (entry instanceof SearchEntry || entry instanceof SearchReference) { if (entry instanceof SearchEntry || entry instanceof SearchReference) {
if (!entry.messageID) if (!entry.messageID) { entry.messageID = this.messageID }
entry.messageID = this.messageID; if (entry.messageID !== this.messageID) { throw new Error('SearchEntry messageID mismatch') }
if (entry.messageID !== this.messageID)
throw new Error('SearchEntry messageID mismatch');
} else { } else {
if (!entry.attributes) if (!entry.attributes) { throw new Error('entry.attributes required') }
throw new Error('entry.attributes required');
var savedAttrs = {}; var savedAttrs = {}
var all = (self.attributes.indexOf('*') !== -1); var all = (self.attributes.indexOf('*') !== -1)
Object.keys(entry.attributes).forEach(function (a) { Object.keys(entry.attributes).forEach(function (a) {
var _a = a.toLowerCase(); var _a = a.toLowerCase()
if (!nofiltering && _a.length && _a[0] === '_') { if (!nofiltering && _a.length && _a[0] === '_') {
savedAttrs[a] = entry.attributes[a]; savedAttrs[a] = entry.attributes[a]
delete entry.attributes[a]; delete entry.attributes[a]
} else if (!nofiltering && self.notAttributes.indexOf(_a) !== -1) { } else if (!nofiltering && self.notAttributes.indexOf(_a) !== -1) {
savedAttrs[a] = entry.attributes[a]; savedAttrs[a] = entry.attributes[a]
delete entry.attributes[a]; delete entry.attributes[a]
} else if (all) { } 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; } 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({ entry = new SearchEntry({
objectName: typeof (save.dn) === 'string' ? parseDN(save.dn) : save.dn, objectName: typeof (save.dn) === 'string' ? parseDN(save.dn) : save.dn,
messageID: self.messageID, messageID: self.messageID,
log: self.log log: self.log
}); })
entry.fromObject(save); entry.fromObject(save)
} }
try { try {
if (this.log.debug()) if (this.log.debug) { this.log.debug('%s: sending: %j', this.connection.ldap.id, entry.json) }
this.log.debug('%s: sending: %j', this.connection.ldap.id, entry.json);
this.connection.write(entry.toBer()); this.connection.write(entry.toBer())
this.sentEntries++; this.sentEntries++
if (self._dtraceOp && self._dtraceId) { if (self._dtraceOp && self._dtraceId) {
dtrace.fire('server-search-entry', function () { dtrace.fire('server-search-entry', function () {
var c = self.connection || {ldap: {}}; var c = self.connection || { ldap: {} }
return [ return [
self._dtraceId || 0, self._dtraceId || 0,
(c.remoteAddress || ''), (c.remoteAddress || ''),
@ -98,54 +90,49 @@ SearchResponse.prototype.send = function (entry, nofiltering) {
(self.requestDN ? self.requestDN.toString() : ''), (self.requestDN ? self.requestDN.toString() : ''),
entry.objectName.toString(), entry.objectName.toString(),
entry.attributes.length entry.attributes.length
]; ]
}); })
} }
// Restore attributes // Restore attributes
Object.keys(savedAttrs || {}).forEach(function (k) { Object.keys(savedAttrs || {}).forEach(function (k) {
save.attributes[k] = savedAttrs[k]; save.attributes[k] = savedAttrs[k]
}); })
} catch (e) { } catch (e) {
this.log.warn(e, '%s failure to write message %j', this.log.warn(e, '%s failure to write message %j',
this.connection.ldap.id, this.json); this.connection.ldap.id, this.json)
} }
}; }
SearchResponse.prototype.createSearchEntry = function (object) { SearchResponse.prototype.createSearchEntry = function (object) {
assert.object(object); assert.object(object)
var entry = new SearchEntry({ var entry = new SearchEntry({
messageID: this.messageID, messageID: this.messageID,
log: this.log, log: this.log,
objectName: object.objectName || object.dn objectName: object.objectName || object.dn
}); })
entry.fromObject((object.attributes || object)); entry.fromObject((object.attributes || object))
return entry; return entry
}; }
SearchResponse.prototype.createSearchReference = function (uris) { SearchResponse.prototype.createSearchReference = function (uris) {
if (!uris) if (!uris) { throw new TypeError('uris ([string]) required') }
throw new TypeError('uris ([string]) required');
if (!Array.isArray(uris)) if (!Array.isArray(uris)) { uris = [uris] }
uris = [uris];
for (var i = 0; i < uris.length; i++) { for (var i = 0; i < uris.length; i++) {
if (typeof (uris[i]) == 'string') if (typeof (uris[i]) === 'string') { uris[i] = parseURL(uris[i]) }
uris[i] = parseURL(uris[i]);
} }
var self = this; var self = this
return new SearchReference({ return new SearchReference({
messageID: self.messageID, messageID: self.messageID,
log: self.log, log: self.log,
uris: uris uris: uris
}); })
}; }
/// --- Exports
///--- Exports module.exports = SearchResponse
module.exports = SearchResponse;

View File

@ -1,65 +1,62 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved. // Copyright 2011 Mark Cavage, Inc. All rights reserved.
var assert = require('assert-plus'); var assert = require('assert-plus')
var util = require('util'); var util = require('util')
var LDAPMessage = require('./message'); var LDAPMessage = require('./message')
var dn = require('../dn'); var dn = require('../dn')
var Protocol = require('../protocol'); var Protocol = require('../protocol')
/// --- Globals
///--- Globals var DN = dn.DN
var RDN = dn.RDN
var DN = dn.DN; /// --- API
var RDN = dn.RDN;
function UnbindRequest (options) {
options = options || {}
assert.object(options)
///--- API options.protocolOp = Protocol.LDAP_REQ_UNBIND
LDAPMessage.call(this, options)
function UnbindRequest(options) {
options = options || {};
assert.object(options);
options.protocolOp = Protocol.LDAP_REQ_UNBIND;
LDAPMessage.call(this, options);
} }
util.inherits(UnbindRequest, LDAPMessage); util.inherits(UnbindRequest, LDAPMessage)
Object.defineProperties(UnbindRequest.prototype, { Object.defineProperties(UnbindRequest.prototype, {
type: { type: {
get: function getType() { return 'UnbindRequest'; }, get: function getType () { return 'UnbindRequest' },
configurable: false configurable: false
}, },
_dn: { _dn: {
get: function getDN() { get: function getDN () {
if (this.connection) { if (this.connection) {
return this.connection.ldap.bindDN; return this.connection.ldap.bindDN
} else { } else {
return new DN([new RDN({cn: 'anonymous'})]); return new DN([new RDN({ cn: 'anonymous' })])
} }
}, },
configurable: false configurable: false
} }
}); })
UnbindRequest.prototype._parse = function (ber) { UnbindRequest.prototype._parse = function (ber) {
assert.ok(ber); assert.ok(ber)
return true; return true
}; }
UnbindRequest.prototype._toBer = function (ber) { UnbindRequest.prototype._toBer = function (ber) {
assert.ok(ber); assert.ok(ber)
return ber; return ber
}; }
UnbindRequest.prototype._json = function (j) { UnbindRequest.prototype._json = function (j) {
assert.ok(j); assert.ok(j)
return j; return j
}; }
/// --- Exports
///--- Exports module.exports = UnbindRequest
module.exports = UnbindRequest;

View File

@ -1,33 +1,32 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved. // Copyright 2011 Mark Cavage, Inc. All rights reserved.
var assert = require('assert-plus'); var assert = require('assert-plus')
var util = require('util'); var util = require('util')
var dtrace = require('../dtrace'); var dtrace = require('../dtrace')
var LDAPMessage = require('./result'); var LDAPMessage = require('./result')
var Protocol = require('../protocol'); // var Protocol = require('../protocol')
/// --- API
///--- API
// Ok, so there's really no such thing as an unbind 'response', but to make // Ok, so there's really no such thing as an unbind 'response', but to make
// the framework not suck, I just made this up, and have it stubbed so it's // the framework not suck, I just made this up, and have it stubbed so it's
// not such a one-off. // not such a one-off.
function UnbindResponse(options) { function UnbindResponse (options) {
options = options || {}; options = options || {}
assert.object(options); assert.object(options)
options.protocolOp = 0; options.protocolOp = 0
LDAPMessage.call(this, options); LDAPMessage.call(this, options)
} }
util.inherits(UnbindResponse, LDAPMessage); util.inherits(UnbindResponse, LDAPMessage)
Object.defineProperties(UnbindResponse.prototype, { Object.defineProperties(UnbindResponse.prototype, {
type: { type: {
get: function getType() { return 'UnbindResponse'; }, get: function getType () { return 'UnbindResponse' },
configurable: false configurable: false
} }
}); })
/** /**
* Special override that just ends the connection, if present. * Special override that just ends the connection, if present.
@ -35,16 +34,16 @@ Object.defineProperties(UnbindResponse.prototype, {
* @param {Number} status completely ignored. * @param {Number} status completely ignored.
*/ */
UnbindResponse.prototype.end = function (status) { UnbindResponse.prototype.end = function (status) {
assert.ok(this.connection); assert.ok(this.connection)
this.log.trace('%s: unbinding!', this.connection.ldap.id); this.log.trace('%s: unbinding!', this.connection.ldap.id)
this.connection.end(); this.connection.end()
var self = this; var self = this
if (self._dtraceOp && self._dtraceId) { if (self._dtraceOp && self._dtraceId) {
dtrace.fire('server-' + self._dtraceOp + '-done', function () { dtrace.fire('server-' + self._dtraceOp + '-done', function () {
var c = self.connection || {ldap: {}}; var c = self.connection || { ldap: {} }
return [ return [
self._dtraceId || 0, self._dtraceId || 0,
(c.remoteAddress || ''), (c.remoteAddress || ''),
@ -52,16 +51,15 @@ UnbindResponse.prototype.end = function (status) {
(self.requestDN ? self.requestDN.toString() : ''), (self.requestDN ? self.requestDN.toString() : ''),
0, 0,
'' ''
]; ]
}); })
} }
}; }
UnbindResponse.prototype._json = function (j) { UnbindResponse.prototype._json = function (j) {
return j; return j
}; }
/// --- Exports
///--- Exports module.exports = UnbindResponse
module.exports = UnbindResponse;

View File

@ -1,123 +1,109 @@
///--- Globals /// --- Globals
var parseDN = require('./dn').parse; // var parseDN = require('./dn').parse
var EntryChangeNotificationControl = var EntryChangeNotificationControl =
require('./controls').EntryChangeNotificationControl; require('./controls').EntryChangeNotificationControl
///--- API /// --- API
// Cache used to store connected persistent search clients // Cache used to store connected persistent search clients
function PersistentSearch() { function PersistentSearch () {
this.clientList = []; this.clientList = []
} }
PersistentSearch.prototype.addClient = function (req, res, callback) { PersistentSearch.prototype.addClient = function (req, res, callback) {
if (typeof (req) !== 'object') if (typeof (req) !== 'object') { throw new TypeError('req must be an object') }
throw new TypeError('req must be an object'); if (typeof (res) !== 'object') { throw new TypeError('res must be an object') }
if (typeof (res) !== 'object') if (callback && typeof (callback) !== 'function') { throw new TypeError('callback must be a function') }
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 log = req.log
var client = {}; var client = {}
client.req = req; client.req = req
client.res = res; client.res = res
log.debug('%s storing client', req.logId); log.debug('%s storing client', req.logId)
this.clientList.push(client); this.clientList.push(client)
log.debug('%s stored client', req.logId); log.debug('%s stored client', req.logId)
log.debug('%s total number of clients %s', log.debug('%s total number of clients %s',
req.logId, this.clientList.length); req.logId, this.clientList.length)
if (callback) if (callback) { callback(client) }
callback(client); }
};
PersistentSearch.prototype.removeClient = function (req, res, callback) { PersistentSearch.prototype.removeClient = function (req, res, callback) {
if (typeof (req) !== 'object') if (typeof (req) !== 'object') { throw new TypeError('req must be an object') }
throw new TypeError('req must be an object'); if (typeof (res) !== 'object') { throw new TypeError('res must be an object') }
if (typeof (res) !== 'object') if (callback && typeof (callback) !== 'function') { throw new TypeError('callback must be a function') }
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 log = req.log
log.debug('%s removing client', req.logId); log.debug('%s removing client', req.logId)
var client = {}; var client = {}
client.req = req; client.req = req
client.res = res; client.res = res
// remove the client if it exists // remove the client if it exists
this.clientList.forEach(function (element, index, array) { this.clientList.forEach(function (element, index, array) {
if (element.req === client.req) { if (element.req === client.req) {
log.debug('%s removing client from list', req.logId); log.debug('%s removing client from list', req.logId)
array.splice(index, 1); array.splice(index, 1)
} }
}); })
log.debug('%s number of persistent search clients %s', log.debug('%s number of persistent search clients %s',
req.logId, this.clientList.length); req.logId, this.clientList.length)
if (callback) if (callback) { callback(client) }
callback(client); }
};
function getOperationType (requestType) {
function getOperationType(requestType) {
switch (requestType) { switch (requestType) {
case 'AddRequest': case 'AddRequest':
case 'add': case 'add':
return 1; return 1
case 'DeleteRequest': case 'DeleteRequest':
case 'delete': case 'delete':
return 2; return 2
case 'ModifyRequest': case 'ModifyRequest':
case 'modify': case 'modify':
return 4; return 4
case 'ModifyDNRequest': case 'ModifyDNRequest':
case 'modrdn': case 'modrdn':
return 8; return 8
default: default:
throw new TypeError('requestType %s, is an invalid request type', throw new TypeError('requestType %s, is an invalid request type',
requestType); requestType)
} }
} }
function getEntryChangeNotificationControl (req, obj, callback) {
function getEntryChangeNotificationControl(req, obj, callback) {
// if we want to return a ECNC // if we want to return a ECNC
if (req.persistentSearch.value.returnECs) { if (req.persistentSearch.value.returnECs) {
var attrs = obj.attributes; var attrs = obj.attributes
var value = {}; var value = {}
value.changeType = getOperationType(attrs.changetype); value.changeType = getOperationType(attrs.changetype)
// if it's a modDN request, fill in the previous DN // if it's a modDN request, fill in the previous DN
if (value.changeType === 8 && attrs.previousDN) { if (value.changeType === 8 && attrs.previousDN) {
value.previousDN = attrs.previousDN; value.previousDN = attrs.previousDN
} }
value.changeNumber = attrs.changenumber; value.changeNumber = attrs.changenumber
return new EntryChangeNotificationControl({ value: value }); return new EntryChangeNotificationControl({ value: value })
} else { } else {
return false; return false
} }
} }
function checkChangeType (req, requestType) {
function checkChangeType(req, requestType) {
return (req.persistentSearch.value.changeTypes & return (req.persistentSearch.value.changeTypes &
getOperationType(requestType)); getOperationType(requestType))
} }
/// --- Exports
///--- Exports
module.exports = { module.exports = {
PersistentSearchCache: PersistentSearch, PersistentSearchCache: PersistentSearch,
checkChangeType: checkChangeType, checkChangeType: checkChangeType,
getEntryChangeNotificationControl: getEntryChangeNotificationControl getEntryChangeNotificationControl: getEntryChangeNotificationControl
}; }

View File

@ -1,6 +1,5 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved. // Copyright 2011 Mark Cavage, Inc. All rights reserved.
module.exports = { module.exports = {
// Misc // Misc
@ -51,4 +50,4 @@ module.exports = {
LDAP_REP_MODRDN: 0x6d, LDAP_REP_MODRDN: 0x6d,
LDAP_REP_COMPARE: 0x6f, LDAP_REP_COMPARE: 0x6f,
LDAP_REP_EXTENSION: 0x78 LDAP_REP_EXTENSION: 0x78
}; }

File diff suppressed because it is too large Load Diff

View File

@ -1,67 +1,72 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved. 'use strict'
var querystring = require('querystring');
var url = require('url');
var util = require('util');
var dn = require('./dn');
var filter = require('./filters/index');
const querystring = require('querystring')
const url = require('url')
const dn = require('./dn')
const filter = require('./filters/')
module.exports = { module.exports = {
parse: function (urlStr, parseDN) { parse: function (urlStr, parseDN) {
var u = url.parse(urlStr); let parsedURL
if (!u.protocol || !(u.protocol === 'ldap:' || u.protocol === 'ldaps:')) try {
throw new TypeError(urlStr + ' is an invalid LDAP url (protocol)'); parsedURL = new url.URL(urlStr)
} catch (error) {
throw new TypeError(urlStr + ' is an invalid LDAP url (scope)')
}
u.secure = (u.protocol === 'ldaps:'); if (!parsedURL.protocol || !(parsedURL.protocol === 'ldap:' || parsedURL.protocol === 'ldaps:')) { throw new TypeError(urlStr + ' is an invalid LDAP url (protocol)') }
if (!u.hostname) const u = {
u.hostname = 'localhost'; protocol: parsedURL.protocol,
hostname: parsedURL.hostname,
port: parsedURL.port,
pathname: parsedURL.pathname,
search: parsedURL.search,
href: parsedURL.href
}
u.secure = (u.protocol === 'ldaps:')
if (!u.hostname) { u.hostname = 'localhost' }
if (!u.port) { if (!u.port) {
u.port = (u.secure ? 636 : 389); u.port = (u.secure ? 636 : 389)
} else { } else {
u.port = parseInt(u.port, 10); u.port = parseInt(u.port, 10)
} }
if (u.pathname) { if (u.pathname) {
u.pathname = querystring.unescape(u.pathname.substr(1)); u.pathname = querystring.unescape(u.pathname.substr(1))
u.DN = parseDN ? dn.parse(u.pathname) : u.pathname; u.DN = parseDN ? dn.parse(u.pathname) : u.pathname
} }
if (u.search) { if (u.search) {
u.attributes = []; u.attributes = []
var tmp = u.search.substr(1).split('?'); var tmp = u.search.substr(1).split('?')
if (tmp && tmp.length) { if (tmp && tmp.length) {
if (tmp[0]) { if (tmp[0]) {
tmp[0].split(',').forEach(function (a) { tmp[0].split(',').forEach(function (a) {
u.attributes.push(querystring.unescape(a.trim())); u.attributes.push(querystring.unescape(a.trim()))
}); })
} }
} }
if (tmp[1]) { if (tmp[1]) {
if (tmp[1] !== 'base' && tmp[1] !== 'one' && tmp[1] !== 'sub') if (tmp[1] !== 'base' && tmp[1] !== 'one' && tmp[1] !== 'sub') { throw new TypeError(urlStr + ' is an invalid LDAP url (scope)') }
throw new TypeError(urlStr + ' is an invalid LDAP url (scope)'); u.scope = tmp[1]
u.scope = tmp[1];
} }
if (tmp[2]) { if (tmp[2]) {
u.filter = querystring.unescape(tmp[2]); u.filter = querystring.unescape(tmp[2])
} }
if (tmp[3]) { if (tmp[3]) {
u.extensions = querystring.unescape(tmp[3]); u.extensions = querystring.unescape(tmp[3])
} }
if (!u.scope) if (!u.scope) { u.scope = 'base' }
u.scope = 'base'; if (!u.filter) { u.filter = filter.parseString('(objectclass=*)') } else { u.filter = filter.parseString(u.filter) }
if (!u.filter)
u.filter = filter.parseString('(objectclass=*)');
else
u.filter = filter.parseString(u.filter);
} }
return u; return u
} }
}; }

View File

@ -1,55 +1,53 @@
{ {
"author": "Mark Cavage <mcavage@gmail.com>", "originalAuthor": "Mark Cavage <mcavage@gmail.com>",
"contributors": [
"Craig Baker",
"Austin King <shout@ozten.com>",
"Mathieu Lecarme <mathieu@garambrogne.net>>",
"Trent Mick <trentm@gmail.com>",
"Yunong Xiao <yunong@joyent.com>",
"Denis Vuyka <denis.vuyka@gmail.com>",
"Pedro Palazón <kusorbox@gmail.com>",
"Patrick Mooney <patrick.f.mooney@gmail.com>",
"Matt Simerson <matt@tnpi.net>"
],
"name": "ldapjs", "name": "ldapjs",
"homepage": "http://ldapjs.org", "homepage": "http://ldapjs.org",
"description": "LDAP client and server APIs", "description": "LDAP client and server APIs",
"version": "1.0.2", "version": "2.0.0",
"license": "MIT", "license": "MIT",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git://github.com/mcavage/node-ldapjs.git" "url": "git://github.com/ldapjs/node-ldapjs.git"
}, },
"main": "lib/index.js", "main": "lib/index.js",
"directories": { "directories": {
"bin": "./bin",
"lib": "./lib" "lib": "./lib"
}, },
"engines": { "engines": {
"node": ">=0.10" "node": ">=10.13.0"
}, },
"dependencies": { "dependencies": {
"asn1": "0.2.3", "abstract-logging": "^1.0.0",
"asn1": "^0.2.4",
"assert-plus": "^1.0.0", "assert-plus": "^1.0.0",
"bunyan": "^1.8.3",
"dashdash": "^1.14.0",
"backoff": "^2.5.0", "backoff": "^2.5.0",
"ldap-filter": "0.2.2", "ldap-filter": "^0.3.3",
"once": "^1.4.0", "once": "^1.4.0",
"vasync": "^1.6.4", "vasync": "^2.2.0",
"verror": "^1.8.1" "verror": "^1.8.1"
}, },
"optionalDependencies": {
"dtrace-provider": "~0.8"
},
"devDependencies": { "devDependencies": {
"node-uuid": "^1.4.7", "get-port": "^5.1.1",
"faucet": "0.0.1", "husky": "^3.0.4",
"istanbul": "^0.4.5", "snazzy": "^8.0.0",
"tape": "^4.6.2" "standard": "^14.0.2",
"tap": "14.10.1",
"uuid": "^3.3.3"
}, },
"scripts": { "scripts": {
"report": "./node_modules/.bin/istanbul report html && open ./coverage/lcov-report/index.html", "test": "tap --no-cov",
"test": "./node_modules/.bin/istanbul cover --print none test/test.js | ./node_modules/.bin/faucet" "test:ci": "tap --coverage-report=lcovonly",
"test:cov": "tap",
"test:cov:html": "tap --coverage-report=html",
"test:watch": "tap -n -w --no-coverage-report",
"test:integration": "tap --no-cov 'test-integration/**/*.test.js'",
"test:integration:local": "docker-compose up -d && npm run test:integration && docker-compose down",
"lint": "standard | snazzy",
"lint:ci": "standard"
},
"husky": {
"hooks": {
"pre-commit": "npm run lint && npm run test"
}
} }
} }

Some files were not shown because too many files have changed in this diff Show More