diff --git a/.dir-locals.el b/.dir-locals.el deleted file mode 100644 index 7f37d53..0000000 --- a/.dir-locals.el +++ /dev/null @@ -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) - ))) diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml new file mode 100644 index 0000000..31108b3 --- /dev/null +++ b/.github/workflows/integration.yml @@ -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 diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..c9468cb --- /dev/null +++ b/.github/workflows/main.yml @@ -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 diff --git a/.gitignore b/.gitignore index 8790ff7..7ab3e39 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,77 @@ -build -node_modules -coverage -*.log *.ldif *.tar.* *.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 + +# keys +*.pem +*.env.json +*.env diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 8ec36e0..0000000 --- a/.gitmodules +++ /dev/null @@ -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 diff --git a/.taprc b/.taprc new file mode 100644 index 0000000..c7447d3 --- /dev/null +++ b/.taprc @@ -0,0 +1,6 @@ +esm: false +jsx: false +ts: false + +files: + - 'test/**/*.test.js' diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index e6c5885..0000000 --- a/.travis.yml +++ /dev/null @@ -1,6 +0,0 @@ -language: node_js -node_js: - - "0.10" - - "0.12" - - "4" - - "stable" diff --git a/CHANGES.md b/CHANGES.md index fa27626..a29861e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,9 @@ # ldapjs Changelog +## 2.0.0 + +- Going foward, please see https://github.com/ldapjs/node-ldapjs/releases + ## 1.0.2 - Update dtrace-provider dependency diff --git a/LICENSE b/LICENSE index 9b5dcdb..a232e66 100644 --- a/LICENSE +++ b/LICENSE @@ -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 of this software and associated documentation files (the "Software"), to deal diff --git a/Makefile b/Makefile deleted file mode 100644 index 9175da9..0000000 --- a/Makefile +++ /dev/null @@ -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 diff --git a/README.md b/README.md index 58eeec0..5f79654 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,7 @@ # LDAPjs -[!['Build status'][travis_image_url]][travis_page_url] - -[travis_image_url]: https://api.travis-ci.org/joyent/node-ldapjs.svg -[travis_page_url]: https://travis-ci.org/joyent/node-ldapjs +[![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)] LDAPjs makes the LDAP protocol a first class citizen in Node.js. @@ -45,10 +43,12 @@ client on your system: npm install ldapjs +DTrace support is included in ldapjs. To enable it, `npm install dtrace-provider`. + ## License MIT. ## Bugs -See . +See . diff --git a/bin/ldapjs-add b/bin/ldapjs-add deleted file mode 100755 index 64dd6da..0000000 --- a/bin/ldapjs-add +++ /dev/null @@ -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; }); - }); -}); diff --git a/bin/ldapjs-compare b/bin/ldapjs-compare deleted file mode 100755 index ec7c317..0000000 --- a/bin/ldapjs-compare +++ /dev/null @@ -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; - }); - } - }); - }); -}); diff --git a/bin/ldapjs-delete b/bin/ldapjs-delete deleted file mode 100755 index e35011f..0000000 --- a/bin/ldapjs-delete +++ /dev/null @@ -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); - }); -}); diff --git a/bin/ldapjs-modify b/bin/ldapjs-modify deleted file mode 100755 index 8d12315..0000000 --- a/bin/ldapjs-modify +++ /dev/null @@ -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); - }); -}); diff --git a/bin/ldapjs-search b/bin/ldapjs-search deleted file mode 100755 index 20d6b5a..0000000 --- a/bin/ldapjs-search +++ /dev/null @@ -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 '); - } - 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 ', - 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; - }); - }); - }); -}); diff --git a/deps/javascriptlint b/deps/javascriptlint deleted file mode 160000 index e1bd0ab..0000000 --- a/deps/javascriptlint +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e1bd0abfd424811af469d1ece3af131d95443924 diff --git a/deps/jsstyle b/deps/jsstyle deleted file mode 160000 index d75b7ca..0000000 --- a/deps/jsstyle +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d75b7ca8308be17c80e2b120f2a01d4a0c20d8a8 diff --git a/deps/restdown b/deps/restdown deleted file mode 160000 index 34a843c..0000000 --- a/deps/restdown +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 34a843cfce0ff988bf5073706882722a61036786 diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..813c3b9 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,8 @@ +version: '3' + +services: + openldap: + image: docker.pkg.github.com/ldapjs/docker-test-openldap/openldap:latest + ports: + - 389:389 + - 636:636 diff --git a/docs/client.md b/docs/client.md index 4764a19..3aee755 100644 --- a/docs/client.md +++ b/docs/client.md @@ -26,19 +26,40 @@ client is: |---------------|-----------------------------------------------------------| |url |A valid LDAP URL (proto/host/port only) | |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)| |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)| |idleTimeout |Milliseconds after last activity before client emits idle event| |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 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. +### 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 @@ -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 must be one of: -||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`.|| +| Operation | Description | +|-----------|-------------| +| 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. @@ -375,6 +398,13 @@ Example: 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: client.unbind(function(err) { diff --git a/docs/examples.md b/docs/examples.md index 656bded..c12f93c 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -289,7 +289,7 @@ with ldapjs. var entry = req.toObject().attributes; 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']; if (entry.description) { diff --git a/docs/guide.md b/docs/guide.md index 6fb056c..1c56a34 100644 --- a/docs/guide.md +++ b/docs/guide.md @@ -409,7 +409,7 @@ the following code in as another handler (you'll need a var entry = req.toObject().attributes; 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']; if (entry.description) { @@ -496,7 +496,7 @@ As before, here's a breakdown of the code: var entry = req.toObject().attributes; 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: diff --git a/docs/server.md b/docs/server.md index 8d41882..efaa25a 100644 --- a/docs/server.md +++ b/docs/server.md @@ -16,10 +16,22 @@ The code to create a new server looks like: 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.|| ||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 ### maxConnections @@ -27,9 +39,10 @@ The full list of options is: Set this property to reject connections when the server's connection count gets 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 @@ -53,7 +66,7 @@ available. Example: 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 `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 The LDAP server API is meant to be the LDAP-equivalent of the express/restify diff --git a/examples/inmemory.js b/examples/inmemory.js index db3523c..3937379 100644 --- a/examples/inmemory.js +++ b/examples/inmemory.js @@ -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 */ - var isSearch = (req instanceof ldap.SearchRequest); - if (!req.connection.ldap.bindDN.equals('cn=root') && !isSearch) - return next(new ldap.InsufficientAccessRightsError()); + var isSearch = (req instanceof ldap.SearchRequest) + if (!req.connection.ldap.bindDN.equals('cn=root') && !isSearch) { return next(new ldap.InsufficientAccessRightsError()) } - return next(); + return next() } +/// --- Globals -///--- Globals - -var SUFFIX = 'o=smartdc'; -var db = {}; -var server = ldap.createServer(); - - +var SUFFIX = 'o=smartdc' +var db = {} +var server = ldap.createServer() server.bind('cn=root', function (req, res, next) { - if (req.dn.toString() !== 'cn=root' || req.credentials !== 'secret') - return next(new ldap.InvalidCredentialsError()); + if (req.dn.toString() !== 'cn=root' || req.credentials !== 'secret') { return next(new ldap.InvalidCredentialsError()) } - res.end(); - return next(); -}); + res.end() + return next() +}) server.add(SUFFIX, authorize, function (req, res, next) { - var dn = req.dn.toString(); + var dn = req.dn.toString() - if (db[dn]) - return next(new ldap.EntryAlreadyExistsError(dn)); + if (db[dn]) { return next(new ldap.EntryAlreadyExistsError(dn)) } - db[dn] = req.toObject().attributes; - res.end(); - return next(); -}); + db[dn] = req.toObject().attributes + res.end() + return next() +}) server.bind(SUFFIX, function (req, res, next) { - var dn = req.dn.toString(); - if (!db[dn]) - return next(new ldap.NoSuchObjectError(dn)); + var dn = req.dn.toString() + if (!db[dn]) { return next(new ldap.NoSuchObjectError(dn)) } - if (!db[dn].userpassword) - return next(new ldap.NoSuchAttributeError('userPassword')); + if (!db[dn].userpassword) { return next(new ldap.NoSuchAttributeError('userPassword')) } - if (db[dn].userpassword.indexOf(req.credentials) === -1) - return next(new ldap.InvalidCredentialsError()); + if (db[dn].userpassword.indexOf(req.credentials) === -1) { return next(new ldap.InvalidCredentialsError()) } - res.end(); - return next(); -}); + res.end() + return next() +}) server.compare(SUFFIX, authorize, function (req, res, next) { - var dn = req.dn.toString(); - if (!db[dn]) - return next(new ldap.NoSuchObjectError(dn)); + var dn = req.dn.toString() + if (!db[dn]) { return next(new ldap.NoSuchObjectError(dn)) } - if (!db[dn][req.attribute]) - return next(new ldap.NoSuchAttributeError(req.attribute)); + if (!db[dn][req.attribute]) { return next(new ldap.NoSuchAttributeError(req.attribute)) } - var matches = false; - var vals = db[dn][req.attribute]; + var matches = false + var vals = db[dn][req.attribute] for (var i = 0; i < vals.length; i++) { if (vals[i] === req.value) { - matches = true; - break; + matches = true + break } } - res.end(matches); - return next(); -}); + res.end(matches) + return next() +}) server.del(SUFFIX, authorize, function (req, res, next) { - var dn = req.dn.toString(); - if (!db[dn]) - return next(new ldap.NoSuchObjectError(dn)); + var dn = req.dn.toString() + if (!db[dn]) { return next(new ldap.NoSuchObjectError(dn)) } - delete db[dn]; + delete db[dn] - res.end(); - return next(); -}); + res.end() + return next() +}) server.modify(SUFFIX, authorize, function (req, res, next) { - var dn = req.dn.toString(); - if (!req.changes.length) - return next(new ldap.ProtocolError('changes required')); - if (!db[dn]) - return next(new ldap.NoSuchObjectError(dn)); + var dn = req.dn.toString() + if (!req.changes.length) { return next(new ldap.ProtocolError('changes required')) } + 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++) { - mod = req.changes[i].modification; + mod = req.changes[i].modification switch (req.changes[i].operation) { - case 'replace': - if (!entry[mod.type]) - return next(new ldap.NoSuchAttributeError(mod.type)); + case 'replace': + if (!entry[mod.type]) { return next(new ldap.NoSuchAttributeError(mod.type)) } - if (!mod.vals || !mod.vals.length) { - delete entry[mod.type]; - } else { - entry[mod.type] = mod.vals; - } + if (!mod.vals || !mod.vals.length) { + delete entry[mod.type] + } else { + entry[mod.type] = mod.vals + } - break; + break - case 'add': - if (!entry[mod.type]) { - entry[mod.type] = mod.vals; - } else { - mod.vals.forEach(function (v) { - if (entry[mod.type].indexOf(v) === -1) - entry[mod.type].push(v); - }); - } + case 'add': + if (!entry[mod.type]) { + entry[mod.type] = mod.vals + } else { + mod.vals.forEach(function (v) { + if (entry[mod.type].indexOf(v) === -1) { entry[mod.type].push(v) } + }) + } - break; + break - case 'delete': - if (!entry[mod.type]) - return next(new ldap.NoSuchAttributeError(mod.type)); + case 'delete': + if (!entry[mod.type]) { return next(new ldap.NoSuchAttributeError(mod.type)) } - delete entry[mod.type]; + delete entry[mod.type] - break; + break } } - res.end(); - return next(); -}); + res.end() + return next() +}) server.search(SUFFIX, authorize, function (req, res, next) { - var dn = req.dn.toString(); - if (!db[dn]) - return next(new ldap.NoSuchObjectError(dn)); + var dn = req.dn.toString() + if (!db[dn]) { return next(new ldap.NoSuchObjectError(dn)) } - var scopeCheck; + var scopeCheck switch (req.scope) { - case 'base': - if (req.filter.matches(db[dn])) { - res.send({ - dn: dn, - attributes: db[dn] - }); - } + case 'base': + if (req.filter.matches(db[dn])) { + res.send({ + dn: dn, + attributes: db[dn] + }) + } - res.end(); - return next(); + res.end() + return next() - case 'one': - scopeCheck = function (k) { - if (req.dn.equals(k)) - return true; + case 'one': + scopeCheck = function (k) { + if (req.dn.equals(k)) { return true } - var parent = ldap.parseDN(k).parent(); - return (parent ? parent.equals(req.dn) : false); - }; - break; + var parent = ldap.parseDN(k).parent() + return (parent ? parent.equals(req.dn) : false) + } + break - case 'sub': - scopeCheck = function (k) { - return (req.dn.equals(k) || req.dn.parentOf(k)); - }; + case 'sub': + scopeCheck = function (k) { + return (req.dn.equals(k) || req.dn.parentOf(k)) + } - break; + break } Object.keys(db).forEach(function (key) { - if (!scopeCheck(key)) - return; + if (!scopeCheck(key)) { return } if (req.filter.matches(db[key])) { res.send({ dn: key, attributes: db[key] - }); + }) } - }); + }) - res.end(); - return next(); -}); + res.end() + return next() +}) - - -///--- Fire it up +/// --- Fire it up server.listen(1389, function () { - console.log('LDAP server up at: %s', server.url); -}); + console.log('LDAP server up at: %s', server.url) +}) diff --git a/lib/assert.js b/lib/assert.js index 2dc693b..a757e84 100644 --- a/lib/assert.js +++ b/lib/assert.js @@ -1,61 +1,54 @@ // Copyright 2015 Joyent, Inc. -var assert = require('assert'); -var util = require('util'); +var assert = require('assert') +var util = require('util') -var isDN = require('./dn').DN.isDN; -var isAttribute = require('./attribute').isAttribute; +var isDN = require('./dn').DN.isDN +var isAttribute = require('./attribute').isAttribute - -///--- Helpers +/// --- Helpers // Copied from mcavage/node-assert-plus -function _assert(arg, type, name) { - name = name || type; +function _assert (arg, type, name) { + name = name || type throw new assert.AssertionError({ message: util.format('%s (%s) required', name, type), actual: typeof (arg), expected: type, operator: '===', stackStartFunction: _assert.caller - }); + }) } +/// --- API -///--- API - -function stringDN(input, name) { - if (isDN(input) || typeof (input) === 'string') - return; - _assert(input, 'DN or string', name); +function stringDN (input, name) { + if (isDN(input) || typeof (input) === 'string') { return } + _assert(input, 'DN or string', name) } -function optionalStringDN(input, name) { - if (input === undefined || isDN(input) || typeof (input) === 'string') - return; - _assert(input, 'DN or string', name); +function optionalStringDN (input, name) { + if (input === undefined || isDN(input) || typeof (input) === 'string') { return } + _assert(input, 'DN or string', name) } -function optionalDN(input, name) { - if (input !== undefined && !isDN(input)) - _assert(input, 'DN', name); +function optionalDN (input, name) { + if (input !== undefined && !isDN(input)) { _assert(input, 'DN', name) } } -function optionalArrayOfAttribute(input, name) { - if (input === undefined) - return; +function optionalArrayOfAttribute (input, name) { + if (input === undefined) { return } if (!Array.isArray(input) || - input.some(function (v) { return !isAttribute(v); })) { - _assert(input, 'array of Attribute', name); + input.some(function (v) { return !isAttribute(v) })) { + _assert(input, 'array of Attribute', name) } } - -///--- Exports +/// --- Exports module.exports = { stringDN: stringDN, optionalStringDN: optionalStringDN, optionalDN: optionalDN, optionalArrayOfAttribute: optionalArrayOfAttribute -}; +} diff --git a/lib/attribute.js b/lib/attribute.js index aa73562..6f41a23 100644 --- a/lib/attribute.js +++ b/lib/attribute.js @@ -1,174 +1,160 @@ // 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 (typeof (options) !== 'object') - throw new TypeError('options must be an object'); - if (options.type && typeof (options.type) !== 'string') - throw new TypeError('options.type must be a string'); + if (typeof (options) !== 'object') { throw new TypeError('options must be an object') } + if (options.type && typeof (options.type) !== 'string') { throw new TypeError('options.type must be a string') } } else { - options = {}; + options = {} } - this.type = options.type || ''; - this._vals = []; + this.type = options.type || '' + this._vals = [] - if (options.vals !== undefined && options.vals !== null) - this.vals = options.vals; + if (options.vals !== undefined && options.vals !== null) { this.vals = options.vals } } -module.exports = Attribute; +module.exports = Attribute Object.defineProperties(Attribute.prototype, { buffers: { - get: function getBuffers() { - return this._vals; + get: function getBuffers () { + return this._vals }, configurable: false }, json: { - get: function getJson() { + get: function getJson () { return { type: this.type, vals: this.vals - }; + } }, configurable: false }, vals: { - get: function getVals() { - var eType = _bufferEncoding(this.type); + get: function getVals () { + var eType = _bufferEncoding(this.type) return this._vals.map(function (v) { - return v.toString(eType); - }); + return v.toString(eType) + }) }, - set: function setVals(vals) { - var self = this; - this._vals = []; + set: function setVals (vals) { + var self = this + this._vals = [] if (Array.isArray(vals)) { vals.forEach(function (v) { - self.addValue(v); - }); + self.addValue(v) + }) } else { - self.addValue(vals); + self.addValue(vals) } }, configurable: false } -}); +}) - -Attribute.prototype.addValue = function addValue(val) { +Attribute.prototype.addValue = function addValue (val) { if (Buffer.isBuffer(val)) { - this._vals.push(val); + this._vals.push(val) } else { - this._vals.push(new Buffer(val + '', _bufferEncoding(this.type))); + this._vals.push(Buffer.from(val + '', _bufferEncoding(this.type))) } -}; - +} /* BEGIN JSSTYLED */ -Attribute.compare = function compare(a, b) { +Attribute.compare = function compare (a, 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.vals.length < b.vals.length) return -1; - if (a.vals.length > b.vals.length) 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 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 */ +Attribute.prototype.parse = function parse (ber) { + assert.ok(ber) -Attribute.prototype.parse = function parse(ber) { - assert.ok(ber); - - ber.readSequence(); - this.type = ber.readString(); + ber.readSequence() + this.type = ber.readString() if (ber.peek() === Protocol.LBER_SET) { if (ber.readSequence(Protocol.LBER_SET)) { - var end = ber.offset + ber.length; - while (ber.offset < end) - this._vals.push(ber.readString(asn1.Ber.OctetString, true)); + var end = ber.offset + ber.length + while (ber.offset < end) { 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) { - assert.ok(ber); - - ber.startSequence(); - ber.writeString(this.type); - ber.startSequence(Protocol.LBER_SET); + ber.startSequence() + ber.writeString(this.type) + ber.startSequence(Protocol.LBER_SET) if (this._vals.length) { this._vals.forEach(function (b) { - ber.writeByte(asn1.Ber.OctetString); - ber.writeLength(b.length); - for (var i = 0; i < b.length; i++) - ber.writeByte(b[i]); - }); + ber.writeByte(asn1.Ber.OctetString) + ber.writeLength(b.length) + for (var i = 0; i < b.length; i++) { ber.writeByte(b[i]) } + }) } else { - ber.writeStringArray([]); + ber.writeStringArray([]) } - ber.endSequence(); - ber.endSequence(); - - return ber; -}; + ber.endSequence() + ber.endSequence() + return ber +} Attribute.prototype.toString = function () { - return JSON.stringify(this.json); -}; - + return JSON.stringify(this.json) +} 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') { - return false; + return false } if (attr instanceof Attribute) { - return true; + return true } if ((typeof (attr.toBer) === 'function') && (typeof (attr.type) === 'string') && (Array.isArray(attr.vals)) && (attr.vals.filter(function (item) { - return (typeof (item) === 'string' || - Buffer.isBuffer(item)); - }).length === attr.vals.length)) { - return true; + return (typeof (item) === 'string' || + Buffer.isBuffer(item)) + }).length === attr.vals.length)) { + return true } - return false; -}; - - -function _bufferEncoding(type) { - /* JSSTYLED */ - return /;binary$/.test(type) ? 'base64' : 'utf8'; + return false +} + +function _bufferEncoding (type) { + /* JSSTYLED */ + return /;binary$/.test(type) ? 'base64' : 'utf8' } diff --git a/lib/change.js b/lib/change.js index 4e23de8..e65c45e 100644 --- a/lib/change.js +++ b/lib/change.js @@ -1,129 +1,127 @@ // Copyright 2011 Mark Cavage, Inc. All rights reserved. -var assert = require('assert-plus'); +var assert = require('assert-plus') -var Attribute = require('./attribute'); -var Protocol = require('./protocol'); +var Attribute = require('./attribute') +// var Protocol = require('./protocol') +/// --- API -///--- API - -function Change(options) { +function Change (options) { if (options) { - assert.object(options); - assert.optionalString(options.operation); + assert.object(options) + assert.optionalString(options.operation) } else { - options = {}; + options = {} } - this._modification = false; - this.operation = options.operation || options.type || 'add'; - this.modification = options.modification || {}; + this._modification = false + this.operation = options.operation || options.type || 'add' + this.modification = options.modification || {} } Object.defineProperties(Change.prototype, { operation: { - get: function getOperation() { + get: function getOperation () { switch (this._operation) { - case 0x00: return 'add'; - case 0x01: return 'delete'; - case 0x02: return 'replace'; - default: - throw new Error('0x' + this._operation.toString(16) + ' is invalid'); + case 0x00: return 'add' + case 0x01: return 'delete' + case 0x02: return 'replace' + default: + throw new Error('0x' + this._operation.toString(16) + ' is invalid') } }, - set: function setOperation(val) { - assert.string(val); + set: function setOperation (val) { + assert.string(val) switch (val.toLowerCase()) { - case 'add': - this._operation = 0x00; - break; - case 'delete': - this._operation = 0x01; - break; - case 'replace': - this._operation = 0x02; - break; - default: - throw new Error('Invalid operation type: 0x' + val.toString(16)); + case 'add': + this._operation = 0x00 + break + case 'delete': + this._operation = 0x01 + break + case 'replace': + this._operation = 0x02 + break + default: + throw new Error('Invalid operation type: 0x' + val.toString(16)) } }, configurable: false }, modification: { - get: function getModification() { - return this._modification; + get: function getModification () { + return this._modification }, - set: function setModification(val) { + set: function setModification (val) { if (Attribute.isAttribute(val)) { - this._modification = val; - return; + this._modification = val + return } // Does it have an attribute-like structure - if (Object.keys(val).length == 2 && + if (Object.keys(val).length === 2 && typeof (val.type) === 'string' && Array.isArray(val.vals)) { this._modification = new Attribute({ type: val.type, vals: val.vals - }); - return; + }) + return } - var keys = Object.keys(val); + var keys = Object.keys(val) 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) { - return; + return } - var k = keys[0]; - var _attr = new Attribute({type: k}); + var k = keys[0] + var _attr = new Attribute({ type: k }) if (Array.isArray(val[k])) { val[k].forEach(function (v) { - _attr.addValue(v.toString()); - }); - } else { - _attr.addValue(val[k].toString()); + _attr.addValue(v.toString()) + }) + } else if (Buffer.isBuffer(val[k])) { + _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 }, json: { - get: function getJSON() { + get: function getJSON () { return { operation: this.operation, modification: this._modification ? this._modification.json : {} - }; + } }, configurable: false } -}); +}) -Change.isChange = function isChange(change) { +Change.isChange = function isChange (change) { if (!change || typeof (change) !== 'object') { - return false; + return false } if ((change instanceof Change) || ((typeof (change.toBer) === 'function') && (change.modification !== undefined) && (change.operation !== undefined))) { - return true; + return true } - return false; -}; + return false +} Change.compare = function (a, b) { - if (!Change.isChange(a) || !Change.isChange(b)) - throw new TypeError('can only compare Changes'); + if (!Change.isChange(a) || !Change.isChange(b)) { throw new TypeError('can only compare Changes') } - if (a.operation < b.operation) - return -1; - if (a.operation > b.operation) - return 1; + 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. @@ -132,86 +130,83 @@ Change.compare = function (a, b) { * @param {Object} obj the object to apply it to. * @param {Boolean} scalar convert single-item arrays to scalars. Default: false */ -Change.apply = function apply(change, obj, scalar) { - assert.string(change.operation); - assert.string(change.modification.type); - assert.ok(Array.isArray(change.modification.vals)); - assert.object(obj); +Change.apply = function apply (change, obj, scalar) { + assert.string(change.operation) + assert.string(change.modification.type) + assert.ok(Array.isArray(change.modification.vals)) + assert.object(obj) - var type = change.modification.type; - var vals = change.modification.vals; - var data = obj[type]; + var type = change.modification.type + var vals = change.modification.vals + var data = obj[type] if (data !== undefined) { if (!Array.isArray(data)) { - data = [data]; + data = [data] } } else { - data = []; + data = [] } switch (change.operation) { - case 'replace': - if (vals.length === 0) { + case 'replace': + if (vals.length === 0) { // replace empty is a delete - delete obj[type]; - return obj; - } else { - data = vals; - } - break; - case 'add': + delete obj[type] + return obj + } else { + data = vals + } + break + case 'add': // add only new unique entries - var newValues = vals.filter(function (entry) { - return (data.indexOf(entry) === -1); - }); - data = data.concat(newValues); - break; - case 'delete': - data = data.filter(function (entry) { - return (vals.indexOf(entry) === -1); - }); - if (data.length === 0) { + var newValues = vals.filter(function (entry) { + return (data.indexOf(entry) === -1) + }) + data = data.concat(newValues) + break + case 'delete': + data = data.filter(function (entry) { + return (vals.indexOf(entry) === -1) + }) + if (data.length === 0) { // Erase the attribute if empty - delete obj[type]; - return obj; - } - break; - default: - break; + delete obj[type] + return obj + } + break + default: + break } if (scalar && data.length === 1) { // store single-value outputs as scalars, if requested - obj[type] = data[0]; + obj[type] = data[0] } else { - obj[type] = data; + obj[type] = data } - return obj; -}; - + return obj +} Change.prototype.parse = function (ber) { - assert.ok(ber); + assert.ok(ber) - ber.readSequence(); - this._operation = ber.readEnumeration(); - this._modification = new Attribute(); - this._modification.parse(ber); - - return true; -}; + ber.readSequence() + this._operation = ber.readEnumeration() + this._modification = new Attribute() + this._modification.parse(ber) + return true +} Change.prototype.toBer = function (ber) { - assert.ok(ber); + assert.ok(ber) - ber.startSequence(); - ber.writeEnumeration(this._operation); - ber = this._modification.toBer(ber); - ber.endSequence(); + ber.startSequence() + ber.writeEnumeration(this._operation) + ber = this._modification.toBer(ber) + ber.endSequence() - return ber; -}; + return ber +} +/// --- Exports -///--- Exports - -module.exports = Change; +module.exports = Change diff --git a/lib/client/client.js b/lib/client/client.js index c26ec0e..b254d50 100644 --- a/lib/client/client.js +++ b/lib/client/client.js @@ -1,291 +1,96 @@ -// Copyright 2011 Mark Cavage, Inc. All rights reserved. +'use strict' -var EventEmitter = require('events').EventEmitter; -var net = require('net'); -var tls = require('tls'); -var util = require('util'); +const requestQueueFactory = require('./request-queue') +const messageTrackerFactory = require('./message-tracker') +const { MAX_MSGID } = require('./constants') -var once = require('once'); -var backoff = require('backoff'); -var vasync = require('vasync'); -var assert = require('assert-plus'); -var VError = require('verror').VError; +var EventEmitter = require('events').EventEmitter +var net = require('net') +var tls = require('tls') +var util = require('util') -var Attribute = require('../attribute'); -var Change = require('../change'); -var Control = require('../controls/index').Control; -var SearchPager = require('./search_pager'); -var Protocol = require('../protocol'); -var dn = require('../dn'); -var errors = require('../errors'); -var filters = require('../filters'); -var messages = require('../messages'); -var url = require('../url'); +var once = require('once') +var backoff = require('backoff') +var vasync = require('vasync') +var assert = require('assert-plus') +var VError = require('verror').VError +var Attribute = require('../attribute') +var Change = require('../change') +var Control = require('../controls/index').Control +var SearchPager = require('./search_pager') +var Protocol = require('../protocol') +var dn = require('../dn') +var errors = require('../errors') +var filters = require('../filters') +var messages = require('../messages') +var url = require('../url') +var CorkedEmitter = require('../corked_emitter') +/// --- Globals -///--- Globals +var AbandonRequest = messages.AbandonRequest +var AddRequest = messages.AddRequest +var BindRequest = messages.BindRequest +var CompareRequest = messages.CompareRequest +var DeleteRequest = messages.DeleteRequest +var ExtendedRequest = messages.ExtendedRequest +var ModifyRequest = messages.ModifyRequest +var ModifyDNRequest = messages.ModifyDNRequest +var SearchRequest = messages.SearchRequest +var UnbindRequest = messages.UnbindRequest +var UnbindResponse = messages.UnbindResponse -var AbandonRequest = messages.AbandonRequest; -var AddRequest = messages.AddRequest; -var BindRequest = messages.BindRequest; -var CompareRequest = messages.CompareRequest; -var DeleteRequest = messages.DeleteRequest; -var ExtendedRequest = messages.ExtendedRequest; -var ModifyRequest = messages.ModifyRequest; -var ModifyDNRequest = messages.ModifyDNRequest; -var SearchRequest = messages.SearchRequest; -var UnbindRequest = messages.UnbindRequest; -var UnbindResponse = messages.UnbindResponse; +var LDAPResult = messages.LDAPResult +var SearchEntry = messages.SearchEntry +var SearchReference = messages.SearchReference +// var SearchResponse = messages.SearchResponse +var Parser = messages.Parser -var LDAPResult = messages.LDAPResult; -var SearchEntry = messages.SearchEntry; -var SearchReference = messages.SearchReference; -var SearchResponse = messages.SearchResponse; -var Parser = messages.Parser; +var PresenceFilter = filters.PresenceFilter -var PresenceFilter = filters.PresenceFilter; +var ConnectionError = errors.ConnectionError -var ConnectionError = errors.ConnectionError; - -var CMP_EXPECT = [errors.LDAP_COMPARE_TRUE, errors.LDAP_COMPARE_FALSE]; -var MAX_MSGID = Math.pow(2, 31) - 1; +var CMP_EXPECT = [errors.LDAP_COMPARE_TRUE, errors.LDAP_COMPARE_FALSE] // node 0.6 got rid of FDs, so make up a client id for logging -var CLIENT_ID = 0; +var CLIENT_ID = 0 +/// --- Internal Helpers +function nextClientId () { + if (++CLIENT_ID === MAX_MSGID) { return 1 } -///--- Internal Helpers - -function nextClientId() { - if (++CLIENT_ID === MAX_MSGID) - return 1; - - return CLIENT_ID; + return CLIENT_ID } -function validateControls(controls) { +function validateControls (controls) { if (Array.isArray(controls)) { controls.forEach(function (c) { - if (!(c instanceof Control)) - throw new TypeError('controls must be [Control]'); - }); + if (!(c instanceof Control)) { throw new TypeError('controls must be [Control]') } + }) } else if (controls instanceof Control) { - controls = [controls]; + controls = [controls] } else { - throw new TypeError('controls must be [Control]'); + throw new TypeError('controls must be [Control]') } - return controls; + return controls } -function ensureDN(input, strict) { +function ensureDN (input, strict) { if (dn.DN.isDN(input)) { - return dn; + return dn } else if (strict) { - return dn.parse(input); + return dn.parse(input) } else if (typeof (input) === 'string') { - return input; + return input } else { - throw new Error('invalid DN'); + throw new Error('invalid DN') } } -/** - * Queue to contain LDAP requests. - * - * @param {Object} opts queue options - * - * Accepted Options: - * - size: Maximum queue size - * - timeout: Set timeout between first queue insertion and queue flush. - */ -function RequestQueue(opts) { - if (!opts || typeof (opts) !== 'object') { - opts = {}; - } - this.size = (opts.size > 0) ? opts.size : Infinity; - this.timeout = (opts.timeout > 0) ? opts.timeout : 0; - this._queue = []; - this._timer = null; - this._frozen = false; -} - -/** - * Insert request into queue. - * - */ -RequestQueue.prototype.enqueue = function enqueue(msg, expect, emitter, cb) { - if (this._queue.length >= this.size || this._frozen) { - return false; - } - var self = this; - this._queue.push([msg, expect, emitter, cb]); - if (this.timeout > 0) { - if (this._timer !== null) { - this._timer = setTimeout(function () { - // If queue times out, don't allow new entries until thawed - self.freeze(); - self.purge(); - }, this.timeout); - } - } - return true; -}; - -/** - * Process all queued requests with callback. - */ -RequestQueue.prototype.flush = function flush(cb) { - if (this._timer) { - clearTimeout(this._timer); - this._timer = null; - } - var items = this._queue; - this._queue = []; - items.forEach(function (req) { - cb(req[0], req[1], req[2], req[3]); - }); -}; - -/** - * Purge all queued requests with an error. - */ -RequestQueue.prototype.purge = function purge() { - this.flush(function (msg, expect, emitter, cb) { - cb(new errors.TimeoutError('request queue timeout')); - }); -}; - -/** - * Freeze queue, refusing any new entries. - */ -RequestQueue.prototype.freeze = function freeze() { - this._frozen = true; -}; - -/** - * Thaw queue, allowing new entries again. - */ -RequestQueue.prototype.thaw = function thaw() { - this._frozen = false; -}; - - -/** - * Track message callback by messageID. - */ -function MessageTracker(opts) { - assert.object(opts); - assert.string(opts.id); - assert.object(opts.parser); - - this.id = opts.id; - this._msgid = 0; - this._messages = {}; - this._abandoned = {}; - this.parser = opts.parser; - - var self = this; - this.__defineGetter__('pending', function () { - return Object.keys(self._messages); - }); -} - -/** - * Record a messageID and callback. - */ -MessageTracker.prototype.track = function track(message, callback) { - var msgid = this._nextID(); - message.messageID = msgid; - this._messages[msgid] = callback; - return msgid; -}; - -/** - * Fetch callback based on messageID. - */ -MessageTracker.prototype.fetch = function fetch(msgid) { - var msg = this._messages[msgid]; - if (msg) { - this._purgeAbandoned(msgid); - return msg; - } - // It's possible that the server has not received the abandon request yet. - // While waiting for evidence that the abandon has been received, incoming - // messages that match the abandoned msgid will be handled as normal. - msg = this._abandoned[msgid]; - if (msg) { - return msg.cb; - } - return null; -}; - -/** - * Cease tracking for a given messageID. - */ -MessageTracker.prototype.remove = function remove(msgid) { - if (this._messages[msgid]) { - delete this._messages[msgid]; - } else if (this._abandoned[msgid]) { - delete this._abandoned[msgid]; - } -}; - -/** - * Mark a messageID as abandoned. - */ -MessageTracker.prototype.abandon = function abandonMsg(msgid) { - if (this._messages[msgid]) { - // Keep track of "when" the message was abandoned - this._abandoned[msgid] = { - age: this._msgid, - cb: this._messages[msgid] - }; - delete this._messages[msgid]; - } -}; - -/** - * Purge old items from abandoned list. - */ -MessageTracker.prototype._purgeAbandoned = function _purgeAbandoned(msgid) { - var self = this; - // Is (comp >= ref) according to sliding window - function geWindow(ref, comp) { - var max = ref + (MAX_MSGID/2); - var 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)); - } - } - - Object.keys(this._abandoned).forEach(function (id) { - // Abandoned messageIDs can be forgotten if a received messageID is "newer" - if (geWindow(self._abandoned[id].age, msgid)) { - self._abandoned[id].cb(new errors.AbandonedError( - 'client request abandoned')); - delete self._abandoned[id]; - } - }); -}; - -/** - * Allocate the next messageID according to a sliding window. - */ -MessageTracker.prototype._nextID = function _nextID() { - if (++this._msgid >= MAX_MSGID) - this._msgid = 1; - - return this._msgid; -}; - -///--- API +/// --- API /** * Constructs a new client. @@ -299,45 +104,44 @@ MessageTracker.prototype._nextID = function _nextID() { * @param {Object} options must have either url or socketPath. * @throws {TypeError} on bad input. */ -function Client(options) { - assert.ok(options); +function Client (options) { + assert.ok(options) - EventEmitter.call(this, options); + EventEmitter.call(this, options) - var self = this; - var _url; - if (options.url) - _url = url.parse(options.url); - this.host = _url ? _url.hostname : undefined; - this.port = _url ? _url.port : false; - this.secure = _url ? _url.secure : false; - this.url = _url; - this.tlsOptions = options.tlsOptions; - this.socketPath = options.socketPath || false; + var self = this + var _url + if (options.url) { _url = url.parse(options.url) } + this.host = _url ? _url.hostname : undefined + this.port = _url ? _url.port : false + this.secure = _url ? _url.secure : false + this.url = _url + this.tlsOptions = options.tlsOptions + this.socketPath = options.socketPath || false - this.log = options.log.child({clazz: 'Client'}, true); + this.log = options.log.child({ clazz: 'Client' }, true) - this.timeout = parseInt((options.timeout || 0), 10); - this.connectTimeout = parseInt((options.connectTimeout || 0), 10); - this.idleTimeout = parseInt((options.idleTimeout || 0), 10); + this.timeout = parseInt((options.timeout || 0), 10) + this.connectTimeout = parseInt((options.connectTimeout || 0), 10) + this.idleTimeout = parseInt((options.idleTimeout || 0), 10) if (options.reconnect) { // Fall back to defaults if options.reconnect === true - var rOpts = (typeof (options.reconnect) === 'object') ? - options.reconnect : {}; + var rOpts = (typeof (options.reconnect) === 'object') + ? options.reconnect : {} this.reconnect = { initialDelay: parseInt(rOpts.initialDelay || 100, 10), maxDelay: parseInt(rOpts.maxDelay || 10000, 10), failAfter: parseInt(rOpts.failAfter, 10) || Infinity - }; + } } - this.strictDN = (options.strictDN !== undefined) ? options.strictDN : true; + this.strictDN = (options.strictDN !== undefined) ? options.strictDN : true - this.queue = new RequestQueue({ + this.queue = requestQueueFactory({ size: parseInt((options.queueSize || 0), 10), timeout: parseInt((options.queueTimeout || 0), 10) - }); + }) if (options.queueDisable) { - this.queue.freeze(); + this.queue.freeze() } // Implicitly configure setup action to bind the client if bindDN and @@ -348,20 +152,22 @@ function Client(options) { this.on('setup', function (clt, cb) { clt.bind(options.bindDN, options.bindCredentials, function (err) { if (err) { - self.emit('error', err); + if (self._socket) { + self._socket.destroy() + } + self.emit('error', err) } - cb(err); - }); - }); + cb(err) + }) + }) } - this._socket = null; - this.connected = false; - this.connect(); + this._socket = null + this.connected = false + this.connect() } -util.inherits(Client, EventEmitter); -module.exports = Client; - +util.inherits(Client, EventEmitter) +module.exports = Client /** * Sends an abandon request to the LDAP server. @@ -374,24 +180,23 @@ module.exports = Client; * @param {Function} callback of the form f(err). * @throws {TypeError} on invalid input. */ -Client.prototype.abandon = function abandon(messageID, controls, callback) { - assert.number(messageID, 'messageID'); +Client.prototype.abandon = function abandon (messageID, controls, callback) { + assert.number(messageID, 'messageID') if (typeof (controls) === 'function') { - callback = controls; - controls = []; + callback = controls + controls = [] } else { - controls = validateControls(controls); + controls = validateControls(controls) } - assert.func(callback, 'callback'); + assert.func(callback, 'callback') var req = new AbandonRequest({ abandonID: messageID, controls: controls - }); - - return this._send(req, 'abandon', null, callback); -}; + }) + return this._send(req, 'abandon', null, callback) +} /** * Adds an entry to the LDAP server. @@ -406,48 +211,46 @@ Client.prototype.abandon = function abandon(messageID, controls, callback) { * @param {Function} callback of the form f(err, res). * @throws {TypeError} on invalid input. */ -Client.prototype.add = function add(name, entry, controls, callback) { - assert.ok(name !== undefined, 'name'); - assert.object(entry, 'entry'); +Client.prototype.add = function add (name, entry, controls, callback) { + assert.ok(name !== undefined, 'name') + assert.object(entry, 'entry') if (typeof (controls) === 'function') { - callback = controls; - controls = []; + callback = controls + controls = [] } else { - controls = validateControls(controls); + controls = validateControls(controls) } - assert.func(callback, 'callback'); + assert.func(callback, 'callback') if (Array.isArray(entry)) { entry.forEach(function (a) { - if (!Attribute.isAttribute(a)) - throw new TypeError('entry must be an Array of Attributes'); - }); + if (!Attribute.isAttribute(a)) { throw new TypeError('entry must be an Array of Attributes') } + }) } else { - var save = entry; + var save = entry - entry = []; + entry = [] Object.keys(save).forEach(function (k) { - var attr = new Attribute({type: k}); + var attr = new Attribute({ type: k }) if (Array.isArray(save[k])) { save[k].forEach(function (v) { - attr.addValue(v.toString()); - }); + attr.addValue(v.toString()) + }) } else { - attr.addValue(save[k].toString()); + attr.addValue(save[k].toString()) } - entry.push(attr); - }); + entry.push(attr) + }) } var req = new AddRequest({ entry: ensureDN(name, this.strictDN), attributes: entry, controls: controls - }); - - return this._send(req, [errors.LDAP_SUCCESS], null, callback); -}; + }) + return this._send(req, [errors.LDAP_SUCCESS], null, callback) +} /** * Performs a simple authentication against the server. @@ -458,32 +261,38 @@ Client.prototype.add = function add(name, entry, controls, callback) { * @param {Function} callback of the form f(err, res). * @throws {TypeError} on invalid input. */ -Client.prototype.bind = function bind(name, - credentials, - controls, - callback, - _bypass) { - if (typeof (name) !== 'string' && !(name instanceof dn.DN)) - throw new TypeError('name (string) required'); - assert.optionalString(credentials, 'credentials'); +Client.prototype.bind = function bind (name, + credentials, + controls, + callback, + _bypass) { + if (typeof (name) !== 'string' && !(name instanceof dn.DN)) { throw new TypeError('name (string) required') } + assert.optionalString(credentials, 'credentials') if (typeof (controls) === 'function') { - callback = controls; - controls = []; + callback = controls + controls = [] } else { - controls = validateControls(controls); + controls = validateControls(controls) } - assert.func(callback, 'callback'); + assert.func(callback, 'callback') var req = new BindRequest({ name: name || '', authentication: 'Simple', credentials: credentials || '', controls: controls - }); + }) - return this._send(req, [errors.LDAP_SUCCESS], null, callback, _bypass); -}; + // Connection errors will be reported to the bind callback too (useful when the LDAP server is not available) + var self = this + function callbackWrapper (err, ret) { + self.removeListener('connectError', callbackWrapper) + callback(err, ret) + } + this.addListener('connectError', callbackWrapper) + return this._send(req, [errors.LDAP_SUCCESS], null, callbackWrapper, _bypass) +} /** * Compares an attribute/value pair with an entry on the LDAP server. @@ -495,37 +304,35 @@ Client.prototype.bind = function bind(name, * @param {Function} callback of the form f(err, boolean, res). * @throws {TypeError} on invalid input. */ -Client.prototype.compare = function compare(name, - attr, - value, - controls, - callback) { - assert.ok(name !== undefined, 'name'); - assert.string(attr, 'attr'); - assert.string(value, 'value'); +Client.prototype.compare = function compare (name, + attr, + value, + controls, + callback) { + assert.ok(name !== undefined, 'name') + assert.string(attr, 'attr') + assert.string(value, 'value') if (typeof (controls) === 'function') { - callback = controls; - controls = []; + callback = controls + controls = [] } else { - controls = validateControls(controls); + controls = validateControls(controls) } - assert.func(callback, 'callback'); + assert.func(callback, 'callback') var req = new CompareRequest({ entry: ensureDN(name, this.strictDN), attribute: attr, value: value, controls: controls - }); + }) return this._send(req, CMP_EXPECT, null, function (err, res) { - if (err) - return callback(err); - - return callback(null, (res.status === errors.LDAP_COMPARE_TRUE), res); - }); -}; + if (err) { return callback(err) } + return callback(null, (res.status === errors.LDAP_COMPARE_TRUE), res) + }) +} /** * Deletes an entry from the LDAP server. @@ -535,24 +342,23 @@ Client.prototype.compare = function compare(name, * @param {Function} callback of the form f(err, res). * @throws {TypeError} on invalid input. */ -Client.prototype.del = function del(name, controls, callback) { - assert.ok(name !== undefined, 'name'); +Client.prototype.del = function del (name, controls, callback) { + assert.ok(name !== undefined, 'name') if (typeof (controls) === 'function') { - callback = controls; - controls = []; + callback = controls + controls = [] } else { - controls = validateControls(controls); + controls = validateControls(controls) } - assert.func(callback, 'callback'); + assert.func(callback, 'callback') var req = new DeleteRequest({ entry: ensureDN(name, this.strictDN), controls: controls - }); - - return this._send(req, [errors.LDAP_SUCCESS], null, callback); -}; + }) + return this._send(req, [errors.LDAP_SUCCESS], null, callback) +} /** * Performs an extended operation on the LDAP server. @@ -567,37 +373,33 @@ Client.prototype.del = function del(name, controls, callback) { * @param {Function} callback of the form f(err, value, res). * @throws {TypeError} on invalid input. */ -Client.prototype.exop = function exop(name, value, controls, callback) { - assert.string(name, 'name'); +Client.prototype.exop = function exop (name, value, controls, callback) { + assert.string(name, 'name') if (typeof (value) === 'function') { - callback = value; - controls = []; - value = ''; + callback = value + controls = [] + value = undefined } - if (!(Buffer.isBuffer(value) || typeof (value) === 'string')) - throw new TypeError('value (Buffer || string) required'); if (typeof (controls) === 'function') { - callback = controls; - controls = []; + callback = controls + controls = [] } else { - controls = validateControls(controls); + controls = validateControls(controls) } - assert.func(callback, 'callback'); + assert.func(callback, 'callback') var req = new ExtendedRequest({ requestName: name, requestValue: value, controls: controls - }); + }) return this._send(req, [errors.LDAP_SUCCESS], null, function (err, res) { - if (err) - return callback(err); - - return callback(null, res.responseValue || '', res); - }); -}; + if (err) { return callback(err) } + return callback(null, res.responseValue || '', res) + }) +} /** * Performs an LDAP modify against the server. @@ -608,70 +410,67 @@ Client.prototype.exop = function exop(name, value, controls, callback) { * @param {Function} callback of the form f(err, res). * @throws {TypeError} on invalid input. */ -Client.prototype.modify = function modify(name, change, controls, callback) { - assert.ok(name !== undefined, 'name'); - assert.object(change, 'change'); +Client.prototype.modify = function modify (name, change, controls, callback) { + assert.ok(name !== undefined, 'name') + assert.object(change, 'change') - var changes = []; + var changes = [] - function changeFromObject(change) { - if (!change.operation && !change.type) - throw new Error('change.operation required'); - if (typeof (change.modification) !== 'object') - throw new Error('change.modification (object) required'); + function changeFromObject (change) { + if (!change.operation && !change.type) { throw new Error('change.operation required') } + if (typeof (change.modification) !== 'object') { throw new Error('change.modification (object) required') } - if (Object.keys(change.modification).length == 2 && + if (Object.keys(change.modification).length === 2 && typeof (change.modification.type) === 'string' && Array.isArray(change.modification.vals)) { // Use modification directly if it's already normalized: changes.push(new Change({ operation: change.operation || change.type, modification: change.modification - })); + })) } else { // Normalize the modification object Object.keys(change.modification).forEach(function (k) { - var mod = {}; - mod[k] = change.modification[k]; + var mod = {} + mod[k] = change.modification[k] changes.push(new Change({ operation: change.operation || change.type, modification: mod - })); - }); + })) + }) } } if (Change.isChange(change)) { - changes.push(change); + changes.push(change) } else if (Array.isArray(change)) { change.forEach(function (c) { if (Change.isChange(c)) { - changes.push(c); + changes.push(c) } else { - changeFromObject(c); + changeFromObject(c) } - }); + }) } else { - changeFromObject(change); + changeFromObject(change) } if (typeof (controls) === 'function') { - callback = controls; - controls = []; + callback = controls + controls = [] } else { - controls = validateControls(controls); + controls = validateControls(controls) } - assert.func(callback, 'callback'); + assert.func(callback, 'callback') var req = new ModifyRequest({ object: ensureDN(name, this.strictDN), changes: changes, controls: controls - }); - - return this._send(req, [errors.LDAP_SUCCESS], null, callback); -}; + }) + return this._send(req, [errors.LDAP_SUCCESS], null, callback) +} /** * Performs an LDAP modifyDN against the server. @@ -687,40 +486,39 @@ Client.prototype.modify = function modify(name, change, controls, callback) { * @param {Function} callback of the form f(err, res). * @throws {TypeError} on invalid input. */ -Client.prototype.modifyDN = function modifyDN(name, - newName, - controls, - callback) { - assert.ok(name !== undefined, 'name'); - assert.string(newName, 'newName'); +Client.prototype.modifyDN = function modifyDN (name, + newName, + controls, + callback) { + assert.ok(name !== undefined, 'name') + assert.string(newName, 'newName') if (typeof (controls) === 'function') { - callback = controls; - controls = []; + callback = controls + controls = [] } else { - controls = validateControls(controls); + controls = validateControls(controls) } - assert.func(callback); + assert.func(callback) - var DN = ensureDN(name); + var DN = ensureDN(name) // TODO: is non-strict handling desired here? - var newDN = dn.parse(newName); + var newDN = dn.parse(newName) var req = new ModifyDNRequest({ entry: DN, deleteOldRdn: true, controls: controls - }); + }) if (newDN.length !== 1) { - req.newRdn = dn.parse(newDN.rdns.shift().toString()); - req.newSuperior = newDN; + req.newRdn = dn.parse(newDN.rdns.shift().toString()) + req.newSuperior = newDN } else { - req.newRdn = newDN; + req.newRdn = newDN } - return this._send(req, [errors.LDAP_SUCCESS], null, callback); -}; - + return this._send(req, [errors.LDAP_SUCCESS], null, callback) +} /** * Performs an LDAP search against the server. @@ -745,55 +543,55 @@ Client.prototype.modifyDN = function modifyDN(name, * @param {Function} callback of the form f(err, res). * @throws {TypeError} on invalid input. */ -Client.prototype.search = function search(base, - options, - controls, - callback, - _bypass) { - assert.ok(base !== undefined, 'search base'); +Client.prototype.search = function search (base, + options, + controls, + callback, + _bypass) { + assert.ok(base !== undefined, 'search base') if (Array.isArray(options) || (options instanceof Control)) { - controls = options; - options = {}; + controls = options + options = {} } else if (typeof (options) === 'function') { - callback = options; - controls = []; + callback = options + controls = [] options = { - filter: new PresenceFilter({attribute: 'objectclass'}) - }; + filter: new PresenceFilter({ attribute: 'objectclass' }) + } } else if (typeof (options) === 'string') { - options = {filter: filters.parseString(options)}; + options = { filter: filters.parseString(options) } } else if (typeof (options) !== 'object') { - throw new TypeError('options (object) required'); + throw new TypeError('options (object) required') } if (typeof (options.filter) === 'string') { - options.filter = filters.parseString(options.filter); + options.filter = filters.parseString(options.filter) } else if (!options.filter) { - options.filter = new PresenceFilter({attribute: 'objectclass'}); + options.filter = new PresenceFilter({ attribute: 'objectclass' }) } else if (!filters.isFilter(options.filter)) { - throw new TypeError('options.filter (Filter) required'); + throw new TypeError('options.filter (Filter) required') } if (typeof (controls) === 'function') { - callback = controls; - controls = []; + callback = controls + controls = [] } else { - controls = validateControls(controls); + controls = validateControls(controls) } - assert.func(callback, 'callback'); + assert.func(callback, 'callback') if (options.attributes) { if (!Array.isArray(options.attributes)) { if (typeof (options.attributes) === 'string') { - options.attributes = [options.attributes]; + options.attributes = [options.attributes] } else { - throw new TypeError('options.attributes must be an Array of Strings'); + throw new TypeError('options.attributes must be an Array of Strings') } } } - var self = this; - var baseDN = ensureDN(base, this.strictDN); + var self = this + var baseDN = ensureDN(base, this.strictDN) - function sendRequest(ctrls, emitter, cb) { + function sendRequest (ctrls, emitter, cb) { var req = new SearchRequest({ baseObject: baseDN, scope: options.scope || 'base', @@ -804,26 +602,26 @@ Client.prototype.search = function search(base, typesOnly: options.typesOnly || false, attributes: options.attributes || [], controls: ctrls - }); + }) return self._send(req, - [errors.LDAP_SUCCESS], - emitter, - cb, - _bypass); + [errors.LDAP_SUCCESS], + emitter, + cb, + _bypass) } if (options.paged) { // Perform automated search paging - var pageOpts = typeof (options.paged) === 'object' ? options.paged : {}; - var size = 100; // Default page size + var pageOpts = typeof (options.paged) === 'object' ? options.paged : {} + var size = 100 // Default page size if (pageOpts.pageSize > 0) { - size = pageOpts.pageSize; - } else if (options.sizeLimit > 1) { + size = pageOpts.pageSize + } else if (options.sizeLimit > 1) { // According to the RFC, servers should ignore the paging control if // pageSize >= sizelimit. Some might still send results, but it's safer // to stay under that figure when assigning a default value. - size = options.sizeLimit - 1; + size = options.sizeLimit - 1 } var pager = new SearchPager({ @@ -831,14 +629,13 @@ Client.prototype.search = function search(base, controls: controls, pageSize: size, pagePause: pageOpts.pagePause - }); - pager.on('search', sendRequest); - pager.begin(); + }) + pager.on('search', sendRequest) + pager.begin() } else { - sendRequest(controls, new EventEmitter(), callback); + sendRequest(controls, new CorkedEmitter(), callback) } -}; - +} /** * Unbinds this client from the LDAP server. @@ -849,45 +646,41 @@ Client.prototype.search = function search(base, * @param {Function} callback of the form f(err). * @throws {TypeError} if you pass in callback as not a function. */ -Client.prototype.unbind = function unbind(callback) { - if (!callback) - callback = function () {}; +Client.prototype.unbind = function unbind (callback) { + if (!callback) { callback = function () {} } - if (typeof (callback) !== 'function') - throw new TypeError('callback must be a function'); + if (typeof (callback) !== 'function') { throw new TypeError('callback must be a function') } // When the socket closes, it is useful to know whether it was due to a // user-initiated unbind or something else. - this.unbound = true; + this.unbound = true - if (!this._socket) - return callback(); - - var req = new UnbindRequest(); - return this._send(req, 'unbind', null, callback); -}; + if (!this._socket) { return callback() } + var req = new UnbindRequest() + return this._send(req, 'unbind', null, callback) +} /** * Attempt to secure connection with StartTLS. */ -Client.prototype.starttls = function starttls(options, - controls, - callback, - _bypass) { - assert.optionalObject(options); - options = options || {}; - callback = once(callback); - var self = this; +Client.prototype.starttls = function starttls (options, + controls, + callback, + _bypass) { + assert.optionalObject(options) + options = options || {} + callback = once(callback) + var self = this if (this._starttls) { - return callback(new Error('STARTTLS already in progress or active')); + return callback(new Error('STARTTLS already in progress or active')) } - function onSend(err, emitter) { + function onSend (err, emitter) { if (err) { - callback(err); - return; + callback(err) + return } /* * Now that the request has been sent, block all outgoing messages @@ -896,69 +689,66 @@ Client.prototype.starttls = function starttls(options, // TODO: block traffic self._starttls = { started: true - }; + } emitter.on('error', function (err) { - self._starttls = null; - callback(err); - }); + self._starttls = null + callback(err) + }) emitter.on('end', function (res) { - var sock = self._socket; + var sock = self._socket /* * Unplumb socket data during SSL negotiation. * This will prevent the LDAP parser from stumbling over the TLS * handshake and raising a ruckus. */ - sock.removeAllListeners('data'); + sock.removeAllListeners('data') - options.socket = sock; - var secure = tls.connect(options); + options.socket = sock + var secure = tls.connect(options) secure.once('secureConnect', function () { /* * Wire up 'data' and 'error' handlers like the normal socket. * Handling 'end' events isn't necessary since the underlying socket * will handle those. */ - secure.removeAllListeners('error'); - secure.on('data', function onData(data) { - if (self.log.trace()) - self.log.trace('data event: %s', util.inspect(data)); + secure.removeAllListeners('error') + secure.on('data', function onData (data) { + if (self.log.trace()) { self.log.trace('data event: %s', util.inspect(data)) } - self._tracker.parser.write(data); - }); - secure.on('error', function (err) { - if (self.log.trace()) - self.log.trace({err: err}, 'error event: %s', new Error().stack); + self._tracker.parser.write(data) + }) + secure.on('error', function (err) { + if (self.log.trace()) { self.log.trace({ err: err }, 'error event: %s', new Error().stack) } - self.emit('error', err); - sock.destroy(); - }); - callback(null); - }); + self.emit('error', err) + sock.destroy() + }) + callback(null) + }) secure.once('error', function (err) { // If the SSL negotiation failed, to back to plain mode. - self._starttls = null; - secure.removeAllListeners(); - callback(err); - }); - self._starttls.success = true; - self._socket = secure; - }); + self._starttls = null + secure.removeAllListeners() + callback(err) + }) + self._starttls.success = true + self._socket = secure + }) } var req = new ExtendedRequest({ requestName: '1.3.6.1.4.1.1466.20037', requestValue: null, controls: controls - }); + }) return this._send(req, - [errors.LDAP_SUCCESS], - new EventEmitter(), - onSend, - _bypass); -}; - + [errors.LDAP_SUCCESS], + new EventEmitter(), + onSend, + _bypass) +} /** * Disconnect from the LDAP server and do not allow reconnection. @@ -971,149 +761,153 @@ Client.prototype.starttls = function starttls(options, * * @param {Object} err (Optional) error that was cause of client destruction */ -Client.prototype.destroy = function destroy(err) { - this.destroyed = true; - this.queue.freeze(); +Client.prototype.destroy = function destroy (err) { + this.destroyed = true + this.queue.freeze() // Purge any queued requests which are now meaningless this.queue.flush(function (msg, expect, emitter, cb) { if (typeof (cb) === 'function') { - cb(new Error('client destroyed')); - } - }); + cb(new Error('client destroyed')) + } + }) if (this.connected) { - this.unbind(); + this.unbind() } else if (this._socket) { - this._socket.destroy(); + this._socket.destroy() } - this.emit('destroy', err); -}; - + this.emit('destroy', err) +} /** * Initiate LDAP connection. */ -Client.prototype.connect = function connect() { +Client.prototype.connect = function connect () { if (this.connecting || this.connected) { - return; + return } - var self = this; - var log = this.log; - var socket; - var tracker; + var self = this + var log = this.log + var socket + var tracker // Establish basic socket connection - function connectSocket(cb) { - cb = once(cb); + function connectSocket (cb) { + cb = once(cb) - function onResult(err, res) { + function onResult (err, res) { if (err) { if (self.connectTimer) { - clearTimeout(self.connectTimer); - self.connectTimer = null; + clearTimeout(self.connectTimer) + self.connectTimer = null } - self.emit('connectError', err); + self.emit('connectError', err) } - cb(err, res); + cb(err, res) } - function onConnect() { + function onConnect () { if (self.connectTimer) { - clearTimeout(self.connectTimer); - self.connectTimer = null; + clearTimeout(self.connectTimer) + self.connectTimer = null } socket.removeAllListeners('error') .removeAllListeners('connect') - .removeAllListeners('secureConnect'); + .removeAllListeners('secureConnect') - tracker.id = nextClientId() + '__' + tracker.id; - self.log = self.log.child({ldap_id: tracker.id}, true); + tracker.id = nextClientId() + '__' + tracker.id + self.log = self.log.child({ ldap_id: tracker.id }, true) // Move on to client setup - setupClient(cb); + setupClient(cb) } - var port = (self.port || self.socketPath); + var port = (self.port || self.socketPath) if (self.secure) { - socket = tls.connect(port, self.host, self.tlsOptions); - socket.once('secureConnect', onConnect); + socket = tls.connect(port, self.host, self.tlsOptions) + socket.once('secureConnect', onConnect) } else { - socket = net.connect(port, self.host); - socket.once('connect', onConnect); + socket = net.connect(port, self.host) + socket.once('connect', onConnect) } - socket.once('error', onResult); - initSocket(); + socket.once('error', onResult) + initSocket() // Setup connection timeout handling, if desired if (self.connectTimeout) { - self.connectTimer = setTimeout(function onConnectTimeout() { + self.connectTimer = setTimeout(function onConnectTimeout () { if (!socket || !socket.readable || !socket.writeable) { - socket.destroy(); - self._socket = null; - onResult(new ConnectionError('connection timeout')); + socket.destroy() + self._socket = null + onResult(new ConnectionError('connection timeout')) } - }, self.connectTimeout); + }, self.connectTimeout) } } // Initialize socket events and LDAP parser. - function initSocket() { - tracker = new MessageTracker({ + function initSocket () { + tracker = messageTrackerFactory({ id: self.url ? self.url.href : self.socketPath, - parser: new Parser({log: log}) - }); + parser: new Parser({ log: log }) + }) // This won't be set on TLS. So. Very. Annoying. if (typeof (socket.setKeepAlive) !== 'function') { - socket.setKeepAlive = function setKeepAlive(enable, delay) { - return socket.socket ? - socket.socket.setKeepAlive(enable, delay) : false; - }; + socket.setKeepAlive = function setKeepAlive (enable, delay) { + return socket.socket + ? socket.socket.setKeepAlive(enable, delay) : false + } } - socket.on('data', function onData(data) { - if (log.trace()) - log.trace('data event: %s', util.inspect(data)); + socket.on('data', function onData (data) { + if (log.trace) { log.trace('data event: %s', util.inspect(data)) } - tracker.parser.write(data); - }); + tracker.parser.write(data) + }) // The "router" - tracker.parser.on('message', function onMessage(message) { - message.connection = self._socket; - var callback = tracker.fetch(message.messageID); + tracker.parser.on('message', function onMessage (message) { + message.connection = self._socket + var callback = tracker.fetch(message.messageID) if (!callback) { - log.error({message: message.json}, 'unsolicited message'); - return false; + log.error({ message: message.json }, 'unsolicited message') + return false } - return callback(message); - }); + return callback(message) + }) - tracker.parser.on('error', function onParseError(err) { + tracker.parser.on('error', function onParseError (err) { self.emit('error', new VError(err, 'Parser error for %s', - tracker.id)); - self.connected = false; - socket.end(); - }); + tracker.id)) + self.connected = false + socket.end() + }) } // After connect, register socket event handlers and run any setup actions - function setupClient(cb) { - cb = once(cb); + function setupClient (cb) { + cb = once(cb) // Indicate failure if anything goes awry during setup - function bail(err) { - socket.destroy(); - cb(err || new Error('client error during setup')); + function bail (err) { + socket.destroy() + cb(err || new Error('client error during setup')) } // Work around lack of close event on tls.socket in node < 0.11 - ((socket.socket) ? socket.socket : socket).once('close', bail); - socket.once('error', bail); - socket.once('end', bail); - socket.once('timeout', bail); + ((socket.socket) ? socket.socket : socket).once('close', bail) + socket.once('error', bail) + socket.once('end', bail) + socket.once('timeout', bail) + socket.once('cleanupSetupListeners', function onCleanup () { + socket.removeListener('error', bail) + .removeListener('close', bail) + .removeListener('end', bail) + .removeListener('timeout', bail) + }) - self._socket = socket; - self._tracker = tracker; + self._socket = socket + self._tracker = tracker // Run any requested setup (such as automatically performing a bind) on // socket before signalling successful connection. @@ -1121,180 +915,171 @@ Client.prototype.connect = function connect() { // blocked until the connection is considered fully established post-setup. // Only allow bind/search/starttls for now. var basicClient = { - bind: function bindBypass(name, credentials, controls, callback) { - return self.bind(name, credentials, controls, callback, true); + bind: function bindBypass (name, credentials, controls, callback) { + return self.bind(name, credentials, controls, callback, true) }, - search: function searchBypass(base, options, controls, callback) { - return self.search(base, options, controls, callback, true); + search: function searchBypass (base, options, controls, callback) { + return self.search(base, options, controls, callback, true) }, - starttls: function starttlsBypass(options, controls, callback) { - return self.starttls(options, controls, callback, true); + starttls: function starttlsBypass (options, controls, callback) { + return self.starttls(options, controls, callback, true) }, unbind: self.unbind.bind(self) - }; + } vasync.forEachPipeline({ func: function (f, callback) { - f(basicClient, callback); + f(basicClient, callback) }, inputs: self.listeners('setup') }, function (err, res) { if (err) { - self.emit('setupError', err); + self.emit('setupError', err) } - cb(err); - }); + cb(err) + }) } // Wire up "official" event handlers after successful connect/setup - function postSetup() { - socket.removeAllListeners('error') - .removeAllListeners('close') - .removeAllListeners('end') - .removeAllListeners('timeout'); + function postSetup () { + // cleanup the listeners we attached in setup phrase. + socket.emit('cleanupSetupListeners'); // Work around lack of close event on tls.socket in node < 0.11 ((socket.socket) ? socket.socket : socket).once('close', - self._onClose.bind(self)); - socket.on('end', function onEnd() { - if (log.trace()) - log.trace('end event'); + self._onClose.bind(self)) + socket.on('end', function onEnd () { + if (log.trace()) { log.trace('end event') } - self.emit('end'); - socket.end(); - }); - socket.on('error', function onSocketError(err) { - if (log.trace()) - log.trace({err: err}, 'error event: %s', new Error().stack); + self.emit('end') + socket.end() + }) + socket.on('error', function onSocketError (err) { + if (log.trace()) { log.trace({ err: err }, 'error event: %s', new Error().stack) } - self.emit('error', err); - socket.destroy(); - }); - socket.on('timeout', function onTimeout() { - if (log.trace()) - log.trace('timeout event'); + self.emit('error', err) + socket.destroy() + }) + socket.on('timeout', function onTimeout () { + if (log.trace()) { log.trace('timeout event') } - self.emit('socketTimeout'); - socket.end(); - }); + self.emit('socketTimeout') + socket.end() + }) } - var retry; - var failAfter; + var retry + var failAfter if (this.reconnect) { retry = backoff.exponential({ initialDelay: this.reconnect.initialDelay, maxDelay: this.reconnect.maxDelay - }); - failAfter = this.reconnect.failAfter; + }) + failAfter = this.reconnect.failAfter } else { retry = backoff.exponential({ initialDelay: 1, maxDelay: 2 - }); - failAfter = 1; + }) + failAfter = 1 } - retry.failAfter(failAfter); + retry.failAfter(failAfter) retry.on('ready', function (num, delay) { if (self.destroyed) { // Cease connection attempts if destroyed - return; + return } connectSocket(function (err) { if (!err) { - postSetup(); - self.connecting = false; - self.connected = true; - self.emit('connect', socket); - self.log.debug('connected after %d attempt(s)', num+1); + postSetup() + self.connecting = false + self.connected = true + self.emit('connect', socket) + self.log.debug('connected after %d attempt(s)', num + 1) // Flush any queued requests - self._flushQueue(); - self._connectRetry = null; + self._flushQueue() + self._connectRetry = null } else { - retry.backoff(err); + retry.backoff(err) } - }); - }); + }) + }) retry.on('fail', function (err) { if (self.destroyed) { // Silence any connect/setup errors if destroyed - return; + return } - self.log.debug('failed to connect after %d attempts', failAfter); + self.log.debug('failed to connect after %d attempts', failAfter) // Communicate the last-encountered error if (err instanceof ConnectionError) { - self.emit('connectTimeout', err); + self.emit('connectTimeout', err) + } else if (err.code === 'ECONNREFUSED') { + self.emit('connectRefused', err) } else { - self.emit('error', err); + self.emit('error', err) } - }); + }) - this._connectRetry = retry; - this.connecting = true; - retry.backoff(); -}; + this._connectRetry = retry + this.connecting = true + retry.backoff() +} - - -///--- Private API +/// --- Private API /** * Flush queued requests out to the socket. */ -Client.prototype._flushQueue = function _flushQueue() { +Client.prototype._flushQueue = function _flushQueue () { // Pull items we're about to process out of the queue. - this.queue.flush(this._send.bind(this)); -}; + this.queue.flush(this._send.bind(this)) +} /** * Clean up socket/parser resources after socket close. */ -Client.prototype._onClose = function _onClose(had_err) { - var socket = this._socket; - var tracker = this._tracker; +Client.prototype._onClose = function _onClose (closeError) { + var socket = this._socket + var tracker = this._tracker socket.removeAllListeners('connect') .removeAllListeners('data') .removeAllListeners('drain') .removeAllListeners('end') .removeAllListeners('error') - .removeAllListeners('timeout'); - this._socket = null; + .removeAllListeners('timeout') + this._socket = null this.connected = false; - ((socket.socket) ? socket.socket : socket).removeAllListeners('close'); + ((socket.socket) ? socket.socket : socket).removeAllListeners('close') - if (this.log.trace()) - this.log.trace('close event had_err=%s', had_err ? 'yes' : 'no'); + if (this.log.trace()) { this.log.trace('close event had_err=%s', closeError ? 'yes' : 'no') } - this.emit('close', had_err); + this.emit('close', closeError) // On close we have to walk the outstanding messages and go invoke their // callback with an error. - tracker.pending.forEach(function (msgid) { - var cb = tracker.fetch(msgid); - tracker.remove(msgid); - - if (socket.unbindMessageID !== parseInt(msgid, 10)) { - return cb(new ConnectionError(tracker.id + ' closed')); + tracker.purge(function (msgid, cb) { + if (socket.unbindMessageID !== msgid) { + return cb(new ConnectionError(tracker.id + ' closed')) } else { // Unbinds will be communicated as a success since we're closed - var unbind = new UnbindResponse({messageID: msgid}); - unbind.status = 'unbind'; - return cb(unbind); + var unbind = new UnbindResponse({ messageID: msgid }) + unbind.status = 'unbind' + return cb(unbind) } - }); + }) // Trash any parser or starttls state - this._tracker = null; - delete this._starttls; + this._tracker = null + delete this._starttls // Automatically fire reconnect logic if the socket was closed for any reason // other than a user-initiated unbind. if (this.reconnect && !this.unbound) { - this.connect(); + this.connect() } - this.unbound = false; - return false; -}; + this.unbound = false + return false +} /** * Maintain idle timer for client. @@ -1304,181 +1089,174 @@ Client.prototype._onClose = function _onClose(had_err) { * * @param {Boolean} override explicitly disable timer. */ -Client.prototype._updateIdle = function _updateIdle(override) { +Client.prototype._updateIdle = function _updateIdle (override) { if (this.idleTimeout === 0) { - return; + return } // Client must be connected but not waiting on any request data - var self = this; - function isIdle(disable) { + var self = this + function isIdle (disable) { return ((disable !== true) && (self._socket && self.connected) && - (self._tracker.pending.length === 0)); + (self._tracker.pending === 0)) } if (isIdle(override)) { if (!this._idleTimer) { this._idleTimer = setTimeout(function () { // Double-check idleness in case socket was torn down if (isIdle()) { - self.emit('idle'); + self.emit('idle') } - }, this.idleTimeout); + }, this.idleTimeout) } } else { if (this._idleTimer) { - clearTimeout(this._idleTimer); - this._idleTimer = null; + clearTimeout(this._idleTimer) + this._idleTimer = null } } -}; +} /** * Attempt to send an LDAP request. */ -Client.prototype._send = function _send(message, - expect, - emitter, - callback, - _bypass) { - assert.ok(message); - assert.ok(expect); - assert.optionalObject(emitter); - assert.ok(callback); +Client.prototype._send = function _send (message, + expect, + emitter, + callback, + _bypass) { + assert.ok(message) + assert.ok(expect) + assert.optionalObject(emitter) + assert.ok(callback) // Allow connect setup traffic to bypass checks if (_bypass && this._socket && this._socket.writable) { - return this._sendSocket(message, expect, emitter, callback); + return this._sendSocket(message, expect, emitter, callback) } if (!this._socket || !this.connected) { if (!this.queue.enqueue(message, expect, emitter, callback)) { - callback(new ConnectionError('connection unavailable')); + callback(new ConnectionError('connection unavailable')) } // Initiate reconnect if needed if (this.reconnect) { - this.connect(); + this.connect() } - return false; + return false } else { - this._flushQueue(); - return this._sendSocket(message, expect, emitter, callback); + this._flushQueue() + return this._sendSocket(message, expect, emitter, callback) } -}; +} -Client.prototype._sendSocket = function _sendSocket(message, - expect, - emitter, - callback) { - var conn = this._socket; - var tracker = this._tracker; - var log = this.log; - var self = this; - var timer = false; - var sentEmitter = false; +Client.prototype._sendSocket = function _sendSocket (message, + expect, + emitter, + callback) { + var conn = this._socket + var tracker = this._tracker + var log = this.log + var self = this + var timer = false + var sentEmitter = false - function sendResult(event, obj) { - if (event === 'error' && self.listeners('resultError')) { - self.emit('resultError', obj); + function sendResult (event, obj) { + if (event === 'error') { + self.emit('resultError', obj) } if (emitter) { if (event === 'error') { // Error will go unhandled if emitter hasn't been sent via callback. // Execute callback with the error instead. - if (!sentEmitter) - return callback(obj); + if (!sentEmitter) { return callback(obj) } } - return emitter.emit(event, obj); + return emitter.emit(event, obj) } - if (event === 'error') - return callback(obj); + if (event === 'error') { return callback(obj) } - return callback(null, obj); + return callback(null, obj) } - function messageCallback(msg) { - if (timer) - clearTimeout(timer); + function messageCallback (msg) { + if (timer) { clearTimeout(timer) } - if (log.trace()) - log.trace({msg: msg ? msg.json : null}, 'response received'); + if (log.trace()) { log.trace({ msg: msg ? msg.json : null }, 'response received') } - if (expect === 'abandon') - return sendResult('end', null); + if (expect === 'abandon') { return sendResult('end', null) } if (msg instanceof SearchEntry || msg instanceof SearchReference) { - var event = msg.constructor.name; - event = event[0].toLowerCase() + event.slice(1); - return sendResult(event, msg); + var event = msg.constructor.name + event = event[0].toLowerCase() + event.slice(1) + return sendResult(event, msg) } else { - tracker.remove(message.messageID); + tracker.remove(message.messageID) // Potentially mark client as idle - self._updateIdle(); + self._updateIdle() if (msg instanceof LDAPResult) { if (expect.indexOf(msg.status) === -1) { - return sendResult('error', errors.getError(msg)); + return sendResult('error', errors.getError(msg)) } - return sendResult('end', msg); + return sendResult('end', msg) } else if (msg instanceof Error) { - return sendResult('error', msg); + return sendResult('error', msg) } else { - return sendResult('error', new errors.ProtocolError(msg.type)); + return sendResult('error', new errors.ProtocolError(msg.type)) } } } - function onRequestTimeout() { - self.emit('timeout', message); - var cb = tracker.fetch(message.messageID); + function onRequestTimeout () { + self.emit('timeout', message) + var cb = tracker.fetch(message.messageID) if (cb) { - //FIXME: the timed-out request should be abandoned - cb(new errors.TimeoutError('request timeout (client interrupt)')); + // FIXME: the timed-out request should be abandoned + cb(new errors.TimeoutError('request timeout (client interrupt)')) } } - function writeCallback() { + function writeCallback () { if (expect === 'abandon') { // Mark the messageID specified as abandoned - tracker.abandon(message.abandonID); + tracker.abandon(message.abandonID) // No need to track the abandon request itself - tracker.remove(message.id); - return callback(null); + tracker.remove(message.id) + return callback(null) } else if (expect === 'unbind') { - conn.unbindMessageID = message.id; + conn.unbindMessageID = message.id // Mark client as disconnected once unbind clears the socket - self.connected = false; + self.connected = false // Some servers will RST the connection after receiving an unbind. // Socket errors are blackholed since the connection is being closed. - conn.removeAllListeners('error'); - conn.on('error', function () {}); - conn.end(); + conn.removeAllListeners('error') + conn.on('error', function () {}) + conn.end() } else if (emitter) { - sentEmitter = true; - return callback(null, emitter); + sentEmitter = true + return callback(null, emitter) } - return false; + return false } // Start actually doing something... - tracker.track(message, messageCallback); + tracker.track(message, messageCallback) // Mark client as active - this._updateIdle(true); + this._updateIdle(true) if (self.timeout) { - log.trace('Setting timeout to %d', self.timeout); - timer = setTimeout(onRequestTimeout, self.timeout); + log.trace('Setting timeout to %d', self.timeout) + timer = setTimeout(onRequestTimeout, self.timeout) } - if (log.trace()) - log.trace('sending request %j', message.json); + if (log.trace()) { log.trace('sending request %j', message.json) } try { - return conn.write(message.toBer(), writeCallback); + return conn.write(message.toBer(), writeCallback) } catch (e) { - if (timer) - clearTimeout(timer); + if (timer) { clearTimeout(timer) } - log.trace({err: e}, 'Error writing message to socket'); - return callback(e); + log.trace({ err: e }, 'Error writing message to socket') + return callback(e) } -}; +} diff --git a/lib/client/constants.js b/lib/client/constants.js new file mode 100644 index 0000000..4a8ac9d --- /dev/null +++ b/lib/client/constants.js @@ -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 +} diff --git a/lib/client/index.js b/lib/client/index.js index 2d6f7c0..0c23052 100644 --- a/lib/client/index.js +++ b/lib/client/index.js @@ -1,56 +1,23 @@ -// Copyright 2012 Mark Cavage, Inc. All rights reserved. +'use strict' -var assert = require('assert'); - -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 +const logger = require('../logger') +const Client = require('./client') module.exports = { Client: Client, - createClient: function createClient(options) { - if (typeof (options) !== 'object') - throw new TypeError('options (object) required'); - if (options.url && typeof (options.url) !== 'string') - throw new TypeError('options.url (string) required'); - if (options.socketPath && typeof (options.socketPath) !== 'string') - throw new TypeError('options.socketPath must be a string'); - if (!xor(options.url, options.socketPath)) - throw new TypeError('options.url ^ options.socketPath (String) required'); - if (!options.log) - options.log = DEF_LOG; - if (typeof (options.log) !== 'object') - throw new TypeError('options.log must be an object'); + createClient: function createClient (options) { + if (isObject(options) === false) throw TypeError('options (object) required') + if (options.url && typeof options.url !== 'string') throw TypeError('options.url (string) required') + if (options.socketPath && typeof options.socketPath !== 'string') throw TypeError('options.socketPath must be a string') + if ((options.url && options.socketPath) || !(options.url || options.socketPath)) throw TypeError('options.url ^ options.socketPath (String) required') + if (!options.log) options.log = logger + if (isObject(options.log) !== true) throw TypeError('options.log must be an object') + if (!options.log.child) options.log.child = function () { return options.log } - return new Client(options); + return new Client(options) } -}; +} + +function isObject (input) { + return Object.prototype.toString.apply(input) === '[object Object]' +} diff --git a/lib/client/message-tracker/ge-window.js b/lib/client/message-tracker/ge-window.js new file mode 100644 index 0000000..58bde78 --- /dev/null +++ b/lib/client/message-tracker/ge-window.js @@ -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)) + } +} diff --git a/lib/client/message-tracker/id-generator.js b/lib/client/message-tracker/id-generator.js new file mode 100644 index 0000000..49423ea --- /dev/null +++ b/lib/client/message-tracker/id-generator.js @@ -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 + } +} diff --git a/lib/client/message-tracker/index.js b/lib/client/message-tracker/index.js new file mode 100644 index 0000000..c2f4d74 --- /dev/null +++ b/lib/client/message-tracker/index.js @@ -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 +} diff --git a/lib/client/message-tracker/purge-abandoned.js b/lib/client/message-tracker/purge-abandoned.js new file mode 100644 index 0000000..8fc6cab --- /dev/null +++ b/lib/client/message-tracker/purge-abandoned.js @@ -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: , cb: }` 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) + }) +} diff --git a/lib/client/request-queue/enqueue.js b/lib/client/request-queue/enqueue.js new file mode 100644 index 0000000..a1fbe95 --- /dev/null +++ b/lib/client/request-queue/enqueue.js @@ -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() + } +} diff --git a/lib/client/request-queue/flush.js b/lib/client/request-queue/flush.js new file mode 100644 index 0000000..c0f581c --- /dev/null +++ b/lib/client/request-queue/flush.js @@ -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) + } +} diff --git a/lib/client/request-queue/index.js b/lib/client/request-queue/index.js new file mode 100644 index 0000000..6fb1325 --- /dev/null +++ b/lib/client/request-queue/index.js @@ -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 +} diff --git a/lib/client/request-queue/purge.js b/lib/client/request-queue/purge.js new file mode 100644 index 0000000..dd0dc46 --- /dev/null +++ b/lib/client/request-queue/purge.js @@ -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')) + }) +} diff --git a/lib/client/search_pager.js b/lib/client/search_pager.js index 2e1088f..ef249dc 100644 --- a/lib/client/search_pager.js +++ b/lib/client/search_pager.js @@ -1,18 +1,16 @@ -// Copyright 2014 Joyent, Inc. All rights reserved. +'use strict' -var EventEmitter = require('events').EventEmitter; -var util = require('util'); +var EventEmitter = require('events').EventEmitter +var util = require('util') -var assert = require('assert-plus'); +var assert = require('assert-plus') -var dn = require('../dn'); -var messages = require('../messages/index'); -var Protocol = require('../protocol'); -var PagedControl = require('../controls/paged_results_control.js'); - - -///--- API +// var dn = require('../dn') +// var messages = require('../messages/index') +// var Protocol = require('../protocol') +var PagedControl = require('../controls/paged_results_control.js') +/// --- API /** * Handler object for paged search operations. @@ -33,140 +31,140 @@ var PagedControl = require('../controls/paged_results_control.js'); * able to emit 'end'. * 3. search - Emitted as an internal event to trigger another client search. */ -function SearchPager(opts) { - assert.object(opts); - assert.func(opts.callback); - assert.number(opts.pageSize); +function SearchPager (opts) { + assert.object(opts) + assert.func(opts.callback) + assert.number(opts.pageSize) - EventEmitter.call(this, {}); + EventEmitter.call(this, {}) - this.callback = opts.callback; - this.controls = opts.controls; - this.pageSize = opts.pageSize; - this.pagePause = opts.pagePause; + this.callback = opts.callback + this.controls = opts.controls + this.pageSize = opts.pageSize + this.pagePause = opts.pagePause this.controls.forEach(function (control) { if (control.type === PagedControl.OID) { // The point of using SearchPager is not having to do this. // Toss an error if the pagedResultsControl is present - throw new Error('redundant pagedResultControl'); + throw new Error('redundant pagedResultControl') } - }); + }) - this.finished = false; - this.started = false; + this.finished = false + this.started = false - var emitter = new EventEmitter(); - emitter.on('searchEntry', this.emit.bind(this, 'searchEntry')); - emitter.on('end', this._onEnd.bind(this)); - emitter.on('error', this._onError.bind(this)); - this.childEmitter = emitter; + var emitter = new EventEmitter() + emitter.on('searchEntry', this.emit.bind(this, 'searchEntry')) + emitter.on('end', this._onEnd.bind(this)) + emitter.on('error', this._onError.bind(this)) + this.childEmitter = emitter } -util.inherits(SearchPager, EventEmitter); -module.exports = SearchPager; +util.inherits(SearchPager, EventEmitter) +module.exports = SearchPager /** * Start the paged search. */ -SearchPager.prototype.begin = function begin() { +SearchPager.prototype.begin = function begin () { // Starting first page - this._nextPage(null); -}; + this._nextPage(null) +} -SearchPager.prototype._onEnd = function _onEnd(res) { - var self = this; - var cookie = null; +SearchPager.prototype._onEnd = function _onEnd (res) { + var self = this + var cookie = null res.controls.forEach(function (control) { if (control.type === PagedControl.OID) { - cookie = control.value.cookie; + cookie = control.value.cookie } - }); + }) // Pass a noop callback by default for page events - var nullCb = function () { }; + var nullCb = function () { } if (cookie === null) { // paged search not supported - this.finished = true; - this.emit('page', res, nullCb); - var err = new Error('missing paged control'); - err.name = 'PagedError'; + this.finished = true + this.emit('page', res, nullCb) + var err = new Error('missing paged control') + err.name = 'PagedError' if (this.listeners('pageError').length > 0) { - this.emit('pageError', err); + this.emit('pageError', err) // If the consumer as subscribed to pageError, SearchPager is absolved // from deliverying the fault via the 'error' event. Emitting an 'end' // event after 'error' breaks the contract that the standard client // provides, so it's only a possibility if 'pageError' is used instead. - this.emit('end', res); + this.emit('end', res) } else { - this.emit('error', err); + this.emit('error', err) // No end event possible per explaination above. } - return; + return } if (cookie.length === 0) { // end of paged results - this.finished = true; - this.emit('page', nullCb); - this.emit('end', res); + this.finished = true + this.emit('page', nullCb) + this.emit('end', res) } else { if (this.pagePause) { // Wait to fetch next page until callback is invoked // Halt page fetching if called with error this.emit('page', res, function (err) { if (!err) { - self._nextPage(cookie); + self._nextPage(cookie) } else { // the paged search has been canceled so emit an end - self.emit('end', res); + self.emit('end', res) } - }); + }) } else { - this.emit('page', res, nullCb); - this._nextPage(cookie); + this.emit('page', res, nullCb) + this._nextPage(cookie) } } -}; +} -SearchPager.prototype._onError = function _onError(err) { - this.finished = true; - this.emit('error', err); -}; +SearchPager.prototype._onError = function _onError (err) { + this.finished = true + this.emit('error', err) +} /** * Initiate a search for the next page using the returned cookie value. */ -SearchPager.prototype._nextPage = function _nextPage(cookie) { - var controls = this.controls.slice(0); +SearchPager.prototype._nextPage = function _nextPage (cookie) { + var controls = this.controls.slice(0) controls.push(new PagedControl({ value: { size: this.pageSize, cookie: cookie } - })); + })) this.emit('search', controls, this.childEmitter, - this._sendCallback.bind(this)); -}; + this._sendCallback.bind(this)) +} /** * 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) { - this.finished = true; + this.finished = true if (!this.started) { // EmitSend error during the first page, bail via callback - this.callback(err, null); + this.callback(err, null) } else { - this.emit('error', err); + this.emit('error', err) } } else { // search successfully send if (!this.started) { - this.started = true; + this.started = true // send self as emitter as the client would - this.callback(null, this); + this.callback(null, this) } } -}; +} diff --git a/lib/controls/control.js b/lib/controls/control.js index 100b8c9..57b9ccd 100644 --- a/lib/controls/control.js +++ b/lib/controls/control.js @@ -1,67 +1,61 @@ // Copyright 2011 Mark Cavage, Inc. All rights reserved. -var assert = require('assert-plus'); -var util = require('util'); +var assert = require('assert-plus') -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 - -///--- API - -function Control(options) { - assert.optionalObject(options); - options = options || {}; - assert.optionalString(options.type); - assert.optionalBool(options.criticality); +function Control (options) { + assert.optionalObject(options) + options = options || {} + assert.optionalString(options.type) + assert.optionalBool(options.criticality) if (options.value) { - assert.buffer(options.value); + assert.buffer(options.value) } - this.type = options.type || ''; - this.criticality = options.critical || options.criticality || false; - this.value = options.value || null; + this.type = options.type || '' + this.criticality = options.critical || options.criticality || false + this.value = options.value || null } Object.defineProperties(Control.prototype, { json: { - get: function getJson() { + get: function getJson () { var obj = { controlType: this.type, criticality: this.criticality, 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) { - assert.ok(ber); +Control.prototype.toBer = function toBer (ber) { + assert.ok(ber) - ber.startSequence(); - ber.writeString(this.type || ''); - ber.writeBoolean(this.criticality); + ber.startSequence() + ber.writeString(this.type || '') + ber.writeBoolean(this.criticality) if (typeof (this._toBer) === 'function') { - this._toBer(ber); + this._toBer(ber) } else { - if (this.value) - ber.writeString(this.value); + if (this.value) { ber.writeString(this.value) } } - ber.endSequence(); - return; -}; + ber.endSequence() +} -Control.prototype.toString = function toString() { - return this.json; -}; +Control.prototype.toString = function toString () { + return this.json +} - -///--- Exports -module.exports = Control; +/// --- Exports +module.exports = Control diff --git a/lib/controls/entry_change_notification_control.js b/lib/controls/entry_change_notification_control.js index 11e3b69..9ed92ad 100644 --- a/lib/controls/entry_change_notification_control.js +++ b/lib/controls/entry_change_notification_control.js @@ -1,89 +1,83 @@ -var assert = require('assert-plus'); -var util = require('util'); +var assert = require('assert-plus') +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; -var BerWriter = asn1.BerWriter; +/// --- API - -///--- API - -function EntryChangeNotificationControl(options) { - assert.optionalObject(options); - options = options || {}; - options.type = EntryChangeNotificationControl.OID; +function EntryChangeNotificationControl (options) { + assert.optionalObject(options) + options = options || {} + options.type = EntryChangeNotificationControl.OID if (options.value) { if (Buffer.isBuffer(options.value)) { - this.parse(options.value); + this.parse(options.value) } else if (typeof (options.value) === 'object') { - this._value = options.value; + this._value = options.value } 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, { value: { - get: function () { return this._value || {}; }, + get: function () { return this._value || {} }, configurable: false } -}); +}) -EntryChangeNotificationControl.prototype.parse = function parse(buffer) { - assert.ok(buffer); +EntryChangeNotificationControl.prototype.parse = function parse (buffer) { + assert.ok(buffer) - var ber = new BerReader(buffer); + var ber = new BerReader(buffer) if (ber.readSequence()) { this._value = { changeType: ber.readInt() - }; + } // if the operation was moddn, then parse the optional previousDN attr - if (this._value.changeType === 8) - this._value.previousDN = ber.readString(); + if (this._value.changeType === 8) { 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) { - assert.ok(ber); + assert.ok(ber) - if (!this._value) - return; + if (!this._value) { return } - var writer = new BerWriter(); - writer.startSequence(); - writer.writeInt(this.value.changeType); - if (this.value.previousDN) - writer.writeString(this.value.previousDN); + var writer = new BerWriter() + writer.startSequence() + writer.writeInt(this.value.changeType) + if (this.value.previousDN) { writer.writeString(this.value.previousDN) } - writer.writeInt(parseInt(this.value.changeNumber, 10)); - writer.endSequence(); + writer.writeInt(parseInt(this.value.changeNumber, 10)) + writer.endSequence() - ber.writeBuffer(writer.buffer, 0x04); -}; + ber.writeBuffer(writer.buffer, 0x04) +} EntryChangeNotificationControl.prototype._json = function (obj) { - obj.controlValue = this.value; - return obj; -}; + obj.controlValue = this.value + return obj +} -EntryChangeNotificationControl.OID = '2.16.840.1.113730.3.4.7'; +EntryChangeNotificationControl.OID = '2.16.840.1.113730.3.4.7' - -///--- Exports -module.exports = EntryChangeNotificationControl; +/// --- Exports +module.exports = EntryChangeNotificationControl diff --git a/lib/controls/index.js b/lib/controls/index.js index f53b7f6..dffc288 100644 --- a/lib/controls/index.js +++ b/lib/controls/index.js @@ -1,73 +1,78 @@ // Copyright 2011 Mark Cavage, Inc. All rights reserved. -var assert = require('assert'); -var Ber = require('asn1').Ber; +var assert = require('assert') +var Ber = require('asn1').Ber -var Control = require('./control'); +var Control = require('./control') var EntryChangeNotificationControl = - require('./entry_change_notification_control'); -var PersistentSearchControl = require('./persistent_search_control'); -var PagedResultsControl = require('./paged_results_control'); + require('./entry_change_notification_control') +var PersistentSearchControl = require('./persistent_search_control') +var PagedResultsControl = require('./paged_results_control') var ServerSideSortingRequestControl = - require('./server_side_sorting_request_control.js'); + require('./server_side_sorting_request_control.js') 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 = { - getControl: function getControl(ber) { - assert.ok(ber); + getControl: function getControl (ber) { + assert.ok(ber) - if (ber.readSequence() === null) - return null; + if (ber.readSequence() === null) { return null } - var type; + var type var opts = { criticality: false, value: null - }; + } 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.peek() === Ber.Boolean) - opts.criticality = ber.readBoolean(); + if (ber.peek() === Ber.Boolean) { opts.criticality = ber.readBoolean() } } - if (ber.offset < end) - opts.value = ber.readString(Ber.OctetString, true); + if (ber.offset < end) { opts.value = ber.readString(Ber.OctetString, true) } } - var control; + var control switch (type) { - case PersistentSearchControl.OID: - control = new PersistentSearchControl(opts); - break; - case EntryChangeNotificationControl.OID: - control = new EntryChangeNotificationControl(opts); - break; - case PagedResultsControl.OID: - control = new PagedResultsControl(opts); - break; - case ServerSideSortingRequestControl.OID: - control = new ServerSideSortingRequestControl(opts); - break; - case ServerSideSortingResponseControl.OID: - control = new ServerSideSortingResponseControl(opts); - break; - default: - opts.type = type; - control = new Control(opts); - break; + case PersistentSearchControl.OID: + control = new PersistentSearchControl(opts) + break + case EntryChangeNotificationControl.OID: + control = new EntryChangeNotificationControl(opts) + break + case PagedResultsControl.OID: + control = new PagedResultsControl(opts) + break + case ServerSideSortingRequestControl.OID: + control = new ServerSideSortingRequestControl(opts) + break + case ServerSideSortingResponseControl.OID: + control = new ServerSideSortingResponseControl(opts) + break + case VirtualListViewRequestControl.OID: + control = new VirtualListViewRequestControl(opts) + break + case VirtualListViewResponseControl.OID: + control = new VirtualListViewResponseControl(opts) + break + default: + opts.type = type + control = new Control(opts) + break } - return control; + return control }, Control: Control, @@ -75,5 +80,7 @@ module.exports = { PagedResultsControl: PagedResultsControl, PersistentSearchControl: PersistentSearchControl, ServerSideSortingRequestControl: ServerSideSortingRequestControl, - ServerSideSortingResponseControl: ServerSideSortingResponseControl -}; + ServerSideSortingResponseControl: ServerSideSortingResponseControl, + VirtualListViewRequestControl: VirtualListViewRequestControl, + VirtualListViewResponseControl: VirtualListViewResponseControl +} diff --git a/lib/controls/paged_results_control.js b/lib/controls/paged_results_control.js index a89c661..de6b23b 100644 --- a/lib/controls/paged_results_control.js +++ b/lib/controls/paged_results_control.js @@ -1,87 +1,82 @@ -var assert = require('assert-plus'); -var util = require('util'); +var assert = require('assert-plus') +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; -var BerWriter = asn1.BerWriter; +/// --- API - -///--- API - -function PagedResultsControl(options) { - assert.optionalObject(options); - options = options || {}; - options.type = PagedResultsControl.OID; +function PagedResultsControl (options) { + assert.optionalObject(options) + options = options || {} + options.type = PagedResultsControl.OID if (options.value) { if (Buffer.isBuffer(options.value)) { - this.parse(options.value); + this.parse(options.value) } else if (typeof (options.value) === 'object') { - this._value = options.value; + this._value = options.value } 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, { value: { - get: function () { return this._value || {}; }, + get: function () { return this._value || {} }, configurable: false } -}); +}) -PagedResultsControl.prototype.parse = function parse(buffer) { - assert.ok(buffer); +PagedResultsControl.prototype.parse = function parse (buffer) { + assert.ok(buffer) - var ber = new BerReader(buffer); + var ber = new BerReader(buffer) if (ber.readSequence()) { - this._value = {}; - this._value.size = ber.readInt(); - this._value.cookie = ber.readString(asn1.Ber.OctetString, true); - //readString returns '' instead of a zero-length buffer - if (!this._value.cookie) - this._value.cookie = new Buffer(0); + this._value = {} + this._value.size = ber.readInt() + this._value.cookie = ber.readString(asn1.Ber.OctetString, true) + // readString returns '' instead of a zero-length buffer + if (!this._value.cookie) { this._value.cookie = Buffer.alloc(0) } - return true; + return true } - return false; -}; + return false +} PagedResultsControl.prototype._toBer = function (ber) { - assert.ok(ber); + assert.ok(ber) - if (!this._value) - return; + if (!this._value) { return } - var writer = new BerWriter(); - writer.startSequence(); - writer.writeInt(this.value.size); + var writer = new BerWriter() + writer.startSequence() + writer.writeInt(this.value.size) if (this.value.cookie && this.value.cookie.length > 0) { - writer.writeBuffer(this.value.cookie, asn1.Ber.OctetString); + writer.writeBuffer(this.value.cookie, asn1.Ber.OctetString) } 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) { - obj.controlValue = this.value; - return obj; -}; + obj.controlValue = this.value + return obj +} -PagedResultsControl.OID = '1.2.840.113556.1.4.319'; +PagedResultsControl.OID = '1.2.840.113556.1.4.319' - -///--- Exports -module.exports = PagedResultsControl; +/// --- Exports +module.exports = PagedResultsControl diff --git a/lib/controls/persistent_search_control.js b/lib/controls/persistent_search_control.js index ee3a7b1..bf5695f 100644 --- a/lib/controls/persistent_search_control.js +++ b/lib/controls/persistent_search_control.js @@ -1,85 +1,82 @@ // Copyright 2011 Mark Cavage, Inc. All rights reserved. -var assert = require('assert-plus'); -var util = require('util'); +var assert = require('assert-plus') +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; -var BerWriter = asn1.BerWriter; +/// --- API - -///--- API - -function PersistentSearchControl(options) { - assert.optionalObject(options); - options = options || {}; - options.type = PersistentSearchControl.OID; +function PersistentSearchControl (options) { + assert.optionalObject(options) + options = options || {} + options.type = PersistentSearchControl.OID if (options.value) { if (Buffer.isBuffer(options.value)) { - this.parse(options.value); + this.parse(options.value) } else if (typeof (options.value) === 'object') { - this._value = options.value; + this._value = options.value } 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, { value: { - get: function () { return this._value || {}; }, + get: function () { return this._value || {} }, configurable: false } -}); +}) -PersistentSearchControl.prototype.parse = function parse(buffer) { - assert.ok(buffer); +PersistentSearchControl.prototype.parse = function parse (buffer) { + assert.ok(buffer) - var ber = new BerReader(buffer); + var ber = new BerReader(buffer) if (ber.readSequence()) { this._value = { changeTypes: ber.readInt(), changesOnly: ber.readBoolean(), returnECs: ber.readBoolean() - }; + } - return true; + return true } - return false; -}; + return false +} PersistentSearchControl.prototype._toBer = function (ber) { - assert.ok(ber); + assert.ok(ber) - if (!this._value) - return; + if (!this._value) { return } - var writer = new BerWriter(); - writer.startSequence(); - writer.writeInt(this.value.changeTypes); - writer.writeBoolean(this.value.changesOnly); - writer.writeBoolean(this.value.returnECs); - writer.endSequence(); + var writer = new BerWriter() + writer.startSequence() + writer.writeInt(this.value.changeTypes) + writer.writeBoolean(this.value.changesOnly) + writer.writeBoolean(this.value.returnECs) + writer.endSequence() - ber.writeBuffer(writer.buffer, 0x04); -}; + ber.writeBuffer(writer.buffer, 0x04) +} PersistentSearchControl.prototype._json = function (obj) { - obj.controlValue = this.value; - return obj; -}; + obj.controlValue = this.value + return obj +} -PersistentSearchControl.OID = '2.16.840.1.113730.3.4.3'; +PersistentSearchControl.OID = '2.16.840.1.113730.3.4.3' -///--- Exports -module.exports = PersistentSearchControl; +/// --- Exports +module.exports = PersistentSearchControl diff --git a/lib/controls/server_side_sorting_request_control.js b/lib/controls/server_side_sorting_request_control.js index 8daf76f..2bf8baf 100644 --- a/lib/controls/server_side_sorting_request_control.js +++ b/lib/controls/server_side_sorting_request_control.js @@ -1,112 +1,108 @@ -var assert = require('assert-plus'); -var util = require('util'); +var assert = require('assert-plus') +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; -var BerWriter = asn1.BerWriter; +/// --- API - -///--- API - -function ServerSideSortingRequestControl(options) { - assert.optionalObject(options); - options = options || {}; - options.type = ServerSideSortingRequestControl.OID; +function ServerSideSortingRequestControl (options) { + assert.optionalObject(options) + options = options || {} + options.type = ServerSideSortingRequestControl.OID if (options.value) { if (Buffer.isBuffer(options.value)) { - this.parse(options.value); + this.parse(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++) { - if (!options.value[i].hasOwnProperty('attributeType')) { - throw new Error('Missing required key: attributeType'); + if (Object.prototype.hasOwnProperty.call(options.value[i], 'attributeType') === false) { + throw new Error('Missing required key: attributeType') } } - this._value = options.value; + this._value = options.value } else if (typeof (options.value) === 'object') { - if (!options.value.hasOwnProperty('attributeType')) { - throw new Error('Missing required key: attributeType'); + if (Object.prototype.hasOwnProperty.call(options.value, 'attributeType') === false) { + throw new Error('Missing required key: attributeType') } - this._value = [options.value]; + this._value = [options.value] } 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, { value: { - get: function () { return this._value || []; }, + get: function () { return this._value || [] }, configurable: false } -}); +}) -ServerSideSortingRequestControl.prototype.parse = function parse(buffer) { - assert.ok(buffer); +ServerSideSortingRequestControl.prototype.parse = function parse (buffer) { + assert.ok(buffer) - var ber = new BerReader(buffer); - var item; + var ber = new BerReader(buffer) + var item if (ber.readSequence(0x30)) { - this._value = []; + this._value = [] while (ber.readSequence(0x30)) { - item = {}; - item.attributeType = ber.readString(asn1.Ber.OctetString); - if (ber.peek() == 0x80) { - item.orderingRule = ber.readString(0x80); + item = {} + item.attributeType = ber.readString(asn1.Ber.OctetString) + if (ber.peek() === 0x80) { + item.orderingRule = ber.readString(0x80) } - if (ber.peek() == 0x81) { - item.reverseOrder = (ber._readTag(0x81) === 0 ? false : true); + if (ber.peek() === 0x81) { + 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) { - assert.ok(ber); + assert.ok(ber) - if (!this._value || this.value.length === 0) - return; + if (!this._value || this.value.length === 0) { return } - var writer = new BerWriter(); - writer.startSequence(0x30); + var writer = new BerWriter() + writer.startSequence(0x30) for (var i = 0; i < this.value.length; i++) { - var item = this.value[i]; - writer.startSequence(0x30); + var item = this.value[i] + writer.startSequence(0x30) if (item.attributeType) { - writer.writeString(item.attributeType, asn1.Ber.OctetString); + writer.writeString(item.attributeType, asn1.Ber.OctetString) } if (item.orderingRule) { - writer.writeString(item.orderingRule, 0x80); + writer.writeString(item.orderingRule, 0x80) } if (item.reverseOrder) { - writer.writeBoolean(item.reverseOrder, 0x81); + writer.writeBoolean(item.reverseOrder, 0x81) } - writer.endSequence(); + writer.endSequence() } - writer.endSequence(); - ber.writeBuffer(writer.buffer, 0x04); -}; + writer.endSequence() + ber.writeBuffer(writer.buffer, 0x04) +} ServerSideSortingRequestControl.prototype._json = function (obj) { - obj.controlValue = this.value; - return obj; -}; + obj.controlValue = this.value + 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 diff --git a/lib/controls/server_side_sorting_response_control.js b/lib/controls/server_side_sorting_response_control.js index c82e60a..8d2d20c 100644 --- a/lib/controls/server_side_sorting_response_control.js +++ b/lib/controls/server_side_sorting_response_control.js @@ -1,16 +1,15 @@ -var assert = require('assert-plus'); -var util = require('util'); +var assert = require('assert-plus') +var util = require('util') -var asn1 = require('asn1'); +var asn1 = require('asn1') -var Control = require('./control'); -var CODES = require('../errors/codes'); +var Control = require('./control') +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 = [ CODES.LDAP_SUCCESS, @@ -24,80 +23,78 @@ var VALID_CODES = [ CODES.LDAP_BUSY, CODES.LDAP_UNWILLING_TO_PERFORM, CODES.LDAP_OTHER -]; +] -function ServerSideSortingResponseControl(options) { - assert.optionalObject(options); - options = options || {}; - options.type = ServerSideSortingResponseControl.OID; - options.criticality = false; +function ServerSideSortingResponseControl (options) { + assert.optionalObject(options) + options = options || {} + options.type = ServerSideSortingResponseControl.OID + options.criticality = false if (options.value) { if (Buffer.isBuffer(options.value)) { - this.parse(options.value); + this.parse(options.value) } else if (typeof (options.value) === 'object') { if (VALID_CODES.indexOf(options.value.result) === -1) { - throw new Error('Invalid result code'); + throw new Error('Invalid result code') } if (options.value.failedAttribute && 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 { - 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, { value: { - get: function () { return this._value || {}; }, + get: function () { return this._value || {} }, configurable: false } -}); +}) -ServerSideSortingResponseControl.prototype.parse = function parse(buffer) { - assert.ok(buffer); +ServerSideSortingResponseControl.prototype.parse = function parse (buffer) { + assert.ok(buffer) - var ber = new BerReader(buffer); + var ber = new BerReader(buffer) if (ber.readSequence(0x30)) { - this._value = {}; - this._value.result = ber.readEnumeration(); - if (ber.peek() == 0x80) { - this._value.failedAttribute = ber.readString(0x80); + this._value = {} + this._value.result = ber.readEnumeration() + if (ber.peek() === 0x80) { + this._value.failedAttribute = ber.readString(0x80) } - return true; + return true } - return false; -}; + return false +} ServerSideSortingResponseControl.prototype._toBer = function (ber) { - assert.ok(ber); + assert.ok(ber) - if (!this._value || this.value.length === 0) - return; + if (!this._value || this.value.length === 0) { return } - var writer = new BerWriter(); - writer.startSequence(0x30); - writer.writeEnumeration(this.value.result); + var writer = new BerWriter() + writer.startSequence(0x30) + writer.writeEnumeration(this.value.result) if (this.value.result !== CODES.LDAP_SUCCESS && this.value.failedAttribute) { - writer.writeString(this.value.failedAttribute, 0x80); + writer.writeString(this.value.failedAttribute, 0x80) } - writer.endSequence(); - ber.writeBuffer(writer.buffer, 0x04); -}; + writer.endSequence() + ber.writeBuffer(writer.buffer, 0x04) +} ServerSideSortingResponseControl.prototype._json = function (obj) { - obj.controlValue = this.value; - return obj; -}; + obj.controlValue = this.value + return obj +} -ServerSideSortingResponseControl.OID = '1.2.840.113556.1.4.474'; +ServerSideSortingResponseControl.OID = '1.2.840.113556.1.4.474' - -///--- Exports -module.exports = ServerSideSortingResponseControl; +/// --- Exports +module.exports = ServerSideSortingResponseControl diff --git a/lib/controls/virtual_list_view_request_control.js b/lib/controls/virtual_list_view_request_control.js new file mode 100644 index 0000000..b49dc58 --- /dev/null +++ b/lib/controls/virtual_list_view_request_control.js @@ -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 diff --git a/lib/controls/virtual_list_view_response_control.js b/lib/controls/virtual_list_view_response_control.js new file mode 100644 index 0000000..2dd66e1 --- /dev/null +++ b/lib/controls/virtual_list_view_response_control.js @@ -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 diff --git a/lib/corked_emitter.js b/lib/corked_emitter.js new file mode 100644 index 0000000..c5f59de --- /dev/null +++ b/lib/corked_emitter.js @@ -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 diff --git a/lib/dn.js b/lib/dn.js index b03ce15..2d93738 100644 --- a/lib/dn.js +++ b/lib/dn.js @@ -1,336 +1,317 @@ // Copyright 2011 Mark Cavage, Inc. All rights reserved. +var assert = require('assert-plus') -var assert = require('assert-plus'); +/// --- Helpers - -///--- Helpers - -function invalidDN(name) { - var e = new Error(); - e.name = 'InvalidDistinguishedNameError'; - e.message = name; - return e; +function invalidDN (name) { + var e = new Error() + e.name = 'InvalidDistinguishedNameError' + e.message = name + return e } -function isAlphaNumeric(c) { - var re = /[A-Za-z0-9]/; - return re.test(c); +function isAlphaNumeric (c) { + var re = /[A-Za-z0-9]/ + return re.test(c) } -function isWhitespace(c) { - var re = /\s/; - return re.test(c); +function isWhitespace (c) { + var re = /\s/ + return re.test(c) } -function repeatChar(c, n) { - var out = ''; - var max = n ? n : 0; - for (var i = 0; i < max; i++) - out += c; - return out; +function repeatChar (c, n) { + var out = '' + var max = n || 0 + for (var i = 0; i < max; i++) { out += c } + return out } -///--- API +/// --- API -function RDN(obj) { - var self = this; - this.attrs = {}; +function RDN (obj) { + var self = this + this.attrs = {} if (obj) { Object.keys(obj).forEach(function (k) { - self.set(k, obj[k]); - }); + self.set(k, obj[k]) + }) } } -RDN.prototype.set = function rdnSet(name, value, opts) { - assert.string(name, 'name (string) required'); - assert.string(value, 'value (string) required'); +RDN.prototype.set = function rdnSet (name, value, opts) { + assert.string(name, 'name (string) required') + assert.string(value, 'value (string) required') - var self = this; - var lname = name.toLowerCase(); + var self = this + var lname = name.toLowerCase() this.attrs[lname] = { value: value, name: name - }; + } if (opts && typeof (opts) === 'object') { Object.keys(opts).forEach(function (k) { - if (k !== 'value') - self.attrs[lname][k] = opts[k]; - }); + if (k !== 'value') { self.attrs[lname][k] = opts[k] } + }) } -}; +} -RDN.prototype.equals = function rdnEquals(rdn) { - if (typeof (rdn) !== 'object') - return false; +RDN.prototype.equals = function rdnEquals (rdn) { + if (typeof (rdn) !== 'object') { return false } - var ourKeys = Object.keys(this.attrs); - var theirKeys = Object.keys(rdn.attrs); - if (ourKeys.length !== theirKeys.length) - return false; + var ourKeys = Object.keys(this.attrs) + var theirKeys = Object.keys(rdn.attrs) + if (ourKeys.length !== theirKeys.length) { return false } - ourKeys.sort(); - theirKeys.sort(); + ourKeys.sort() + theirKeys.sort() for (var i = 0; i < ourKeys.length; i++) { - if (ourKeys[i] !== theirKeys[i]) - return false; - if (this.attrs[ourKeys[i]].value !== rdn.attrs[ourKeys[i]].value) - return false; + if (ourKeys[i] !== theirKeys[i]) { 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. * (see: DN.format for option details) */ -RDN.prototype.format = function rdnFormat(options) { - assert.optionalObject(options, 'options must be an object'); - options = options || {}; +RDN.prototype.format = function rdnFormat (options) { + assert.optionalObject(options, 'options must be an object') + options = options || {} - var self = this; - var str = ''; + var self = this + var str = '' - function escapeValue(val, forceQuote) { - var out = ''; - var cur = 0; - var len = val.length; - var quoted = false; + function escapeValue (val, forceQuote) { + var out = '' + var cur = 0 + var len = val.length + var quoted = false /* BEGIN JSSTYLED */ - var escaped = /[\\\"]/; - var special = /[,=+<>#;]/; + // TODO: figure out what this regex is actually trying to test for and + // fix it to appease the linter. + /* eslint-disable-next-line no-useless-escape */ + var escaped = /[\\\"]/ + var special = /[,=+<>#;]/ /* END JSSTYLED */ if (len > 0) { // Wrap strings with trailing or leading spaces in quotes - quoted = forceQuote || (val[0] == ' ' || val[len-1] == ' '); + quoted = forceQuote || (val[0] === ' ' || val[len - 1] === ' ') } while (cur < len) { if (escaped.test(val[cur]) || (!quoted && special.test(val[cur]))) { - out += '\\'; + out += '\\' } - out += val[cur++]; + out += val[cur++] } - if (quoted) - out = '"' + out + '"'; - return out; + if (quoted) { out = '"' + out + '"' } + return out } - function sortParsed(a, b) { - return self.attrs[a].order - self.attrs[b].order; + function sortParsed (a, b) { + return self.attrs[a].order - self.attrs[b].order } - function sortStandard(a, b) { - var nameCompare = a.localeCompare(b); + function sortStandard (a, b) { + var nameCompare = a.localeCompare(b) if (nameCompare === 0) { // TODO: Handle binary values - return self.attrs[a].value.localeCompare(self.attrs[b].value); + return self.attrs[a].value.localeCompare(self.attrs[b].value) } else { - return nameCompare; + return nameCompare } } - var keys = Object.keys(this.attrs); + var keys = Object.keys(this.attrs) if (options.keepOrder) { - keys.sort(sortParsed); + keys.sort(sortParsed) } else { - keys.sort(sortStandard); + keys.sort(sortStandard) } keys.forEach(function (key) { - var attr = self.attrs[key]; - if (str.length) - str += '+'; + var attr = self.attrs[key] + if (str.length) { str += '+' } if (options.keepCase) { - str += attr.name; + str += attr.name } else { - if (options.upperName) - str += key.toUpperCase(); - else - str += key; + if (options.upperName) { str += key.toUpperCase() } else { str += key } } - str += '=' + escapeValue(attr.value, (options.keepQuote && attr.quoted)); - }); + str += '=' + escapeValue(attr.value, (options.keepQuote && attr.quoted)) + }) - return str; -}; - -RDN.prototype.toString = function rdnToString() { - return this.format(); -}; + return str +} +RDN.prototype.toString = function rdnToString () { + return this.format() +} // Thank you OpenJDK! -function parse(name) { - if (typeof (name) !== 'string') - throw new TypeError('name (string) required'); +function parse (name) { + if (typeof (name) !== 'string') { throw new TypeError('name (string) required') } - var cur = 0; - var len = name.length; + var cur = 0 + var len = name.length - function parseRdn() { - var rdn = new RDN(); - var order = 0; - rdn.spLead = trim(); + function parseRdn () { + var rdn = new RDN() + var order = 0 + rdn.spLead = trim() while (cur < len) { var opts = { order: order - }; - var attr = parseAttrType(); - trim(); - if (cur >= len || name[cur++] !== '=') - throw invalidDN(name); + } + var attr = parseAttrType() + trim() + if (cur >= len || name[cur++] !== '=') { throw invalidDN(name) } - trim(); + trim() // Parameters about RDN value are set in 'opts' by parseAttrValue - var value = parseAttrValue(opts); - rdn.set(attr, value, opts); - rdn.spTrail = trim(); - if (cur >= len || name[cur] !== '+') - break; - ++cur; - ++order; + var value = parseAttrValue(opts) + rdn.set(attr, value, opts) + rdn.spTrail = trim() + if (cur >= len || name[cur] !== '+') { break } + ++cur + ++order } - return rdn; + return rdn } - - function trim() { - var count = 0; + function trim () { + var count = 0 while ((cur < len) && isWhitespace(name[cur])) { - ++cur; - count++; + ++cur + count++ } - return count; + return count } - function parseAttrType() { - var beg = cur; + function parseAttrType () { + var beg = cur while (cur < len) { - var c = name[cur]; + var c = name[cur] if (isAlphaNumeric(c) || - c == '.' || - c == '-' || - c == ' ') { - ++cur; + c === '.' || + c === '-' || + c === ' ') { + ++cur } else { - break; + break } } // Back out any trailing spaces. - while ((cur > beg) && (name[cur - 1] == ' ')) - --cur; + while ((cur > beg) && (name[cur - 1] === ' ')) { --cur } - if (beg == cur) - throw invalidDN(name); + if (beg === cur) { throw invalidDN(name) } - return name.slice(beg, cur); + return name.slice(beg, cur) } - function parseAttrValue(opts) { - if (cur < len && name[cur] == '#') { - opts.binary = true; - return parseBinaryAttrValue(); - } else if (cur < len && name[cur] == '"') { - opts.quoted = true; - return parseQuotedAttrValue(); + function parseAttrValue (opts) { + if (cur < len && name[cur] === '#') { + opts.binary = true + return parseBinaryAttrValue() + } else if (cur < len && name[cur] === '"') { + opts.quoted = true + return parseQuotedAttrValue() } else { - return parseStringAttrValue(); + return parseStringAttrValue() } } - function parseBinaryAttrValue() { - var beg = cur++; - while (cur < len && isAlphaNumeric(name[cur])) - ++cur; + function parseBinaryAttrValue () { + var beg = cur++ + while (cur < len && isAlphaNumeric(name[cur])) { ++cur } - return name.slice(beg, cur); + return name.slice(beg, cur) } - function parseQuotedAttrValue() { - var str = ''; - ++cur; // Consume the first quote + function parseQuotedAttrValue () { + var str = '' + ++cur // Consume the first quote - while ((cur < len) && name[cur] != '"') { - if (name[cur] === '\\') - cur++; - str += name[cur++]; + while ((cur < len) && name[cur] !== '"') { + if (name[cur] === '\\') { 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() { - var beg = cur; - var str = ''; - var esc = -1; + function parseStringAttrValue () { + var beg = cur + var str = '' + var esc = -1 while ((cur < len) && !atTerminator()) { if (name[cur] === '\\') { // Consume the backslash and mark its place just in case it's escaping // whitespace which needs to be preserved. - esc = cur++; + esc = cur++ } - if (cur === len) // backslash followed by nothing - throw invalidDN(name); - str += name[cur++]; + if (cur === len) { + // backslash followed by nothing + throw invalidDN(name) + } + str += name[cur++] } // Trim off (unescaped) trailing whitespace and rewind cursor to the end of // the AttrValue to record whitespace length. for (; cur > beg; cur--) { - if (!isWhitespace(name[cur - 1]) || (esc === (cur - 1))) - break; + if (!isWhitespace(name[cur - 1]) || (esc === (cur - 1))) { break } } - return str.slice(0, cur - beg); + return str.slice(0, cur - beg) } - function atTerminator() { + function atTerminator () { return (cur < len && (name[cur] === ',' || name[cur] === ';' || - name[cur] === '+')); + name[cur] === '+')) } - var rdns = []; + var rdns = [] // Short-circuit for empty DNs - if (len === 0) - return new DN(rdns); + if (len === 0) { return new DN(rdns) } - rdns.push(parseRdn()); + rdns.push(parseRdn()) while (cur < len) { if (name[cur] === ',' || name[cur] === ';') { - ++cur; - rdns.push(parseRdn()); + ++cur + rdns.push(parseRdn()) } 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) { - assert.optionalArrayOfObject(rdns, '[object] required'); - - this.rdns = rdns ? rdns.slice() : []; - this._format = {}; + this.rdns = rdns ? rdns.slice() : [] + this._format = {} } Object.defineProperties(DN.prototype, { length: { - get: function getLength() { return this.rdns.length; }, + get: function getLength () { return this.rdns.length }, configurable: false } -}); +}) /** * 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. * - skipSpace: Disable trailing space after RDN separators */ -DN.prototype.format = function dnFormat(options) { - assert.optionalObject(options, 'options must be an object'); - options = options || this._format; +DN.prototype.format = function dnFormat (options) { + assert.optionalObject(options, 'options must be an object') + options = options || this._format - var str = ''; + var str = '' this.rdns.forEach(function (rdn) { - var rdnString = rdn.format(options); + var rdnString = rdn.format(options) if (str.length !== 0) { - str += ','; + str += ',' } if (options.keepSpace) { str += (repeatChar(' ', rdn.spLead) + - rdnString + repeatChar(' ', rdn.spTrail)); + rdnString + repeatChar(' ', rdn.spTrail)) } else if (options.skipSpace === true || str.length === 0) { - str += rdnString; + str += rdnString } else { - str += ' ' + rdnString; + str += ' ' + rdnString } - }); - return str; -}; + }) + return str +} /** * Set default string formatting options. */ -DN.prototype.setFormat = function setFormat(options) { - assert.object(options, 'options must be an object'); +DN.prototype.setFormat = function setFormat (options) { + assert.object(options, 'options must be an object') - this._format = options; -}; + this._format = options +} -DN.prototype.toString = function dnToString() { - return this.format(); -}; +DN.prototype.toString = function dnToString () { + return this.format() +} -DN.prototype.parentOf = function parentOf(dn) { - if (typeof (dn) !== 'object') - dn = parse(dn); +DN.prototype.parentOf = function parentOf (dn) { + if (typeof (dn) !== 'object') { dn = parse(dn) } - if (this.rdns.length >= dn.rdns.length) - return false; + if (this.rdns.length >= dn.rdns.length) { 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--) { - var myRDN = this.rdns[i]; - var theirRDN = dn.rdns[i + diff]; + var myRDN = this.rdns[i] + var theirRDN = dn.rdns[i + diff] - if (!myRDN.equals(theirRDN)) - return false; + if (!myRDN.equals(theirRDN)) { return false } } - return true; -}; + return true +} -DN.prototype.childOf = function childOf(dn) { - if (typeof (dn) !== 'object') - dn = parse(dn); - return dn.parentOf(this); -}; +DN.prototype.childOf = function childOf (dn) { + if (typeof (dn) !== 'object') { dn = parse(dn) } + return dn.parentOf(this) +} -DN.prototype.isEmpty = function isEmpty() { - return (this.rdns.length === 0); -}; +DN.prototype.isEmpty = function isEmpty () { + return (this.rdns.length === 0) +} -DN.prototype.equals = function dnEquals(dn) { - if (typeof (dn) !== 'object') - dn = parse(dn); +DN.prototype.equals = function dnEquals (dn) { + if (typeof (dn) !== 'object') { dn = parse(dn) } - if (this.rdns.length !== dn.rdns.length) - return false; + if (this.rdns.length !== dn.rdns.length) { return false } for (var i = 0; i < this.rdns.length; i++) { - if (!this.rdns[i].equals(dn.rdns[i])) - return false; + if (!this.rdns[i].equals(dn.rdns[i])) { return false } } - return true; -}; + return true +} -DN.prototype.parent = function dnParent() { +DN.prototype.parent = function dnParent () { if (this.rdns.length !== 0) { - var save = this.rdns.shift(); - var dn = new DN(this.rdns); - this.rdns.unshift(save); - return dn; + var save = this.rdns.shift() + var dn = new DN(this.rdns) + this.rdns.unshift(save) + return dn } - return null; -}; + return null +} -DN.prototype.clone = function dnClone() { - var dn = new DN(this.rdns); - dn._format = this._format; - return dn; -}; +DN.prototype.clone = function dnClone () { + var dn = new DN(this.rdns) + dn._format = this._format + return dn +} -DN.prototype.reverse = function dnReverse() { - this.rdns.reverse(); - return this; -}; +DN.prototype.reverse = function dnReverse () { + this.rdns.reverse() + return this +} -DN.prototype.pop = function dnPop() { - return this.rdns.pop(); -}; +DN.prototype.pop = function dnPop () { + return this.rdns.pop() +} -DN.prototype.push = function dnPush(rdn) { - assert.object(rdn, 'rdn (RDN) required'); +DN.prototype.push = function dnPush (rdn) { + assert.object(rdn, 'rdn (RDN) required') - return this.rdns.push(rdn); -}; + return this.rdns.push(rdn) +} -DN.prototype.shift = function dnShift() { - return this.rdns.shift(); -}; +DN.prototype.shift = function dnShift () { + return this.rdns.shift() +} -DN.prototype.unshift = function dnUnshift(rdn) { - assert.object(rdn, 'rdn (RDN) required'); +DN.prototype.unshift = function dnUnshift (rdn) { + 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') { - return false; + return false } if (dn instanceof DN) { - return true; + return true } if (Array.isArray(dn.rdns)) { // Really simple duck-typing for now - return true; + return true } - return false; -}; + return false +} - -///--- Exports +/// --- Exports module.exports = { parse: parse, DN: DN, RDN: RDN -}; +} diff --git a/lib/dtrace.js b/lib/dtrace.js index 1666d85..8c088bb 100644 --- a/lib/dtrace.js +++ b/lib/dtrace.js @@ -1,12 +1,10 @@ // Copyright 2011 Mark Cavage, Inc. All rights reserved.s +/// --- Globals - -///--- Globals - -var SERVER_PROVIDER; -var DTRACE_ID = 0; -var MAX_INT = 4294967295; +var SERVER_PROVIDER +var DTRACE_ID = 0 +var MAX_INT = 4294967295 /* * Args: @@ -37,7 +35,7 @@ var SERVER_PROBES = { // 4: attribute, 5: value 'server-compare-start': ['int', 'char *', 'char *', 'char *', - 'char *', 'char *'], + 'char *', 'char *'], 'server-compare-done': ['int', 'char *', 'char *', 'char *', 'int', 'char *'], 'server-delete-start': ['int', 'char *', 'char *', 'char *'], @@ -45,7 +43,7 @@ var SERVER_PROBES = { // 4: requestName, 5: requestValue 'server-exop-start': ['int', 'char *', 'char *', 'char *', 'char *', - 'char *'], + 'char *'], 'server-exop-done': ['int', 'char *', 'char *', 'char *', 'int', 'char *'], // 4: changes.length @@ -54,13 +52,13 @@ var SERVER_PROBES = { // 4: newRdn, 5: newSuperior 'server-modifydn-start': ['int', 'char *', 'char *', 'char *', 'char *', - 'char *'], + 'char *'], 'server-modifydn-done': ['int', 'char *', 'char *', 'char *', 'int', - 'char *'], + 'char *'], // 4: scope, 5: filter 'server-search-start': ['int', 'char *', 'char *', 'char *', 'char *', - 'char *'], + 'char *'], 'server-search-done': ['int', 'char *', 'char *', 'char *', 'int', 'char *'], // Last two are searchEntry.DN and seachEntry.attributes.length 'server-search-entry': ['int', 'char *', 'char *', 'char *', 'char *', 'int'], @@ -73,52 +71,50 @@ var SERVER_PROBES = { // remote IP 'server-connection': ['char *'] -}; +} +/// --- API -///--- API - -module.exports = function () { +module.exports = (function () { if (!SERVER_PROVIDER) { try { - var dtrace = require('dtrace-provider'); - SERVER_PROVIDER = dtrace.createDTraceProvider('ldapjs'); + var dtrace = require('dtrace-provider') + SERVER_PROVIDER = dtrace.createDTraceProvider('ldapjs') Object.keys(SERVER_PROBES).forEach(function (p) { - var args = SERVER_PROBES[p].splice(0); - args.unshift(p); + var args = SERVER_PROBES[p].splice(0) + args.unshift(p) - dtrace.DTraceProvider.prototype.addProbe.apply(SERVER_PROVIDER, args); - }); + dtrace.DTraceProvider.prototype.addProbe.apply(SERVER_PROVIDER, args) + }) } catch (e) { SERVER_PROVIDER = { - fire: function () { - }, - enable: function () { - }, - addProbe: function () { - var p = { - fire: function () { - } - }; - return (p); - }, - removeProbe: function () { - }, - disable: function () { + fire: function () { + }, + enable: function () { + }, + addProbe: function () { + var p = { + fire: function () { + } } - }; + return (p) + }, + removeProbe: function () { + }, + disable: function () { + } + } } - SERVER_PROVIDER.enable(); + SERVER_PROVIDER.enable() SERVER_PROVIDER._nextId = function () { - if (DTRACE_ID === MAX_INT) - DTRACE_ID = 0; + if (DTRACE_ID === MAX_INT) { DTRACE_ID = 0 } - return ++DTRACE_ID; - }; + return ++DTRACE_ID + } } - return SERVER_PROVIDER; -}(); + return SERVER_PROVIDER +}()) diff --git a/lib/errors/codes.js b/lib/errors/codes.js index 4021a42..944398a 100644 --- a/lib/errors/codes.js +++ b/lib/errors/codes.js @@ -1,4 +1,4 @@ -// Copyright 2014 Joyent, Inc. All rights reserved. +'use strict' module.exports = { LDAP_SUCCESS: 0, @@ -32,6 +32,8 @@ module.exports = { LDAP_UNAVAILABLE: 52, LDAP_UNWILLING_TO_PERFORM: 53, LDAP_LOOP_DETECT: 54, + LDAP_SORT_CONTROL_MISSING: 60, + LDAP_INDEX_RANGE_ERROR: 61, LDAP_NAMING_VIOLATION: 64, LDAP_OBJECTCLASS_VIOLATION: 65, LDAP_NOT_ALLOWED_ON_NON_LEAF: 66, @@ -39,6 +41,7 @@ module.exports = { LDAP_ENTRY_ALREADY_EXISTS: 68, LDAP_OBJECTCLASS_MODS_PROHIBITED: 69, LDAP_AFFECTS_MULTIPLE_DSAS: 71, + LDAP_CONTROL_ERROR: 76, LDAP_OTHER: 80, LDAP_PROXIED_AUTHORIZATION_DENIED: 123 -}; +} diff --git a/lib/errors/index.js b/lib/errors/index.js index 774cf44..015d435 100644 --- a/lib/errors/index.js +++ b/lib/errors/index.js @@ -1,151 +1,144 @@ -// Copyright 2011 Mark Cavage, Inc. All rights reserved. +'use strict' -var util = require('util'); -var assert = require('assert-plus'); +var util = require('util') +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'); -var ERRORS = []; +/// --- Error Base class +function LDAPError (message, dn, caller) { + if (Error.captureStackTrace) { Error.captureStackTrace(this, caller || LDAPError) } -///--- Error Base class - -function LDAPError(message, dn, caller) { - if (Error.captureStackTrace) - Error.captureStackTrace(this, caller || LDAPError); - - this.lde_message = message; - this.lde_dn = dn; + this.lde_message = message + this.lde_dn = dn } -util.inherits(LDAPError, Error); +util.inherits(LDAPError, Error) Object.defineProperties(LDAPError.prototype, { name: { - get: function getName() { return 'LDAPError'; }, + get: function getName () { return 'LDAPError' }, configurable: false }, code: { - get: function getCode() { return CODES.LDAP_OTHER; }, + get: function getCode () { return CODES.LDAP_OTHER }, configurable: false }, message: { - get: function getMessage() { - return this.lde_message || this.name; + get: function getMessage () { + return this.lde_message || this.name }, configurable: false }, dn: { - get: function getDN() { - return (this.lde_dn ? this.lde_dn.toString() : ''); + get: function getDN () { + return (this.lde_dn ? this.lde_dn.toString() : '') }, 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 Object.keys(CODES).forEach(function (code) { - module.exports[code] = CODES[code]; - if (code === 'LDAP_SUCCESS') - return; + module.exports[code] = CODES[code] + if (code === 'LDAP_SUCCESS') { return } - var err = ''; - var msg = ''; - var pieces = code.split('_').slice(1); + var err = '' + var msg = '' + var pieces = code.split('_').slice(1) for (var i = 0; i < pieces.length; i++) { - var lc = pieces[i].toLowerCase(); - var key = lc.charAt(0).toUpperCase() + lc.slice(1); - err += key; - msg += key + ((i + 1) < pieces.length ? ' ' : ''); + var lc = pieces[i].toLowerCase() + var key = lc.charAt(0).toUpperCase() + lc.slice(1) + err += key + msg += key + ((i + 1) < pieces.length ? ' ' : '') } - if (!/\w+Error$/.test(err)) - err += 'Error'; + if (!/\w+Error$/.test(err)) { err += 'Error' } // At this point LDAP_OPERATIONS_ERROR is now OperationsError in $err // and 'Operations Error' in $msg module.exports[err] = function (message, dn, caller) { - LDAPError.call(this, message, dn, caller || module.exports[err]); - }; - module.exports[err].constructor = module.exports[err]; - util.inherits(module.exports[err], LDAPError); + LDAPError.call(this, message, dn, caller || module.exports[err]) + } + module.exports[err].constructor = module.exports[err] + util.inherits(module.exports[err], LDAPError) Object.defineProperties(module.exports[err].prototype, { name: { - get: function getName() { return err; }, + get: function getName () { return err }, configurable: false }, code: { - get: function getCode() { return CODES[code]; }, + get: function getCode () { return CODES[code] }, configurable: false } - }); + }) ERRORS[CODES[code]] = { err: err, message: msg - }; -}); + } +}) 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 E = module.exports[errObj.err]; + var errObj = ERRORS[res.status] + var E = module.exports[errObj.err] return new E(res.errorMessage || errObj.message, - res.matchedDN || null, - module.exports.getError); -}; + res.matchedDN || null, + module.exports.getError) +} module.exports.getMessage = function (code) { - assert.number(code, 'code (number) required'); + assert.number(code, 'code (number) required') - var errObj = ERRORS[code]; - return (errObj && errObj.message ? errObj.message : ''); -}; - - -///--- Custom application errors - -function ConnectionError(message) { - LDAPError.call(this, message, null, ConnectionError); + var errObj = ERRORS[code] + return (errObj && errObj.message ? errObj.message : '') } -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, { name: { - get: function () { return 'ConnectionError'; }, + get: function () { return 'ConnectionError' }, configurable: false } -}); +}) -function AbandonedError(message) { - LDAPError.call(this, message, null, AbandonedError); +function AbandonedError (message) { + LDAPError.call(this, message, null, AbandonedError) } -util.inherits(AbandonedError, LDAPError); -module.exports.AbandonedError = AbandonedError; +util.inherits(AbandonedError, LDAPError) +module.exports.AbandonedError = AbandonedError Object.defineProperties(AbandonedError.prototype, { name: { - get: function () { return 'AbandonedError'; }, + get: function () { return 'AbandonedError' }, configurable: false } -}); +}) -function TimeoutError(message) { - LDAPError.call(this, message, null, TimeoutError); +function TimeoutError (message) { + LDAPError.call(this, message, null, TimeoutError) } -util.inherits(TimeoutError, LDAPError); -module.exports.TimeoutError = TimeoutError; +util.inherits(TimeoutError, LDAPError) +module.exports.TimeoutError = TimeoutError Object.defineProperties(TimeoutError.prototype, { name: { - get: function () { return 'TimeoutError'; }, + get: function () { return 'TimeoutError' }, configurable: false } -}); +}) diff --git a/lib/filters/and_filter.js b/lib/filters/and_filter.js index 2b7e73c..9708af2 100644 --- a/lib/filters/and_filter.js +++ b/lib/filters/and_filter.js @@ -1,30 +1,27 @@ // Copyright 2011 Mark Cavage, Inc. All rights reserved. -var assert = require('assert'); -var util = require('util'); +var assert = require('assert') +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 AndFilter(options) { - parents.AndFilter.call(this, options); +function AndFilter (options) { + parents.AndFilter.call(this, options) } -util.inherits(AndFilter, parents.AndFilter); -Filter.mixin(AndFilter); -module.exports = AndFilter; - +util.inherits(AndFilter, parents.AndFilter) +Filter.mixin(AndFilter) +module.exports = AndFilter AndFilter.prototype._toBer = function (ber) { - assert.ok(ber); + assert.ok(ber) this.filters.forEach(function (f) { - ber = f.toBer(ber); - }); + ber = f.toBer(ber) + }) - return ber; -}; + return ber +} diff --git a/lib/filters/approx_filter.js b/lib/filters/approx_filter.js index 2a7a1f1..34fdbd2 100644 --- a/lib/filters/approx_filter.js +++ b/lib/filters/approx_filter.js @@ -1,39 +1,35 @@ // Copyright 2011 Mark Cavage, Inc. All rights reserved. -var assert = require('assert'); -var util = require('util'); +var assert = require('assert') +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 ApproximateFilter(options) { - parents.ApproximateFilter.call(this, options); +function ApproximateFilter (options) { + parents.ApproximateFilter.call(this, options) } -util.inherits(ApproximateFilter, parents.ApproximateFilter); -Filter.mixin(ApproximateFilter); -module.exports = ApproximateFilter; - +util.inherits(ApproximateFilter, parents.ApproximateFilter) +Filter.mixin(ApproximateFilter) +module.exports = ApproximateFilter ApproximateFilter.prototype.parse = function (ber) { - assert.ok(ber); + assert.ok(ber) - this.attribute = ber.readString().toLowerCase(); - this.value = ber.readString(); - - return true; -}; + this.attribute = ber.readString().toLowerCase() + this.value = ber.readString() + return true +} ApproximateFilter.prototype._toBer = function (ber) { - assert.ok(ber); + assert.ok(ber) - ber.writeString(this.attribute); - ber.writeString(this.value); + ber.writeString(this.attribute) + ber.writeString(this.value) - return ber; -}; + return ber +} diff --git a/lib/filters/equality_filter.js b/lib/filters/equality_filter.js index dd5edea..09fb216 100644 --- a/lib/filters/equality_filter.js +++ b/lib/filters/equality_filter.js @@ -1,66 +1,60 @@ // Copyright 2011 Mark Cavage, Inc. All rights reserved. -var assert = require('assert-plus'); -var util = require('util'); +var assert = require('assert-plus') +var util = require('util') -var ASN1 = require('asn1').Ber; -var parents = require('ldap-filter'); +var ASN1 = require('asn1').Ber +var parents = require('ldap-filter') -var Filter = require('./filter'); +var Filter = require('./filter') +/// --- API - -///--- API - -function EqualityFilter(options) { - parents.EqualityFilter.call(this, options); +function EqualityFilter (options) { + parents.EqualityFilter.call(this, options) } -util.inherits(EqualityFilter, parents.EqualityFilter); -Filter.mixin(EqualityFilter); -module.exports = EqualityFilter; - +util.inherits(EqualityFilter, parents.EqualityFilter) +Filter.mixin(EqualityFilter) +module.exports = EqualityFilter EqualityFilter.prototype.matches = function (target, strictAttrCase) { - assert.object(target, 'target'); + assert.object(target, 'target') - var tv = parents.getAttrValue(target, this.attribute, strictAttrCase); - var value = this.value; + var tv = parents.getAttrValue(target, this.attribute, strictAttrCase) + var value = this.value if (this.attribute.toLowerCase() === 'objectclass') { /* * Perform case-insensitive match for objectClass since nearly every LDAP * implementation behaves in this manner. */ - value = value.toLowerCase(); + value = value.toLowerCase() return parents.testValues(function (v) { - return value === v.toLowerCase(); - }, tv); + return value === v.toLowerCase() + }, tv) } else { return parents.testValues(function (v) { - return value === v; - }, tv); + return value === v + }, tv) } -}; - +} EqualityFilter.prototype.parse = function (ber) { - assert.ok(ber); + assert.ok(ber) - this.attribute = ber.readString().toLowerCase(); - this.value = ber.readString(ASN1.OctetString, true); + this.attribute = ber.readString().toLowerCase() + this.value = ber.readString(ASN1.OctetString, true) - if (this.attribute === 'objectclass') - this.value = this.value.toLowerCase(); - - return true; -}; + if (this.attribute === 'objectclass') { this.value = this.value.toLowerCase() } + return true +} EqualityFilter.prototype._toBer = function (ber) { - assert.ok(ber); + assert.ok(ber) - ber.writeString(this.attribute); - ber.writeBuffer(this.raw, ASN1.OctetString); + ber.writeString(this.attribute) + ber.writeBuffer(this.raw, ASN1.OctetString) - return ber; -}; + return ber +} diff --git a/lib/filters/escape.js b/lib/filters/escape.js index c9aea4e..2d4da3d 100644 --- a/lib/filters/escape.js +++ b/lib/filters/escape.js @@ -14,32 +14,31 @@ */ exports.escape = function (inp) { if (typeof (inp) === 'string') { - var esc = ''; + var esc = '' for (var i = 0; i < inp.length; i++) { switch (inp[i]) { case '*': - esc += '\\2a'; - break; + esc += '\\2a' + break case '(': - esc += '\\28'; - break; + esc += '\\28' + break case ')': - esc += '\\29'; - break; + esc += '\\29' + break case '\\': - esc += '\\5c'; - break; + esc += '\\5c' + break case '\0': - esc += '\\00'; - break; + esc += '\\00' + break default: - esc += inp[i]; - break; + esc += inp[i] + break } } - return esc; - + return esc } else { - return inp; + return inp } -}; +} diff --git a/lib/filters/ext_filter.js b/lib/filters/ext_filter.js index c3ffe7e..e96ebdd 100644 --- a/lib/filters/ext_filter.js +++ b/lib/filters/ext_filter.js @@ -1,66 +1,59 @@ // Copyright 2011 Mark Cavage, Inc. All rights reserved. -var assert = require('assert'); -var util = require('util'); - -var parents = require('ldap-filter'); - -var Filter = require('./filter'); +var assert = require('assert') +var util = require('util') +var parents = require('ldap-filter') +var Filter = require('./filter') // THIS IS A STUB! // // ldapjs does not support server side extensible matching. // This class exists only for the client to send them. -///--- API +/// --- API -function ExtensibleFilter(options) { - parents.ExtensibleFilter.call(this, options); +function ExtensibleFilter (options) { + parents.ExtensibleFilter.call(this, options) } -util.inherits(ExtensibleFilter, parents.ExtensibleFilter); -Filter.mixin(ExtensibleFilter); -module.exports = ExtensibleFilter; - +util.inherits(ExtensibleFilter, parents.ExtensibleFilter) +Filter.mixin(ExtensibleFilter) +module.exports = ExtensibleFilter ExtensibleFilter.prototype.parse = function (ber) { - var end = ber.offset + ber.length; + var end = ber.offset + ber.length while (ber.offset < end) { - var tag = ber.peek(); + var tag = ber.peek() switch (tag) { - case 0x81: - this.rule = ber.readString(tag); - break; - case 0x82: - this.matchType = ber.readString(tag); - break; - case 0x83: - this.value = ber.readString(tag); - break; - case 0x84: - this.dnAttributes = ber.readBoolean(tag); - break; - default: - throw new Error('Invalid ext_match filter type: 0x' + tag.toString(16)); + case 0x81: + this.rule = ber.readString(tag) + break + case 0x82: + this.matchType = ber.readString(tag) + break + case 0x83: + this.value = ber.readString(tag) + break + case 0x84: + this.dnAttributes = ber.readBoolean(tag) + break + default: + throw new Error('Invalid ext_match filter type: 0x' + tag.toString(16)) } } - return true; -}; - + return true +} ExtensibleFilter.prototype._toBer = function (ber) { - assert.ok(ber); + assert.ok(ber) - if (this.rule) - ber.writeString(this.rule, 0x81); - if (this.matchType) - ber.writeString(this.matchType, 0x82); + if (this.rule) { ber.writeString(this.rule, 0x81) } + if (this.matchType) { ber.writeString(this.matchType, 0x82) } - ber.writeString(this.value, 0x83); - if (this.dnAttributes) - ber.writeBoolean(this.dnAttributes, 0x84); + ber.writeString(this.value, 0x83) + if (this.dnAttributes) { ber.writeBoolean(this.dnAttributes, 0x84) } - return ber; -}; + return ber +} diff --git a/lib/filters/filter.js b/lib/filters/filter.js index 60e2551..4c7e4ba 100644 --- a/lib/filters/filter.js +++ b/lib/filters/filter.js @@ -1,58 +1,55 @@ // 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 = { - 'and': Protocol.FILTER_AND, - 'or': Protocol.FILTER_OR, - 'not': Protocol.FILTER_NOT, - 'equal': Protocol.FILTER_EQUALITY, - 'substring': Protocol.FILTER_SUBSTRINGS, - 'ge': Protocol.FILTER_GE, - 'le': Protocol.FILTER_LE, - 'present': Protocol.FILTER_PRESENT, - 'approx': Protocol.FILTER_APPROX, - 'ext': Protocol.FILTER_EXT -}; + and: Protocol.FILTER_AND, + or: Protocol.FILTER_OR, + not: Protocol.FILTER_NOT, + equal: Protocol.FILTER_EQUALITY, + substring: Protocol.FILTER_SUBSTRINGS, + ge: Protocol.FILTER_GE, + le: Protocol.FILTER_LE, + present: Protocol.FILTER_PRESENT, + approx: Protocol.FILTER_APPROX, + ext: Protocol.FILTER_EXT +} +/// --- API -///--- API - -function isFilter(filter) { +function isFilter (filter) { if (!filter || typeof (filter) !== 'object') { - return false; + return false } // Do our best to duck-type it if (typeof (filter.toBer) === 'function' && typeof (filter.matches) === 'function' && TYPES[filter.type] !== undefined) { - return true; + return true } - return false; + return false } -function mixin(target) { - target.prototype.toBer = function toBer(ber) { - if (!ber || !(ber instanceof BerWriter)) - throw new TypeError('ber (BerWriter) required'); +function mixin (target) { + target.prototype.toBer = function toBer (ber) { + if (!ber || !(ber instanceof BerWriter)) { throw new TypeError('ber (BerWriter) required') } - ber.startSequence(TYPES[this.type]); - ber = this._toBer(ber); - ber.endSequence(); - return ber; - }; + ber.startSequence(TYPES[this.type]) + ber = this._toBer(ber) + ber.endSequence() + return ber + } } module.exports = { isFilter: isFilter, mixin: mixin -}; +} diff --git a/lib/filters/ge_filter.js b/lib/filters/ge_filter.js index 6f10c70..324b856 100644 --- a/lib/filters/ge_filter.js +++ b/lib/filters/ge_filter.js @@ -1,38 +1,35 @@ // Copyright 2011 Mark Cavage, Inc. All rights reserved. -var assert = require('assert'); -var util = require('util'); +var assert = require('assert') +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); -Filter.mixin(GreaterThanEqualsFilter); -module.exports = GreaterThanEqualsFilter; - +util.inherits(GreaterThanEqualsFilter, parents.GreaterThanEqualsFilter) +Filter.mixin(GreaterThanEqualsFilter) +module.exports = GreaterThanEqualsFilter GreaterThanEqualsFilter.prototype.parse = function (ber) { - assert.ok(ber); + assert.ok(ber) - this.attribute = ber.readString().toLowerCase(); - this.value = ber.readString(); - - return true; -}; + this.attribute = ber.readString().toLowerCase() + this.value = ber.readString() + return true +} GreaterThanEqualsFilter.prototype._toBer = function (ber) { - assert.ok(ber); + assert.ok(ber) - ber.writeString(this.attribute); - ber.writeString(this.value); + ber.writeString(this.attribute) + ber.writeString(this.value) - return ber; -}; + return ber +} diff --git a/lib/filters/index.js b/lib/filters/index.js index feb0717..d86eba4 100644 --- a/lib/filters/index.js +++ b/lib/filters/index.js @@ -1,33 +1,30 @@ // 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 AndFilter = require('./and_filter'); -var ApproximateFilter = require('./approx_filter'); -var EqualityFilter = require('./equality_filter'); -var ExtensibleFilter = require('./ext_filter'); -var GreaterThanEqualsFilter = require('./ge_filter'); -var LessThanEqualsFilter = require('./le_filter'); -var NotFilter = require('./not_filter'); -var OrFilter = require('./or_filter'); -var PresenceFilter = require('./presence_filter'); -var SubstringFilter = require('./substr_filter'); +var Filter = require('./filter') +var AndFilter = require('./and_filter') +var ApproximateFilter = require('./approx_filter') +var EqualityFilter = require('./equality_filter') +var ExtensibleFilter = require('./ext_filter') +var GreaterThanEqualsFilter = require('./ge_filter') +var LessThanEqualsFilter = require('./le_filter') +var NotFilter = require('./not_filter') +var OrFilter = require('./or_filter') +var PresenceFilter = require('./presence_filter') +var SubstringFilter = require('./substr_filter') +/// --- Globals +var BerReader = asn1.BerReader -///--- Globals - -var BerReader = asn1.BerReader; - - -///--- Internal Parsers +/// --- Internal Parsers /* * A filter looks like this coming in: @@ -62,132 +59,137 @@ var BerReader = asn1.BerReader; * dnAttributes [4] BOOLEAN DEFAULT FALSE * } */ -function _parse(ber) { - assert.ok(ber); +function _parse (ber) { + assert.ok(ber) - function parseSet(f) { - var end = ber.offset + ber.length; - while (ber.offset < end) - f.addFilter(_parse(ber)); + function parseSet (f) { + var end = ber.offset + ber.length + while (ber.offset < end) { f.addFilter(_parse(ber)) } } - var f; + var f - var type = ber.readSequence(); + var type = ber.readSequence() switch (type) { + case Protocol.FILTER_AND: + f = new AndFilter() + parseSet(f) + break - case Protocol.FILTER_AND: - f = new AndFilter(); - parseSet(f); - break; + case Protocol.FILTER_APPROX: + f = new ApproximateFilter() + f.parse(ber) + break - case Protocol.FILTER_APPROX: - f = new ApproximateFilter(); - f.parse(ber); - break; + case Protocol.FILTER_EQUALITY: + f = new EqualityFilter() + f.parse(ber) + return f - case Protocol.FILTER_EQUALITY: - f = new EqualityFilter(); - f.parse(ber); - return f; + case Protocol.FILTER_EXT: + f = new ExtensibleFilter() + f.parse(ber) + return f - case Protocol.FILTER_EXT: - f = new ExtensibleFilter(); - f.parse(ber); - return f; + case Protocol.FILTER_GE: + f = new GreaterThanEqualsFilter() + f.parse(ber) + return f - case Protocol.FILTER_GE: - f = new GreaterThanEqualsFilter(); - f.parse(ber); - return f; + case Protocol.FILTER_LE: + f = new LessThanEqualsFilter() + f.parse(ber) + return f - case Protocol.FILTER_LE: - f = new LessThanEqualsFilter(); - f.parse(ber); - return f; + case Protocol.FILTER_NOT: + var _f = _parse(ber) + f = new NotFilter({ + filter: _f + }) + break - case Protocol.FILTER_NOT: - var _f = _parse(ber); - f = new NotFilter({ - filter: _f - }); - break; + case Protocol.FILTER_OR: + f = new OrFilter() + parseSet(f) + break - case Protocol.FILTER_OR: - f = new OrFilter(); - parseSet(f); - break; + case Protocol.FILTER_PRESENT: + f = new PresenceFilter() + f.parse(ber) + break - case Protocol.FILTER_PRESENT: - f = new PresenceFilter(); - f.parse(ber); - break; + case Protocol.FILTER_SUBSTRINGS: + f = new SubstringFilter() + f.parse(ber) + break - case Protocol.FILTER_SUBSTRINGS: - f = new SubstringFilter(); - f.parse(ber); - break; - - default: - throw new Error('Invalid search filter type: 0x' + type.toString(16)); + default: + throw new Error('Invalid search filter type: 0x' + type.toString(16)) } - - assert.ok(f); - return f; + assert.ok(f) + return f } - -function cloneFilter(input) { - var child; +function cloneFilter (input) { + var child if (input.type === 'and' || input.type === 'or') { - child = input.filters.map(cloneFilter); + child = input.filters.map(cloneFilter) } else if (input.type === 'not') { - child = cloneFilter(input.filter); + child = cloneFilter(input.filter) } switch (input.type) { - case 'and': - return new AndFilter({filters: child}); - case 'or': - return new OrFilter({filters: child}); - case 'not': - return new NotFilter({filter: child}); - case 'equal': - return new EqualityFilter(input); - case 'substring': - return new SubstringFilter(input); - case 'ge': - return new GreaterThanEqualsFilter(input); - case 'le': - return new LessThanEqualsFilter(input); - case 'present': - return new PresenceFilter(input); - case 'approx': - return new ApproximateFilter(input); - case 'ext': - return new ExtensibleFilter(input); - default: - throw new Error('invalid filter type:' + input.type); + case 'and': + return new AndFilter({ filters: child }) + case 'or': + return new OrFilter({ filters: child }) + case 'not': + return new NotFilter({ filter: child }) + case 'equal': + return new EqualityFilter(input) + case 'substring': + return new SubstringFilter(input) + case 'ge': + return new GreaterThanEqualsFilter(input) + case 'le': + return new LessThanEqualsFilter(input) + case 'present': + return new PresenceFilter(input) + case 'approx': + return new ApproximateFilter(input) + case 'ext': + return new ExtensibleFilter(input) + default: + 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) { - var generic = parents.parse(str); - // The filter object(s) return from ldap-filter.parse lack the toBer/parse - // decoration that native ldapjs filter possess. cloneFilter adds that back. - return cloneFilter(generic); + const hexCode = p1.charCodeAt(0).toString(16) + return '\\' + hexCode + }) } +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 = { parse: function (ber) { - if (!ber || !(ber instanceof BerReader)) - throw new TypeError('ber (BerReader) required'); + if (!ber || !(ber instanceof BerReader)) { throw new TypeError('ber (BerReader) required') } - return _parse(ber); + return _parse(ber) }, parseString: parseString, @@ -204,4 +206,4 @@ module.exports = { OrFilter: OrFilter, PresenceFilter: PresenceFilter, SubstringFilter: SubstringFilter -}; +} diff --git a/lib/filters/le_filter.js b/lib/filters/le_filter.js index f3b9b7b..0b98335 100644 --- a/lib/filters/le_filter.js +++ b/lib/filters/le_filter.js @@ -1,38 +1,35 @@ // Copyright 2011 Mark Cavage, Inc. All rights reserved. -var assert = require('assert'); -var util = require('util'); +var assert = require('assert') +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); -Filter.mixin(LessThanEqualsFilter); -module.exports = LessThanEqualsFilter; - +util.inherits(LessThanEqualsFilter, parents.LessThanEqualsFilter) +Filter.mixin(LessThanEqualsFilter) +module.exports = LessThanEqualsFilter LessThanEqualsFilter.prototype.parse = function (ber) { - assert.ok(ber); + assert.ok(ber) - this.attribute = ber.readString().toLowerCase(); - this.value = ber.readString(); - - return true; -}; + this.attribute = ber.readString().toLowerCase() + this.value = ber.readString() + return true +} LessThanEqualsFilter.prototype._toBer = function (ber) { - assert.ok(ber); + assert.ok(ber) - ber.writeString(this.attribute); - ber.writeString(this.value); + ber.writeString(this.attribute) + ber.writeString(this.value) - return ber; -}; + return ber +} diff --git a/lib/filters/not_filter.js b/lib/filters/not_filter.js index 4656ca7..eee9519 100644 --- a/lib/filters/not_filter.js +++ b/lib/filters/not_filter.js @@ -1,25 +1,23 @@ // Copyright 2011 Mark Cavage, Inc. All rights reserved. -var assert = require('assert'); -var util = require('util'); +var assert = require('assert') +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); -Filter.mixin(NotFilter); -module.exports = NotFilter; - +util.inherits(NotFilter, parents.NotFilter) +Filter.mixin(NotFilter) +module.exports = NotFilter NotFilter.prototype._toBer = function (ber) { - assert.ok(ber); + assert.ok(ber) - return this.filter.toBer(ber); -}; + return this.filter.toBer(ber) +} diff --git a/lib/filters/or_filter.js b/lib/filters/or_filter.js index 4218a7f..13a054e 100644 --- a/lib/filters/or_filter.js +++ b/lib/filters/or_filter.js @@ -1,29 +1,27 @@ // Copyright 2011 Mark Cavage, Inc. All rights reserved. -var assert = require('assert'); -var util = require('util'); +var assert = require('assert') +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); -Filter.mixin(OrFilter); -module.exports = OrFilter; - +util.inherits(OrFilter, parents.OrFilter) +Filter.mixin(OrFilter) +module.exports = OrFilter OrFilter.prototype._toBer = function (ber) { - assert.ok(ber); + assert.ok(ber) this.filters.forEach(function (f) { - ber = f.toBer(ber); - }); + ber = f.toBer(ber) + }) - return ber; -}; + return ber +} diff --git a/lib/filters/presence_filter.js b/lib/filters/presence_filter.js index cdd0a3f..3307f63 100644 --- a/lib/filters/presence_filter.js +++ b/lib/filters/presence_filter.js @@ -1,40 +1,36 @@ // Copyright 2011 Mark Cavage, Inc. All rights reserved. -var assert = require('assert'); -var util = require('util'); +var assert = require('assert') +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); -Filter.mixin(PresenceFilter); -module.exports = PresenceFilter; - +util.inherits(PresenceFilter, parents.PresenceFilter) +Filter.mixin(PresenceFilter) +module.exports = PresenceFilter PresenceFilter.prototype.parse = function (ber) { - assert.ok(ber); + assert.ok(ber) this.attribute = - ber.buffer.slice(0, ber.length).toString('utf8').toLowerCase(); + ber.buffer.slice(0, ber.length).toString('utf8').toLowerCase() - ber._offset += ber.length; - - return true; -}; + ber._offset += ber.length + return true +} PresenceFilter.prototype._toBer = function (ber) { - assert.ok(ber); + assert.ok(ber) - for (var i = 0; i < this.attribute.length; i++) - ber.writeByte(this.attribute.charCodeAt(i)); + for (var i = 0; i < this.attribute.length; i++) { ber.writeByte(this.attribute.charCodeAt(i)) } - return ber; -}; + return ber +} diff --git a/lib/filters/substr_filter.js b/lib/filters/substr_filter.js index 04e5a7e..c926db4 100644 --- a/lib/filters/substr_filter.js +++ b/lib/filters/substr_filter.js @@ -1,76 +1,69 @@ // Copyright 2011 Mark Cavage, Inc. All rights reserved. -var assert = require('assert'); -var util = require('util'); +var assert = require('assert') +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); -Filter.mixin(SubstringFilter); -module.exports = SubstringFilter; - +util.inherits(SubstringFilter, parents.SubstringFilter) +Filter.mixin(SubstringFilter) +module.exports = SubstringFilter SubstringFilter.prototype.parse = function (ber) { - assert.ok(ber); + assert.ok(ber) - this.attribute = ber.readString().toLowerCase(); - ber.readSequence(); - var end = ber.offset + ber.length; + this.attribute = ber.readString().toLowerCase() + ber.readSequence() + var end = ber.offset + ber.length while (ber.offset < end) { - var tag = ber.peek(); + var tag = ber.peek() switch (tag) { - case 0x80: // Initial - this.initial = ber.readString(tag); - if (this.attribute === 'objectclass') - this.initial = this.initial.toLowerCase(); - break; - case 0x81: // Any - var anyVal = ber.readString(tag); - if (this.attribute === 'objectclass') - anyVal = anyVal.toLowerCase(); - this.any.push(anyVal); - break; - case 0x82: // Final - this.final = ber.readString(tag); - if (this.attribute === 'objectclass') - this.final = this.final.toLowerCase(); - break; - default: - throw new Error('Invalid substrings filter type: 0x' + tag.toString(16)); + case 0x80: // Initial + this.initial = ber.readString(tag) + if (this.attribute === 'objectclass') { this.initial = this.initial.toLowerCase() } + break + case 0x81: // Any + var anyVal = ber.readString(tag) + if (this.attribute === 'objectclass') { anyVal = anyVal.toLowerCase() } + this.any.push(anyVal) + break + case 0x82: // Final + this.final = ber.readString(tag) + if (this.attribute === 'objectclass') { this.final = this.final.toLowerCase() } + break + default: + throw new Error('Invalid substrings filter type: 0x' + tag.toString(16)) } } - return true; -}; - + return true +} SubstringFilter.prototype._toBer = function (ber) { - assert.ok(ber); + assert.ok(ber) - ber.writeString(this.attribute); - ber.startSequence(); + ber.writeString(this.attribute) + ber.startSequence() - if (this.initial) - ber.writeString(this.initial, 0x80); + if (this.initial) { ber.writeString(this.initial, 0x80) } - if (this.any && this.any.length) + if (this.any && this.any.length) { this.any.forEach(function (s) { - ber.writeString(s, 0x81); - }); + ber.writeString(s, 0x81) + }) + } - if (this.final) - ber.writeString(this.final, 0x82); + if (this.final) { ber.writeString(this.final, 0x82) } - ber.endSequence(); + ber.endSequence() - return ber; -}; + return ber +} diff --git a/lib/index.js b/lib/index.js index 6677522..b8ee53c 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,24 +1,24 @@ // Copyright 2011 Mark Cavage, Inc. All rights reserved. -var Logger = require('bunyan'); +var logger = require('./logger') -var client = require('./client'); -var Attribute = require('./attribute'); -var Change = require('./change'); -var Protocol = require('./protocol'); -var Server = require('./server'); +var client = require('./client') +var Attribute = require('./attribute') +var Change = require('./change') +var Protocol = require('./protocol') +var Server = require('./server') -var assert = require('assert'); -var controls = require('./controls'); -var persistentSearch = require('./persistent_search'); -var dn = require('./dn'); -var errors = require('./errors'); -var filters = require('./filters'); -var messages = require('./messages'); -var url = require('./url'); +var controls = require('./controls') +var persistentSearch = require('./persistent_search') +var dn = require('./dn') +var errors = require('./errors') +var filters = require('./filters') +var messages = require('./messages') +var url = require('./url') +const hasOwnProperty = (target, val) => Object.prototype.hasOwnProperty.call(target, val) -///--- API +/// --- API module.exports = { Client: client.Client, @@ -26,21 +26,15 @@ module.exports = { Server: Server, createServer: function (options) { - if (options === undefined) - options = {}; + if (options === undefined) { options = {} } - if (typeof (options) !== 'object') - throw new TypeError('options (object) required'); + if (typeof (options) !== 'object') { throw new TypeError('options (object) required') } if (!options.log) { - options.log = new Logger({ - name: 'ldapjs', - component: 'client', - stream: process.stderr - }); + options.log = logger } - return new Server(options); + return new Server(options) }, Attribute: Attribute, @@ -59,37 +53,32 @@ module.exports = { url: url, parseURL: url.parse -}; +} +/// --- Export all the childrenz -///--- Export all the childrenz - -var k; +var k for (k in Protocol) { - if (Protocol.hasOwnProperty(k)) - module.exports[k] = Protocol[k]; + if (hasOwnProperty(Protocol, k)) { module.exports[k] = Protocol[k] } } for (k in messages) { - if (messages.hasOwnProperty(k)) - module.exports[k] = messages[k]; + if (hasOwnProperty(messages, k)) { module.exports[k] = messages[k] } } for (k in controls) { - if (controls.hasOwnProperty(k)) - module.exports[k] = controls[k]; + if (hasOwnProperty(controls, k)) { module.exports[k] = controls[k] } } for (k in filters) { - if (filters.hasOwnProperty(k)) { - if (k !== 'parse' && k !== 'parseString') - module.exports[k] = filters[k]; + if (hasOwnProperty(filters, k)) { + if (k !== 'parse' && k !== 'parseString') { module.exports[k] = filters[k] } } } for (k in errors) { - if (errors.hasOwnProperty(k)) { - module.exports[k] = errors[k]; + if (hasOwnProperty(errors, k)) { + module.exports[k] = errors[k] } } diff --git a/lib/logger.js b/lib/logger.js new file mode 100644 index 0000000..ad96ba4 --- /dev/null +++ b/lib/logger.js @@ -0,0 +1,6 @@ +'use strict' + +const logger = Object.create(require('abstract-logging')) +logger.child = function () { return logger } + +module.exports = logger diff --git a/lib/messages/abandon_request.js b/lib/messages/abandon_request.js index 461ce49..2a25aaf 100644 --- a/lib/messages/abandon_request.js +++ b/lib/messages/abandon_request.js @@ -1,90 +1,87 @@ // Copyright 2011 Mark Cavage, Inc. All rights reserved. -var assert = require('assert-plus'); -var util = require('util'); +var assert = require('assert-plus') +var util = require('util') -var LDAPMessage = require('./message'); -var Protocol = require('../protocol'); +var LDAPMessage = require('./message') +var Protocol = require('../protocol') +/// --- API -///--- API +function AbandonRequest (options) { + options = options || {} + assert.object(options) + assert.optionalNumber(options.abandonID) -function AbandonRequest(options) { - options = options || {}; - assert.object(options); - assert.optionalNumber(options.abandonID); + options.protocolOp = Protocol.LDAP_REQ_ABANDON + LDAPMessage.call(this, options) - options.protocolOp = Protocol.LDAP_REQ_ABANDON; - LDAPMessage.call(this, options); - - this.abandonID = options.abandonID || 0; + this.abandonID = options.abandonID || 0 } -util.inherits(AbandonRequest, LDAPMessage); +util.inherits(AbandonRequest, LDAPMessage) Object.defineProperties(AbandonRequest.prototype, { type: { - get: function getType() { return 'AbandonRequest'; }, + get: function getType () { return 'AbandonRequest' }, configurable: false } -}); +}) AbandonRequest.prototype._parse = function (ber, length) { - assert.ok(ber); - assert.ok(length); + assert.ok(ber) + assert.ok(length) // What a PITA - have to replicate ASN.1 integer logic to work around the // way abandon is encoded and the way ldapjs framework handles "normal" // messages - var buf = ber.buffer; - var offset = 0; - var value = 0; + var buf = ber.buffer + var offset = 0 + var value = 0 - var fb = buf[offset++]; - value = fb & 0x7F; + var fb = buf[offset++] + value = fb & 0x7F for (var i = 1; i < length; i++) { - value <<= 8; - value |= (buf[offset++] & 0xff); + value <<= 8 + value |= (buf[offset++] & 0xff) } - if ((fb & 0x80) == 0x80) - value = -value; + if ((fb & 0x80) === 0x80) { value = -value } - ber._offset += length; + ber._offset += length - this.abandonID = value; + this.abandonID = value - return true; -}; + return true +} AbandonRequest.prototype._toBer = function (ber) { - assert.ok(ber); + assert.ok(ber) - var i = this.abandonID; - var sz = 4; + var i = this.abandonID + var sz = 4 while ((((i & 0xff800000) === 0) || ((i & 0xff800000) === 0xff800000)) && (sz > 1)) { - sz--; - i <<= 8; + sz-- + i <<= 8 } - assert.ok(sz <= 4); + assert.ok(sz <= 4) while (sz-- > 0) { - ber.writeByte((i & 0xff000000) >> 24); - i <<= 8; + ber.writeByte((i & 0xff000000) >> 24) + i <<= 8 } - return ber; -}; + return ber +} 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 diff --git a/lib/messages/abandon_response.js b/lib/messages/abandon_response.js index 534c218..7926ab7 100644 --- a/lib/messages/abandon_response.js +++ b/lib/messages/abandon_response.js @@ -1,36 +1,34 @@ // Copyright 2011 Mark Cavage, Inc. All rights reserved. -var assert = require('assert-plus'); -var util = require('util'); +var assert = require('assert-plus') +var util = require('util') -var LDAPMessage = require('./result'); -var Protocol = require('../protocol'); +var LDAPMessage = require('./result') +// var Protocol = require('../protocol') +/// --- API -///--- API +function AbandonResponse (options) { + options = options || {} + assert.object(options) -function AbandonResponse(options) { - options = options || {}; - assert.object(options); - - options.protocolOp = 0; - LDAPMessage.call(this, options); + options.protocolOp = 0 + LDAPMessage.call(this, options) } -util.inherits(AbandonResponse, LDAPMessage); +util.inherits(AbandonResponse, LDAPMessage) Object.defineProperties(AbandonResponse.prototype, { type: { - get: function getType() { return 'AbandonResponse'; }, + get: function getType () { return 'AbandonResponse' }, configurable: false } -}); +}) -AbandonResponse.prototype.end = function (status) {}; +AbandonResponse.prototype.end = function (status) {} AbandonResponse.prototype._json = function (j) { - return j; -}; + return j +} +/// --- Exports -///--- Exports - -module.exports = AbandonResponse; +module.exports = AbandonResponse diff --git a/lib/messages/add_request.js b/lib/messages/add_request.js index e1d4bdd..21eba1a 100644 --- a/lib/messages/add_request.js +++ b/lib/messages/add_request.js @@ -1,130 +1,122 @@ // Copyright 2011 Mark Cavage, Inc. All rights reserved. -var assert = require('assert-plus'); -var util = require('util'); +var assert = require('assert-plus') +var util = require('util') -var LDAPMessage = require('./message'); -var Attribute = require('../attribute'); -var Protocol = require('../protocol'); -var lassert = require('../assert'); +var LDAPMessage = require('./message') +var Attribute = require('../attribute') +var Protocol = require('../protocol') +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 = options || {}; - assert.object(options); - lassert.optionalStringDN(options.entry); - lassert.optionalArrayOfAttribute(options.attributes); + options.protocolOp = Protocol.LDAP_REQ_ADD + LDAPMessage.call(this, options) - options.protocolOp = Protocol.LDAP_REQ_ADD; - LDAPMessage.call(this, options); - - this.entry = options.entry || null; - 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, { type: { - get: function getType() { return 'AddRequest'; }, + get: function getType () { return 'AddRequest' }, configurable: false }, _dn: { - get: function getDN() { return this.entry; }, + get: function getDN () { return this.entry }, configurable: false } -}); +}) 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) { - var a = new Attribute(); - a.parse(ber); - a.type = a.type.toLowerCase(); + var a = new Attribute() + a.parse(ber) + a.type = a.type.toLowerCase() if (a.type === 'objectclass') { - for (var i = 0; i < a.vals.length; i++) - a.vals[i] = a.vals[i].toLowerCase(); + for (var i = 0; i < a.vals.length; i++) { a.vals[i] = a.vals[i].toLowerCase() } } - this.attributes.push(a); + this.attributes.push(a) } - this.attributes.sort(Attribute.compare); - return true; -}; + this.attributes.sort(Attribute.compare) + return true +} AddRequest.prototype._toBer = function (ber) { - assert.ok(ber); + assert.ok(ber) - ber.writeString(this.entry.toString()); - ber.startSequence(); + ber.writeString(this.entry.toString()) + ber.startSequence() this.attributes.forEach(function (a) { - a.toBer(ber); - }); - ber.endSequence(); + a.toBer(ber) + }) + ber.endSequence() - return ber; -}; + return ber +} AddRequest.prototype._json = function (j) { - assert.ok(j); + assert.ok(j) - j.entry = this.entry.toString(); - j.attributes = []; + j.entry = this.entry.toString() + j.attributes = [] this.attributes.forEach(function (a) { - j.attributes.push(a.json); - }); + j.attributes.push(a.json) + }) - return j; -}; + return j +} AddRequest.prototype.indexOf = function (attr) { - if (!attr || typeof (attr) !== 'string') - throw new TypeError('attr (string) required'); + if (!attr || typeof (attr) !== 'string') { throw new TypeError('attr (string) required') } for (var i = 0; i < this.attributes.length; i++) { - if (this.attributes[i].type === attr) - return i; + if (this.attributes[i].type === attr) { return i } } - return -1; -}; + return -1 +} AddRequest.prototype.attributeNames = function () { - var attrs = []; + var attrs = [] - for (var i = 0; i < this.attributes.length; i++) - attrs.push(this.attributes[i].type.toLowerCase()); + for (var i = 0; i < this.attributes.length; i++) { attrs.push(this.attributes[i].type.toLowerCase()) } - return attrs; -}; + return attrs +} AddRequest.prototype.getAttribute = function (name) { - if (!name || typeof (name) !== 'string') - throw new TypeError('attribute name (string) required'); + if (!name || typeof (name) !== 'string') { throw new TypeError('attribute name (string) required') } - name = name.toLowerCase(); + name = name.toLowerCase() for (var i = 0; i < this.attributes.length; i++) { - if (this.attributes[i].type === name) - return this.attributes[i]; + if (this.attributes[i].type === name) { return this.attributes[i] } } - return null; -}; + return null +} AddRequest.prototype.addAttribute = function (attr) { - if (!(attr instanceof Attribute)) - throw new TypeError('attribute (Attribute) required'); + if (!(attr instanceof Attribute)) { throw new TypeError('attribute (Attribute) required') } - return this.attributes.push(attr); -}; + return this.attributes.push(attr) +} /** * Returns a "pure" JS representation of this object. @@ -142,30 +134,26 @@ AddRequest.prototype.addAttribute = function (attr) { * @return {Object} that looks like the above. */ AddRequest.prototype.toObject = function () { - var self = this; + var self = this var obj = { dn: self.entry ? self.entry.toString() : '', attributes: {} - }; + } - if (!this.attributes || !this.attributes.length) - return obj; + if (!this.attributes || !this.attributes.length) { return obj } this.attributes.forEach(function (a) { - if (!obj.attributes[a.type]) - obj.attributes[a.type] = []; + if (!obj.attributes[a.type]) { obj.attributes[a.type] = [] } a.vals.forEach(function (v) { - if (obj.attributes[a.type].indexOf(v) === -1) - obj.attributes[a.type].push(v); - }); - }); + if (obj.attributes[a.type].indexOf(v) === -1) { obj.attributes[a.type].push(v) } + }) + }) - return obj; -}; + return obj +} +/// --- Exports -///--- Exports - -module.exports = AddRequest; +module.exports = AddRequest diff --git a/lib/messages/add_response.js b/lib/messages/add_response.js index 2163552..8520162 100644 --- a/lib/messages/add_response.js +++ b/lib/messages/add_response.js @@ -1,24 +1,22 @@ // Copyright 2011 Mark Cavage, Inc. All rights reserved. -var assert = require('assert-plus'); -var util = require('util'); +var assert = require('assert-plus') +var util = require('util') -var LDAPResult = require('./result'); -var Protocol = require('../protocol'); +var LDAPResult = require('./result') +var Protocol = require('../protocol') +/// --- API -///--- API +function AddResponse (options) { + options = options || {} + assert.object(options) -function AddResponse(options) { - options = options || {}; - assert.object(options); - - options.protocolOp = Protocol.LDAP_REP_ADD; - LDAPResult.call(this, 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 diff --git a/lib/messages/bind_request.js b/lib/messages/bind_request.js index ca99ddf..a55b971 100644 --- a/lib/messages/bind_request.js +++ b/lib/messages/bind_request.js @@ -1,88 +1,84 @@ // Copyright 2011 Mark Cavage, Inc. All rights reserved. -var assert = require('assert-plus'); -var util = require('util'); +var assert = require('assert-plus') +var util = require('util') -var asn1 = require('asn1'); +var asn1 = require('asn1') -var LDAPMessage = require('./message'); -var Protocol = require('../protocol'); +var LDAPMessage = require('./message') +var Protocol = require('../protocol') +/// --- Globals -///--- Globals +var Ber = asn1.Ber +var LDAP_BIND_SIMPLE = 'simple' +// var LDAP_BIND_SASL = 'sasl' -var Ber = asn1.Ber; -var LDAP_BIND_SIMPLE = 'simple'; -var LDAP_BIND_SASL = 'sasl'; +/// --- API +function BindRequest (options) { + options = options || {} + assert.object(options) -///--- API + options.protocolOp = Protocol.LDAP_REQ_BIND + LDAPMessage.call(this, options) -function BindRequest(options) { - options = options || {}; - assert.object(options); - - options.protocolOp = Protocol.LDAP_REQ_BIND; - LDAPMessage.call(this, options); - - this.version = options.version || 0x03; - this.name = options.name || null; - this.authentication = options.authentication || LDAP_BIND_SIMPLE; - this.credentials = options.credentials || ''; + 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, { type: { - get: function getType() { return 'BindRequest'; }, + get: function getType () { return 'BindRequest' }, configurable: false }, _dn: { - get: function getDN() { return this.name; }, + get: function getDN () { return this.name }, configurable: false } -}); +}) BindRequest.prototype._parse = function (ber) { - assert.ok(ber); + assert.ok(ber) - this.version = ber.readInt(); - this.name = ber.readString(); + this.version = ber.readInt() + this.name = ber.readString() - var t = ber.peek(); + var t = ber.peek() // TODO add support for SASL et al - if (t !== Ber.Context) - throw new Error('authentication 0x' + t.toString(16) + ' not supported'); + if (t !== Ber.Context) { throw new Error('authentication 0x' + t.toString(16) + ' not supported') } - this.authentication = LDAP_BIND_SIMPLE; - this.credentials = ber.readString(Ber.Context); + this.authentication = LDAP_BIND_SIMPLE + this.credentials = ber.readString(Ber.Context) - return true; -}; + return true +} BindRequest.prototype._toBer = function (ber) { - assert.ok(ber); + assert.ok(ber) - ber.writeInt(this.version); - ber.writeString((this.name || '').toString()); + ber.writeInt(this.version) + ber.writeString((this.name || '').toString()) // 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) { - assert.ok(j); + assert.ok(j) - j.version = this.version; - j.name = this.name; - j.authenticationType = this.authentication; - j.credentials = this.credentials; + j.version = this.version + j.name = this.name + j.authenticationType = this.authentication + j.credentials = this.credentials - return j; -}; + return j +} +/// --- Exports -///--- Exports - -module.exports = BindRequest; +module.exports = BindRequest diff --git a/lib/messages/bind_response.js b/lib/messages/bind_response.js index 8542613..2b7813f 100644 --- a/lib/messages/bind_response.js +++ b/lib/messages/bind_response.js @@ -1,24 +1,22 @@ // Copyright 2011 Mark Cavage, Inc. All rights reserved. -var assert = require('assert-plus'); -var util = require('util'); +var assert = require('assert-plus') +var util = require('util') -var LDAPResult = require('./result'); -var Protocol = require('../protocol'); +var LDAPResult = require('./result') +var Protocol = require('../protocol') +/// --- API -///--- API +function BindResponse (options) { + options = options || {} + assert.object(options) -function BindResponse(options) { - options = options || {}; - assert.object(options); - - options.protocolOp = Protocol.LDAP_REP_BIND; - LDAPResult.call(this, 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 diff --git a/lib/messages/compare_request.js b/lib/messages/compare_request.js index 9b7e2fe..5029546 100644 --- a/lib/messages/compare_request.js +++ b/lib/messages/compare_request.js @@ -1,76 +1,74 @@ // Copyright 2011 Mark Cavage, Inc. All rights reserved. -var assert = require('assert-plus'); -var util = require('util'); +var assert = require('assert-plus') +var util = require('util') -var LDAPMessage = require('./message'); -var Protocol = require('../protocol'); -var lassert = require('../assert'); +var LDAPMessage = require('./message') +var Protocol = require('../protocol') +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 = options || {}; - assert.object(options); - assert.optionalString(options.attribute); - assert.optionalString(options.value); - lassert.optionalStringDN(options.entry); + options.protocolOp = Protocol.LDAP_REQ_COMPARE + LDAPMessage.call(this, options) - options.protocolOp = Protocol.LDAP_REQ_COMPARE; - LDAPMessage.call(this, options); - - this.entry = options.entry || null; - 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, { type: { - get: function getType() { return 'CompareRequest'; }, + get: function getType () { return 'CompareRequest' }, configurable: false }, _dn: { - get: function getDN() { return this.entry; }, + get: function getDN () { return this.entry }, configurable: false } -}); +}) CompareRequest.prototype._parse = function (ber) { - assert.ok(ber); + assert.ok(ber) - this.entry = ber.readString(); + this.entry = ber.readString() - ber.readSequence(); - this.attribute = ber.readString().toLowerCase(); - this.value = ber.readString(); + ber.readSequence() + this.attribute = ber.readString().toLowerCase() + this.value = ber.readString() - return true; -}; + return true +} CompareRequest.prototype._toBer = function (ber) { - assert.ok(ber); + assert.ok(ber) - ber.writeString(this.entry.toString()); - ber.startSequence(); - ber.writeString(this.attribute); - ber.writeString(this.value); - ber.endSequence(); + ber.writeString(this.entry.toString()) + ber.startSequence() + ber.writeString(this.attribute) + ber.writeString(this.value) + ber.endSequence() - return ber; -}; + return ber +} CompareRequest.prototype._json = function (j) { - assert.ok(j); + assert.ok(j) - j.entry = this.entry.toString(); - j.attribute = this.attribute; - j.value = this.value; + j.entry = this.entry.toString() + j.attribute = this.attribute + j.value = this.value - return j; -}; + return j +} +/// --- Exports -///--- Exports - -module.exports = CompareRequest; +module.exports = CompareRequest diff --git a/lib/messages/compare_response.js b/lib/messages/compare_response.js index 4f949d4..ee35bfa 100644 --- a/lib/messages/compare_response.js +++ b/lib/messages/compare_response.js @@ -1,36 +1,33 @@ // Copyright 2011 Mark Cavage, Inc. All rights reserved. -var assert = require('assert-plus'); -var util = require('util'); +var assert = require('assert-plus') +var util = require('util') -var LDAPResult = require('./result'); -var Protocol = require('../protocol'); +var LDAPResult = require('./result') +var Protocol = require('../protocol') +/// --- API -///--- API +function CompareResponse (options) { + options = options || {} + assert.object(options) -function CompareResponse(options) { - options = options || {}; - assert.object(options); - - options.protocolOp = Protocol.LDAP_REP_COMPARE; - LDAPResult.call(this, options); + options.protocolOp = Protocol.LDAP_REP_COMPARE + LDAPResult.call(this, options) } -util.inherits(CompareResponse, LDAPResult); +util.inherits(CompareResponse, LDAPResult) CompareResponse.prototype.end = function (matches) { - var status = 0x06; + var status = 0x06 if (typeof (matches) === 'boolean') { - if (!matches) - status = 0x05; // Compare false + if (!matches) { status = 0x05 } // Compare false } 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 diff --git a/lib/messages/del_request.js b/lib/messages/del_request.js index dc66f55..4fb9028 100644 --- a/lib/messages/del_request.js +++ b/lib/messages/del_request.js @@ -1,65 +1,62 @@ // Copyright 2011 Mark Cavage, Inc. All rights reserved. -var assert = require('assert-plus'); -var util = require('util'); +var assert = require('assert-plus') +var util = require('util') -var LDAPMessage = require('./message'); -var Protocol = require('../protocol'); -var lassert = require('../assert'); +var LDAPMessage = require('./message') +var Protocol = require('../protocol') +var lassert = require('../assert') +/// --- API -///--- API +function DeleteRequest (options) { + options = options || {} + assert.object(options) + lassert.optionalStringDN(options.entry) -function DeleteRequest(options) { - options = options || {}; - assert.object(options); - lassert.optionalStringDN(options.entry); + options.protocolOp = Protocol.LDAP_REQ_DELETE + LDAPMessage.call(this, options) - options.protocolOp = Protocol.LDAP_REQ_DELETE; - LDAPMessage.call(this, options); - - this.entry = options.entry || null; + this.entry = options.entry || null } -util.inherits(DeleteRequest, LDAPMessage); +util.inherits(DeleteRequest, LDAPMessage) Object.defineProperties(DeleteRequest.prototype, { type: { - get: function getType() { return 'DeleteRequest'; }, + get: function getType () { return 'DeleteRequest' }, configurable: false }, _dn: { - get: function getDN() { return this.entry; }, + get: function getDN () { return this.entry }, configurable: false } -}); +}) DeleteRequest.prototype._parse = function (ber, length) { - assert.ok(ber); + assert.ok(ber) - this.entry = ber.buffer.slice(0, length).toString('utf8'); - ber._offset += ber.length; + this.entry = ber.buffer.slice(0, length).toString('utf8') + ber._offset += ber.length - return true; -}; + return true +} DeleteRequest.prototype._toBer = function (ber) { - assert.ok(ber); + assert.ok(ber) - var buf = new Buffer(this.entry.toString()); - for (var i = 0; i < buf.length; i++) - ber.writeByte(buf[i]); + var buf = Buffer.from(this.entry.toString()) + for (var i = 0; i < buf.length; i++) { ber.writeByte(buf[i]) } - return ber; -}; + return ber +} 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 diff --git a/lib/messages/del_response.js b/lib/messages/del_response.js index f42322f..7e8c953 100644 --- a/lib/messages/del_response.js +++ b/lib/messages/del_response.js @@ -1,24 +1,22 @@ // Copyright 2011 Mark Cavage, Inc. All rights reserved. -var assert = require('assert-plus'); -var util = require('util'); +var assert = require('assert-plus') +var util = require('util') -var LDAPResult = require('./result'); -var Protocol = require('../protocol'); +var LDAPResult = require('./result') +var Protocol = require('../protocol') +/// --- API -///--- API +function DeleteResponse (options) { + options = options || {} + assert.object(options) -function DeleteResponse(options) { - options = options || {}; - assert.object(options); - - options.protocolOp = Protocol.LDAP_REP_DELETE; - LDAPResult.call(this, 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 diff --git a/lib/messages/ext_request.js b/lib/messages/ext_request.js index 35ac072..b719825 100644 --- a/lib/messages/ext_request.js +++ b/lib/messages/ext_request.js @@ -1,98 +1,116 @@ // Copyright 2011 Mark Cavage, Inc. All rights reserved. -var assert = require('assert-plus'); -var util = require('util'); +var assert = require('assert-plus') +var util = require('util') -var LDAPMessage = require('./message'); -var Protocol = require('../protocol'); +var LDAPMessage = require('./message') +var Protocol = require('../protocol') +/// --- API -///--- API - -function ExtendedRequest(options) { - options = options || {}; - assert.object(options); - assert.optionalString(options.requestName); +function ExtendedRequest (options) { + options = options || {} + assert.object(options) + assert.optionalString(options.requestName) if (options.requestValue && !(Buffer.isBuffer(options.requestValue) || typeof (options.requestValue) === 'string')) { - throw new TypeError('options.requestValue must be a buffer or a string'); + throw new TypeError('options.requestValue must be a buffer or a string') } - options.protocolOp = Protocol.LDAP_REQ_EXTENSION; - LDAPMessage.call(this, options); + options.protocolOp = Protocol.LDAP_REQ_EXTENSION + LDAPMessage.call(this, options) - this.requestName = options.requestName || ''; - this.requestValue = options.requestValue; + this.requestName = options.requestName || '' + 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, { type: { - get: function getType() { return 'ExtendedRequest'; }, + get: function getType () { return 'ExtendedRequest' }, configurable: false }, _dn: { - get: function getDN() { return this.requestName; }, + get: function getDN () { return this.requestName }, configurable: false }, name: { - get: function getName() { return this.requestName; }, - set: function setName(val) { - assert.string(val); - this.requestName = val; + get: function getName () { return this.requestName }, + set: function setName (val) { + assert.string(val) + this.requestName = val }, configurable: false }, value: { - get: function getValue() { return this.requestValue; }, - set: function setValue(val) { - if (!(Buffer.isBuffer(val) || typeof (val) === 'string')) - throw new TypeError('value must be a buffer or a string'); + get: function getValue () { return this.requestValue }, + set: function setValue (val) { + if (!(Buffer.isBuffer(val) || typeof (val) === '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 } -}); +}) ExtendedRequest.prototype._parse = function (ber) { - assert.ok(ber); + assert.ok(ber) - this.requestName = ber.readString(0x80); - if (ber.peek() === 0x81) - try { - this.requestValue = ber.readString(0x81); - } catch (e) { - this.requestValue = ber.readBuffer(0x81); - } - - return true; -}; - -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); + this.requestName = ber.readString(0x80) + if (ber.peek() === 0x81) { + this.requestValueBuffer = ber.readString(0x81, true) + this.requestValue = this.requestValueBuffer.toString('utf8') } - 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) { - assert.ok(j); + assert.ok(j) - j.requestName = this.requestName; - j.requestValue = (Buffer.isBuffer(this.requestValue)) ? - this.requestValue.toString('hex') : this.requestValue; + j.requestName = this.requestName + j.requestValue = (Buffer.isBuffer(this.requestValue)) + ? this.requestValue.toString('hex') : this.requestValue - return j; -}; + return j +} +/// --- Exports -///--- Exports - -module.exports = ExtendedRequest; +module.exports = ExtendedRequest diff --git a/lib/messages/ext_response.js b/lib/messages/ext_response.js index 4e72d0a..9ecb9bb 100644 --- a/lib/messages/ext_response.js +++ b/lib/messages/ext_response.js @@ -1,94 +1,86 @@ // Copyright 2011 Mark Cavage, Inc. All rights reserved. -var assert = require('assert-plus'); -var util = require('util'); +var assert = require('assert-plus') +var util = require('util') -var LDAPResult = require('./result'); -var Protocol = require('../protocol'); +var LDAPResult = require('./result') +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) { - options = options || {}; - assert.object(options); - assert.optionalString(options.responseName); - assert.optionalString(options.responsevalue); + this.responseName = options.responseName || undefined + this.responseValue = options.responseValue || undefined - this.responseName = options.responseName || undefined; - this.responseValue = options.responseValue || undefined; - - options.protocolOp = Protocol.LDAP_REP_EXTENSION; - 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, { type: { - get: function getType() { return 'ExtendedResponse'; }, + get: function getType () { return 'ExtendedResponse' }, configurable: false }, _dn: { - get: function getDN() { return this.responseName; }, + get: function getDN () { return this.responseName }, configurable: false }, name: { - get: function getName() { return this.responseName; }, - set: function setName(val) { - assert.string(val); - this.responseName = val; + get: function getName () { return this.responseName }, + set: function setName (val) { + assert.string(val) + this.responseName = val }, configurable: false }, value: { - get: function getValue() { return this.responseValue; }, + get: function getValue () { return this.responseValue }, set: function (val) { - assert.string(val); - this.responseValue = val; + assert.string(val) + this.responseValue = val }, configurable: false } -}); +}) ExtendedResponse.prototype._parse = function (ber) { - assert.ok(ber); + assert.ok(ber) - if (!LDAPResult.prototype._parse.call(this, ber)) - return false; + if (!LDAPResult.prototype._parse.call(this, ber)) { return false } - if (ber.peek() === 0x8a) - this.responseName = ber.readString(0x8a); - if (ber.peek() === 0x8b) - this.responseValue = ber.readString(0x8b); + if (ber.peek() === 0x8a) { this.responseName = ber.readString(0x8a) } + if (ber.peek() === 0x8b) { this.responseValue = ber.readString(0x8b) } - return true; -}; + return true +} ExtendedResponse.prototype._toBer = function (ber) { - assert.ok(ber); + assert.ok(ber) - if (!LDAPResult.prototype._toBer.call(this, ber)) - return false; + if (!LDAPResult.prototype._toBer.call(this, ber)) { return false } - if (this.responseName) - ber.writeString(this.responseName, 0x8a); - if (this.responseValue) - ber.writeString(this.responseValue, 0x8b); + if (this.responseName) { ber.writeString(this.responseName, 0x8a) } + if (this.responseValue) { ber.writeString(this.responseValue, 0x8b) } - return ber; -}; + return ber +} 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.responseValue = this.responseValue; + j.responseName = this.responseName + j.responseValue = this.responseValue - return j; -}; + return j +} +/// --- Exports -///--- Exports - -module.exports = ExtendedResponse; +module.exports = ExtendedResponse diff --git a/lib/messages/index.js b/lib/messages/index.js index 3586377..59b562d 100644 --- a/lib/messages/index.js +++ b/lib/messages/index.js @@ -1,34 +1,33 @@ // Copyright 2011 Mark Cavage, Inc. All rights reserved. -var LDAPMessage = require('./message'); -var LDAPResult = require('./result'); -var Parser = require('./parser'); +var LDAPMessage = require('./message') +var LDAPResult = require('./result') +var Parser = require('./parser') -var AbandonRequest = require('./abandon_request'); -var AbandonResponse = require('./abandon_response'); -var AddRequest = require('./add_request'); -var AddResponse = require('./add_response'); -var BindRequest = require('./bind_request'); -var BindResponse = require('./bind_response'); -var CompareRequest = require('./compare_request'); -var CompareResponse = require('./compare_response'); -var DeleteRequest = require('./del_request'); -var DeleteResponse = require('./del_response'); -var ExtendedRequest = require('./ext_request'); -var ExtendedResponse = require('./ext_response'); -var ModifyRequest = require('./modify_request'); -var ModifyResponse = require('./modify_response'); -var ModifyDNRequest = require('./moddn_request'); -var ModifyDNResponse = require('./moddn_response'); -var SearchRequest = require('./search_request'); -var SearchEntry = require('./search_entry'); -var SearchReference = require('./search_reference'); -var SearchResponse = require('./search_response'); -var UnbindRequest = require('./unbind_request'); -var UnbindResponse = require('./unbind_response'); +var AbandonRequest = require('./abandon_request') +var AbandonResponse = require('./abandon_response') +var AddRequest = require('./add_request') +var AddResponse = require('./add_response') +var BindRequest = require('./bind_request') +var BindResponse = require('./bind_response') +var CompareRequest = require('./compare_request') +var CompareResponse = require('./compare_response') +var DeleteRequest = require('./del_request') +var DeleteResponse = require('./del_response') +var ExtendedRequest = require('./ext_request') +var ExtendedResponse = require('./ext_response') +var ModifyRequest = require('./modify_request') +var ModifyResponse = require('./modify_response') +var ModifyDNRequest = require('./moddn_request') +var ModifyDNResponse = require('./moddn_response') +var SearchRequest = require('./search_request') +var SearchEntry = require('./search_entry') +var SearchReference = require('./search_reference') +var SearchResponse = require('./search_response') +var UnbindRequest = require('./unbind_request') +var UnbindResponse = require('./unbind_response') - -///--- API +/// --- API module.exports = { @@ -59,4 +58,4 @@ module.exports = { UnbindRequest: UnbindRequest, UnbindResponse: UnbindResponse -}; +} diff --git a/lib/messages/message.js b/lib/messages/message.js index aa8ce5e..06790ef 100644 --- a/lib/messages/message.js +++ b/lib/messages/message.js @@ -1,50 +1,48 @@ // Copyright 2011 Mark Cavage, Inc. All rights reserved. -var assert = require('assert-plus'); -var util = require('util'); +var assert = require('assert-plus') +var util = require('util') -var asn1 = require('asn1'); +var asn1 = require('asn1') -var Control = require('../controls').Control; -var Protocol = require('../protocol'); +var logger = require('../logger') +// var Control = require('../controls').Control +// var Protocol = require('../protocol') +/// --- Globals -///--- Globals - -var Ber = asn1.Ber; -var BerReader = asn1.BerReader; -var BerWriter = asn1.BerWriter; -var getControl = require('../controls').getControl; - - -///--- API +// var Ber = asn1.Ber +// var BerReader = asn1.BerReader +var BerWriter = asn1.BerWriter +var getControl = require('../controls').getControl +/// --- API /** * LDAPMessage structure. * * @param {Object} options stuff. */ -function LDAPMessage(options) { - assert.object(options); +function LDAPMessage (options) { + assert.object(options) - this.messageID = options.messageID || 0; - this.protocolOp = options.protocolOp || undefined; - this.controls = options.controls ? options.controls.slice(0) : []; + this.messageID = options.messageID || 0 + this.protocolOp = options.protocolOp || undefined + this.controls = options.controls ? options.controls.slice(0) : [] - this.log = options.log; + this.log = options.log || logger } Object.defineProperties(LDAPMessage.prototype, { id: { - get: function getId() { return this.messageID; }, + get: function getId () { return this.messageID }, configurable: false }, dn: { - get: function getDN() { return this._dn || ''; }, + get: function getDN () { return this._dn || '' }, configurable: false }, type: { - get: function getType() { return 'LDAPMessage'; }, + get: function getType () { return 'LDAPMessage' }, configurable: false }, json: { @@ -52,66 +50,61 @@ Object.defineProperties(LDAPMessage.prototype, { var out = this._json({ messageID: this.messageID, protocolOp: this.type - }); - out.controls = this.controls; - return out; + }) + out.controls = this.controls + return out }, configurable: false } -}); +}) LDAPMessage.prototype.toString = function () { - return JSON.stringify(this.json); -}; + return JSON.stringify(this.json) +} LDAPMessage.prototype.parse = function (ber) { - assert.ok(ber); + assert.ok(ber) - if (this.log.trace()) - this.log.trace('parse: data=%s', util.inspect(ber.buffer)); + if (this.log.trace()) { this.log.trace('parse: data=%s', util.inspect(ber.buffer)) } // Delegate off to the specific type to parse - this._parse(ber, ber.length); + this._parse(ber, ber.length) // Look for controls if (ber.peek() === 0xa0) { - ber.readSequence(); - var end = ber.offset + ber.length; + ber.readSequence() + var end = ber.offset + ber.length while (ber.offset < end) { - var c = getControl(ber); - if (c) - this.controls.push(c); + var c = getControl(ber) + if (c) { this.controls.push(c) } } } - if (this.log.trace()) - this.log.trace('Parsing done: %j', this.json); - return true; -}; + if (this.log.trace()) { this.log.trace('Parsing done: %j', this.json) } + return true +} LDAPMessage.prototype.toBer = function () { - var writer = new BerWriter(); - writer.startSequence(); - writer.writeInt(this.messageID); + var writer = new BerWriter() + writer.startSequence() + writer.writeInt(this.messageID) - writer.startSequence(this.protocolOp); - if (this._toBer) - writer = this._toBer(writer); - writer.endSequence(); + writer.startSequence(this.protocolOp) + if (this._toBer) { writer = this._toBer(writer) } + writer.endSequence() if (this.controls && this.controls.length) { - writer.startSequence(0xa0); + writer.startSequence(0xa0) this.controls.forEach(function (c) { - c.toBer(writer); - }); - writer.endSequence(); + c.toBer(writer) + }) + writer.endSequence() } - writer.endSequence(); - return writer.buffer; -}; + writer.endSequence() + return writer.buffer +} +/// --- Exports -///--- Exports - -module.exports = LDAPMessage; +module.exports = LDAPMessage diff --git a/lib/messages/moddn_request.js b/lib/messages/moddn_request.js index 6a63156..3fafdff 100644 --- a/lib/messages/moddn_request.js +++ b/lib/messages/moddn_request.js @@ -1,88 +1,85 @@ // Copyright 2011 Mark Cavage, Inc. All rights reserved. -var assert = require('assert-plus'); -var util = require('util'); +var assert = require('assert-plus') +var util = require('util') -var LDAPMessage = require('./message'); -var Protocol = require('../protocol'); -var dn = require('../dn'); -var lassert = require('../assert'); +var LDAPMessage = require('./message') +var Protocol = require('../protocol') +var dn = require('../dn') +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 = options || {}; - assert.object(options); - assert.optionalBool(options.deleteOldRdn); - lassert.optionalStringDN(options.entry); - lassert.optionalDN(options.newRdn); - lassert.optionalDN(options.newSuperior); + options.protocolOp = Protocol.LDAP_REQ_MODRDN + LDAPMessage.call(this, options) - options.protocolOp = Protocol.LDAP_REQ_MODRDN; - LDAPMessage.call(this, options); - - this.entry = options.entry || null; - this.newRdn = options.newRdn || null; - this.deleteOldRdn = options.deleteOldRdn || true; - this.newSuperior = options.newSuperior || null; + this.entry = options.entry || 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, { type: { - get: function getType() { return 'ModifyDNRequest'; }, + get: function getType () { return 'ModifyDNRequest' }, configurable: false }, _dn: { - get: function getDN() { return this.entry; }, + get: function getDN () { return this.entry }, configurable: false } -}); +}) ModifyDNRequest.prototype._parse = function (ber) { - assert.ok(ber); + assert.ok(ber) - this.entry = ber.readString(); - this.newRdn = dn.parse(ber.readString()); - this.deleteOldRdn = ber.readBoolean(); - if (ber.peek() === 0x80) - this.newSuperior = dn.parse(ber.readString(0x80)); + this.entry = ber.readString() + this.newRdn = dn.parse(ber.readString()) + this.deleteOldRdn = ber.readBoolean() + if (ber.peek() === 0x80) { this.newSuperior = dn.parse(ber.readString(0x80)) } - return true; -}; + return true +} ModifyDNRequest.prototype._toBer = function (ber) { - //assert.ok(ber); + // assert.ok(ber); - ber.writeString(this.entry.toString()); - ber.writeString(this.newRdn.toString()); - ber.writeBoolean(this.deleteOldRdn); + ber.writeString(this.entry.toString()) + ber.writeString(this.newRdn.toString()) + ber.writeBoolean(this.deleteOldRdn) if (this.newSuperior) { - var s = this.newSuperior.toString(); - var len = Buffer.byteLength(s); + var s = this.newSuperior.toString() + var len = Buffer.byteLength(s) - ber.writeByte(0x80); // MODIFY_DN_REQUEST_NEW_SUPERIOR_TAG - ber.writeByte(len); - ber._ensure(len); - ber._buf.write(s, ber._offset); - ber._offset += len; + ber.writeByte(0x80) // MODIFY_DN_REQUEST_NEW_SUPERIOR_TAG + ber.writeByte(len) + ber._ensure(len) + ber._buf.write(s, ber._offset) + ber._offset += len } - return ber; -}; + return ber +} ModifyDNRequest.prototype._json = function (j) { - assert.ok(j); + assert.ok(j) - j.entry = this.entry.toString(); - j.newRdn = this.newRdn.toString(); - j.deleteOldRdn = this.deleteOldRdn; - j.newSuperior = this.newSuperior ? this.newSuperior.toString() : ''; + j.entry = this.entry.toString() + j.newRdn = this.newRdn.toString() + j.deleteOldRdn = this.deleteOldRdn + j.newSuperior = this.newSuperior ? this.newSuperior.toString() : '' - return j; -}; + return j +} +/// --- Exports -///--- Exports - -module.exports = ModifyDNRequest; +module.exports = ModifyDNRequest diff --git a/lib/messages/moddn_response.js b/lib/messages/moddn_response.js index 57f245f..13cd25f 100644 --- a/lib/messages/moddn_response.js +++ b/lib/messages/moddn_response.js @@ -1,24 +1,22 @@ // Copyright 2011 Mark Cavage, Inc. All rights reserved. -var assert = require('assert-plus'); -var util = require('util'); +var assert = require('assert-plus') +var util = require('util') -var LDAPResult = require('./result'); -var Protocol = require('../protocol'); +var LDAPResult = require('./result') +var Protocol = require('../protocol') +/// --- API -///--- API +function ModifyDNResponse (options) { + options = options || {} + assert.object(options) -function ModifyDNResponse(options) { - options = options || {}; - assert.object(options); - - options.protocolOp = Protocol.LDAP_REP_MODRDN; - LDAPResult.call(this, 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 diff --git a/lib/messages/modify_request.js b/lib/messages/modify_request.js index 38f70b3..b4bb4d0 100644 --- a/lib/messages/modify_request.js +++ b/lib/messages/modify_request.js @@ -1,85 +1,83 @@ // Copyright 2011 Mark Cavage, Inc. All rights reserved. -var assert = require('assert-plus'); -var util = require('util'); +var assert = require('assert-plus') +var util = require('util') -var LDAPMessage = require('./message'); -var Change = require('../change'); -var Protocol = require('../protocol'); -var lassert = require('../assert'); +var LDAPMessage = require('./message') +var Change = require('../change') +var Protocol = require('../protocol') +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 = options || {}; - assert.object(options); - lassert.optionalStringDN(options.object); - lassert.optionalArrayOfAttribute(options.attributes); + options.protocolOp = Protocol.LDAP_REQ_MODIFY + LDAPMessage.call(this, options) - options.protocolOp = Protocol.LDAP_REQ_MODIFY; - LDAPMessage.call(this, options); - - this.object = options.object || null; - 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, { type: { - get: function getType() { return 'ModifyRequest'; }, + get: function getType () { return 'ModifyRequest' }, configurable: false }, _dn: { - get: function getDN() { return this.object; }, + get: function getDN () { return this.object }, configurable: false } -}); +}) ModifyRequest.prototype._parse = function (ber) { - assert.ok(ber); + assert.ok(ber) - this.object = ber.readString(); + this.object = ber.readString() - ber.readSequence(); - var end = ber.offset + ber.length; + ber.readSequence() + var end = ber.offset + ber.length while (ber.offset < end) { - var c = new Change(); - c.parse(ber); - c.modification.type = c.modification.type.toLowerCase(); - this.changes.push(c); + var c = new Change() + c.parse(ber) + c.modification.type = c.modification.type.toLowerCase() + this.changes.push(c) } - this.changes.sort(Change.compare); - return true; -}; + this.changes.sort(Change.compare) + return true +} ModifyRequest.prototype._toBer = function (ber) { - assert.ok(ber); + assert.ok(ber) - ber.writeString(this.object.toString()); - ber.startSequence(); + ber.writeString(this.object.toString()) + ber.startSequence() this.changes.forEach(function (c) { - c.toBer(ber); - }); - ber.endSequence(); + c.toBer(ber) + }) + ber.endSequence() - return ber; -}; + return ber +} ModifyRequest.prototype._json = function (j) { - assert.ok(j); + assert.ok(j) - j.object = this.object; - j.changes = []; + j.object = this.object + j.changes = [] 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 diff --git a/lib/messages/modify_response.js b/lib/messages/modify_response.js index 5e568fc..9822efc 100644 --- a/lib/messages/modify_response.js +++ b/lib/messages/modify_response.js @@ -1,24 +1,22 @@ // Copyright 2011 Mark Cavage, Inc. All rights reserved. -var assert = require('assert-plus'); -var util = require('util'); +var assert = require('assert-plus') +var util = require('util') -var LDAPResult = require('./result'); -var Protocol = require('../protocol'); +var LDAPResult = require('./result') +var Protocol = require('../protocol') +/// --- API -///--- API +function ModifyResponse (options) { + options = options || {} + assert.object(options) -function ModifyResponse(options) { - options = options || {}; - assert.object(options); - - options.protocolOp = Protocol.LDAP_REP_MODIFY; - LDAPResult.call(this, 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 diff --git a/lib/messages/parser.js b/lib/messages/parser.js index 60de51e..89b4f83 100644 --- a/lib/messages/parser.js +++ b/lib/messages/parser.js @@ -1,228 +1,221 @@ // Copyright 2011 Mark Cavage, Inc. All rights reserved. -var EventEmitter = require('events').EventEmitter; -var util = require('util'); +var EventEmitter = require('events').EventEmitter +var util = require('util') -var assert = require('assert-plus'); -var asn1 = require('asn1'); -var VError = require('verror').VError; +var assert = require('assert-plus') +var asn1 = require('asn1') +// var VError = require('verror').VError +var logger = require('../logger') -var AbandonRequest = require('./abandon_request'); -var AddRequest = require('./add_request'); -var AddResponse = require('./add_response'); -var BindRequest = require('./bind_request'); -var BindResponse = require('./bind_response'); -var CompareRequest = require('./compare_request'); -var CompareResponse = require('./compare_response'); -var DeleteRequest = require('./del_request'); -var DeleteResponse = require('./del_response'); -var ExtendedRequest = require('./ext_request'); -var ExtendedResponse = require('./ext_response'); -var ModifyRequest = require('./modify_request'); -var ModifyResponse = require('./modify_response'); -var ModifyDNRequest = require('./moddn_request'); -var ModifyDNResponse = require('./moddn_response'); -var SearchRequest = require('./search_request'); -var SearchEntry = require('./search_entry'); -var SearchReference = require('./search_reference'); -var SearchResponse = require('./search_response'); -var UnbindRequest = require('./unbind_request'); -var UnbindResponse = require('./unbind_response'); +var AbandonRequest = require('./abandon_request') +var AddRequest = require('./add_request') +var AddResponse = require('./add_response') +var BindRequest = require('./bind_request') +var BindResponse = require('./bind_response') +var CompareRequest = require('./compare_request') +var CompareResponse = require('./compare_response') +var DeleteRequest = require('./del_request') +var DeleteResponse = require('./del_response') +var ExtendedRequest = require('./ext_request') +var ExtendedResponse = require('./ext_response') +var ModifyRequest = require('./modify_request') +var ModifyResponse = require('./modify_response') +var ModifyDNRequest = require('./moddn_request') +var ModifyDNResponse = require('./moddn_response') +var SearchRequest = require('./search_request') +var SearchEntry = require('./search_entry') +var SearchReference = require('./search_reference') +var SearchResponse = require('./search_response') +var UnbindRequest = require('./unbind_request') +// var UnbindResponse = require('./unbind_response') -var LDAPResult = require('./result'); -var Message = require('./message'); +var LDAPResult = require('./result') +// 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; -var BerReader = asn1.BerReader; +/// --- API +function Parser (options = {}) { + assert.object(options) -///--- API + EventEmitter.call(this) -function Parser(options) { - assert.object(options); - assert.object(options.log); - - EventEmitter.call(this); - - this.buffer = null; - this.log = options.log; + this.buffer = null + this.log = options.log || logger } -util.inherits(Parser, EventEmitter); +util.inherits(Parser, EventEmitter) Parser.prototype.write = function (data) { - if (!data || !Buffer.isBuffer(data)) - throw new TypeError('data (buffer) required'); + if (!data || !Buffer.isBuffer(data)) { throw new TypeError('data (buffer) required') } - var nextMessage = null; - var self = this; + var nextMessage = null + var self = this - function end() { - if (nextMessage) - return self.write(nextMessage); + function end () { + if (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 { - foundSeq = ber.readSequence(); + foundSeq = ber.readSequence() } catch (e) { - this.emit('error', e); + this.emit('error', e) } if (!foundSeq || ber.remain < ber.length) { // ENOTENOUGH - return false; + return false } else if (ber.remain > ber.length) { // ETOOMUCH // This is sort of ugly, but allows us to make miminal copies - nextMessage = self.buffer.slice(ber.offset + ber.length); - ber._size = ber.offset + ber.length; - assert.equal(ber.remain, ber.length); + nextMessage = self.buffer.slice(ber.offset + ber.length) + ber._size = ber.offset + ber.length + assert.equal(ber.remain, ber.length) } // If we're here, ber holds the message, and nextMessage is temporarily // pointing at the next sequence of data (if it exists) - self.buffer = null; + self.buffer = null - var message; + var message try { // Bail here if peer isn't speaking protocol at all - message = this.getMessage(ber); + message = this.getMessage(ber) if (!message) { - return end(); + return end() } - message.parse(ber); + message.parse(ber) } catch (e) { - this.emit('error', e, message); - return false; + this.emit('error', e, message) + return false } - this.emit('message', message); - return end(); -}; + this.emit('message', message) + return end() +} Parser.prototype.getMessage = function (ber) { - assert.ok(ber); + assert.ok(ber) - var self = this; + var self = this - var messageID = ber.readInt(); - var type = ber.readSequence(); + var messageID = ber.readInt() + var type = ber.readSequence() - var Message; + var Message switch (type) { + case Protocol.LDAP_REQ_ABANDON: + Message = AbandonRequest + break - case Protocol.LDAP_REQ_ABANDON: - Message = AbandonRequest; - break; + case Protocol.LDAP_REQ_ADD: + Message = AddRequest + break - case Protocol.LDAP_REQ_ADD: - Message = AddRequest; - break; + case Protocol.LDAP_REP_ADD: + Message = AddResponse + break - case Protocol.LDAP_REP_ADD: - Message = AddResponse; - break; + case Protocol.LDAP_REQ_BIND: + Message = BindRequest + break - case Protocol.LDAP_REQ_BIND: - Message = BindRequest; - break; + case Protocol.LDAP_REP_BIND: + Message = BindResponse + break - case Protocol.LDAP_REP_BIND: - Message = BindResponse; - break; + case Protocol.LDAP_REQ_COMPARE: + Message = CompareRequest + break - case Protocol.LDAP_REQ_COMPARE: - Message = CompareRequest; - break; + case Protocol.LDAP_REP_COMPARE: + Message = CompareResponse + break - case Protocol.LDAP_REP_COMPARE: - Message = CompareResponse; - break; + case Protocol.LDAP_REQ_DELETE: + Message = DeleteRequest + break - case Protocol.LDAP_REQ_DELETE: - Message = DeleteRequest; - break; + case Protocol.LDAP_REP_DELETE: + Message = DeleteResponse + break - case Protocol.LDAP_REP_DELETE: - Message = DeleteResponse; - break; + case Protocol.LDAP_REQ_EXTENSION: + Message = ExtendedRequest + break - case Protocol.LDAP_REQ_EXTENSION: - Message = ExtendedRequest; - break; + case Protocol.LDAP_REP_EXTENSION: + Message = ExtendedResponse + break - case Protocol.LDAP_REP_EXTENSION: - Message = ExtendedResponse; - break; + case Protocol.LDAP_REQ_MODIFY: + Message = ModifyRequest + break - case Protocol.LDAP_REQ_MODIFY: - Message = ModifyRequest; - break; + case Protocol.LDAP_REP_MODIFY: + Message = ModifyResponse + break - case Protocol.LDAP_REP_MODIFY: - Message = ModifyResponse; - break; + case Protocol.LDAP_REQ_MODRDN: + Message = ModifyDNRequest + break - case Protocol.LDAP_REQ_MODRDN: - Message = ModifyDNRequest; - break; + case Protocol.LDAP_REP_MODRDN: + Message = ModifyDNResponse + break - case Protocol.LDAP_REP_MODRDN: - Message = ModifyDNResponse; - break; + case Protocol.LDAP_REQ_SEARCH: + Message = SearchRequest + break - case Protocol.LDAP_REQ_SEARCH: - Message = SearchRequest; - break; + case Protocol.LDAP_REP_SEARCH_ENTRY: + Message = SearchEntry + break - case Protocol.LDAP_REP_SEARCH_ENTRY: - Message = SearchEntry; - break; + case Protocol.LDAP_REP_SEARCH_REF: + Message = SearchReference + break - case Protocol.LDAP_REP_SEARCH_REF: - Message = SearchReference; - break; + case Protocol.LDAP_REP_SEARCH: + Message = SearchResponse + break - case Protocol.LDAP_REP_SEARCH: - Message = SearchResponse; - break; + case Protocol.LDAP_REQ_UNBIND: + Message = UnbindRequest + break - case Protocol.LDAP_REQ_UNBIND: - Message = UnbindRequest; - break; - - default: - this.emit('error', - new Error('Op 0x' + (type ? type.toString(16) : '??') + + default: + this.emit('error', + new Error('Op 0x' + (type ? type.toString(16) : '??') + ' not supported'), - new LDAPResult({ - messageID: messageID, - protocolOp: type || Protocol.LDAP_REP_EXTENSION - })); + new LDAPResult({ + messageID: messageID, + protocolOp: type || Protocol.LDAP_REP_EXTENSION + })) - return false; + return false } - return new Message({ messageID: messageID, log: self.log - }); -}; + }) +} +/// --- Exports -///--- Exports - -module.exports = Parser; +module.exports = Parser diff --git a/lib/messages/result.js b/lib/messages/result.js index c0987d1..d92cfef 100644 --- a/lib/messages/result.js +++ b/lib/messages/result.js @@ -1,65 +1,61 @@ // Copyright 2011 Mark Cavage, Inc. All rights reserved. -var assert = require('assert-plus'); -var util = require('util'); +var assert = require('assert-plus') +var util = require('util') -var asn1 = require('asn1'); +// var asn1 = require('asn1') -var dtrace = require('../dtrace'); -var LDAPMessage = require('./message'); -var Protocol = require('../protocol'); +var dtrace = require('../dtrace') +var LDAPMessage = require('./message') +var Protocol = require('../protocol') +/// --- Globals -///--- Globals +// var Ber = asn1.Ber +// var BerWriter = asn1.BerWriter -var Ber = asn1.Ber; -var BerWriter = asn1.BerWriter; +/// --- API +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) { - options = options || {}; - assert.object(options); - assert.optionalNumber(options.status); - assert.optionalString(options.matchedDN); - assert.optionalString(options.errorMessage); - assert.optionalArrayOfString(options.referrals); + this.status = options.status || 0 // LDAP SUCCESS + this.matchedDN = options.matchedDN || '' + this.errorMessage = options.errorMessage || '' + this.referrals = options.referrals || [] - LDAPMessage.call(this, options); - - this.status = options.status || 0; // LDAP SUCCESS - this.matchedDN = options.matchedDN || ''; - this.errorMessage = options.errorMessage || ''; - this.referrals = options.referrals || []; - - this.connection = options.connection || null; + this.connection = options.connection || null } -util.inherits(LDAPResult, LDAPMessage); +util.inherits(LDAPResult, LDAPMessage) Object.defineProperties(LDAPResult.prototype, { type: { - get: function getType() { return 'LDAPResult'; }, + get: function getType () { return 'LDAPResult' }, configurable: false } -}); +}) LDAPResult.prototype.end = function (status) { - assert.ok(this.connection); + assert.ok(this.connection) - if (typeof (status) === 'number') - this.status = status; + if (typeof (status) === 'number') { this.status = status } - var ber = this.toBer(); - if (this.log.debug()) - this.log.debug('%s: sending: %j', this.connection.ldap.id, this.json); + var ber = this.toBer() + if (this.log.debug()) { this.log.debug('%s: sending: %j', this.connection.ldap.id, this.json) } try { - var self = this; - this.connection.write(ber); + var self = this + this.connection.write(ber) if (self._dtraceOp && self._dtraceId) { dtrace.fire('server-' + self._dtraceOp + '-done', function () { - var c = self.connection || {ldap: {}}; + var c = self.connection || { ldap: {} } return [ self._dtraceId || 0, (c.remoteAddress || ''), @@ -67,63 +63,59 @@ LDAPResult.prototype.end = function (status) { (self.requestDN ? self.requestDN.toString() : ''), status || self.status, self.errorMessage - ]; - }); + ] + }) } - } catch (e) { this.log.warn(e, '%s failure to write message %j', - this.connection.ldap.id, this.json); + this.connection.ldap.id, this.json) } - -}; +} LDAPResult.prototype._parse = function (ber) { - assert.ok(ber); + assert.ok(ber) - this.status = ber.readEnumeration(); - this.matchedDN = ber.readString(); - this.errorMessage = ber.readString(); + this.status = ber.readEnumeration() + this.matchedDN = ber.readString() + this.errorMessage = ber.readString() - var t = ber.peek(); + var t = ber.peek() if (t === Protocol.LDAP_REP_REFERRAL) { - var end = ber.offset + ber.length; - while (ber.offset < end) - this.referrals.push(ber.readString()); + var end = ber.offset + ber.length + while (ber.offset < end) { this.referrals.push(ber.readString()) } } - return true; -}; + return true +} LDAPResult.prototype._toBer = function (ber) { - assert.ok(ber); + assert.ok(ber) - ber.writeEnumeration(this.status); - ber.writeString(this.matchedDN || ''); - ber.writeString(this.errorMessage || ''); + ber.writeEnumeration(this.status) + ber.writeString(this.matchedDN || '') + ber.writeString(this.errorMessage || '') if (this.referrals.length) { - ber.startSequence(Protocol.LDAP_REP_REFERRAL); - ber.writeStringArray(this.referrals); - ber.endSequence(); + ber.startSequence(Protocol.LDAP_REP_REFERRAL) + ber.writeStringArray(this.referrals) + ber.endSequence() } - return ber; -}; + return ber +} LDAPResult.prototype._json = function (j) { - assert.ok(j); + assert.ok(j) - j.status = this.status; - j.matchedDN = this.matchedDN; - j.errorMessage = this.errorMessage; - j.referrals = this.referrals; + j.status = this.status + j.matchedDN = this.matchedDN + j.errorMessage = this.errorMessage + j.referrals = this.referrals - return j; -}; + return j +} +/// --- Exports -///--- Exports - -module.exports = LDAPResult; +module.exports = LDAPResult diff --git a/lib/messages/search_entry.js b/lib/messages/search_entry.js index 57b1458..b6ba798 100644 --- a/lib/messages/search_entry.js +++ b/lib/messages/search_entry.js @@ -1,196 +1,187 @@ // Copyright 2011 Mark Cavage, Inc. All rights reserved. -var assert = require('assert-plus'); -var util = require('util'); +var assert = require('assert-plus') +var util = require('util') -var asn1 = require('asn1'); +// var asn1 = require('asn1') -var LDAPMessage = require('./message'); -var Attribute = require('../attribute'); -var Protocol = require('../protocol'); -var lassert = require('../assert'); +var LDAPMessage = require('./message') +var Attribute = require('../attribute') +var Protocol = require('../protocol') +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) { - options = options || {}; - assert.object(options); - lassert.optionalStringDN(options.objectName); - - options.protocolOp = Protocol.LDAP_REP_SEARCH_ENTRY; - LDAPMessage.call(this, options); - - this.objectName = options.objectName || null; - this.setAttributes(options.attributes || []); + this.objectName = options.objectName || null + this.setAttributes(options.attributes || []) } -util.inherits(SearchEntry, LDAPMessage); +util.inherits(SearchEntry, LDAPMessage) Object.defineProperties(SearchEntry.prototype, { type: { - get: function getType() { return 'SearchEntry'; }, + get: function getType () { return 'SearchEntry' }, configurable: false }, _dn: { - get: function getDN() { return this.objectName; }, + get: function getDN () { return this.objectName }, configurable: false }, object: { - get: function getObject() { + get: function getObject () { var obj = { dn: this.dn.toString(), controls: [] - }; + } this.attributes.forEach(function (a) { if (a.vals && a.vals.length) { if (a.vals.length > 1) { - obj[a.type] = a.vals.slice(); + obj[a.type] = a.vals.slice() } else { - obj[a.type] = a.vals[0]; + obj[a.type] = a.vals[0] } } else { - obj[a.type] = []; + obj[a.type] = [] } - }); + }) this.controls.forEach(function (element, index, array) { - obj.controls.push(element.json); - }); - return obj; + obj.controls.push(element.json) + }) + return obj }, configurable: false }, raw: { - get: function getRaw() { + get: function getRaw () { var obj = { dn: this.dn.toString(), controls: [] - }; + } this.attributes.forEach(function (a) { if (a.buffers && a.buffers.length) { if (a.buffers.length > 1) { - obj[a.type] = a.buffers.slice(); + obj[a.type] = a.buffers.slice() } else { - obj[a.type] = a.buffers[0]; + obj[a.type] = a.buffers[0] } } else { - obj[a.type] = []; + obj[a.type] = [] } - }); + }) this.controls.forEach(function (element, index, array) { - obj.controls.push(element.json); - }); - return obj; + obj.controls.push(element.json) + }) + return obj }, configurable: false } -}); +}) SearchEntry.prototype.addAttribute = function (attr) { - if (!attr || typeof (attr) !== 'object') - throw new TypeError('attr (attribute) required'); + if (!attr || typeof (attr) !== 'object') { throw new TypeError('attr (attribute) required') } - this.attributes.push(attr); -}; + this.attributes.push(attr) +} SearchEntry.prototype.toObject = function () { - return this.object; -}; + return this.object +} SearchEntry.prototype.fromObject = function (obj) { - if (typeof (obj) !== 'object') - throw new TypeError('object required'); + if (typeof (obj) !== 'object') { throw new TypeError('object required') } - var self = this; - if (obj.controls) - this.controls = obj.controls; + var self = this + if (obj.controls) { this.controls = obj.controls } - if (obj.attributes) - obj = obj.attributes; - this.attributes = []; + if (obj.attributes) { obj = obj.attributes } + this.attributes = [] 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) { - if (typeof (obj) !== 'object') - throw new TypeError('object required'); + if (typeof (obj) !== 'object') { throw new TypeError('object required') } if (Array.isArray(obj)) { obj.forEach(function (a) { - if (!Attribute.isAttribute(a)) - throw new TypeError('entry must be an Array of Attributes'); - }); - this.attributes = obj; + if (!Attribute.isAttribute(a)) { throw new TypeError('entry must be an Array of Attributes') } + }) + this.attributes = obj } else { - var self = this; + var self = this - self.attributes = []; + self.attributes = [] Object.keys(obj).forEach(function (k) { - var attr = new Attribute({type: k}); + var attr = new Attribute({ type: k }) if (Array.isArray(obj[k])) { obj[k].forEach(function (v) { - attr.addValue(v.toString()); - }); + attr.addValue(v.toString()) + }) } else { - attr.addValue(obj[k].toString()); + attr.addValue(obj[k].toString()) } - self.attributes.push(attr); - }); + self.attributes.push(attr) + }) } -}; +} SearchEntry.prototype._json = function (j) { - assert.ok(j); + assert.ok(j) - j.objectName = this.objectName.toString(); - j.attributes = []; + j.objectName = this.objectName.toString() + j.attributes = [] 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) { - assert.ok(ber); + assert.ok(ber) - this.objectName = ber.readString(); - assert.ok(ber.readSequence()); + this.objectName = ber.readString() + assert.ok(ber.readSequence()) - var end = ber.offset + ber.length; + var end = ber.offset + ber.length while (ber.offset < end) { - var a = new Attribute(); - a.parse(ber); - this.attributes.push(a); + var a = new Attribute() + a.parse(ber) + this.attributes.push(a) } - return true; -}; + return true +} SearchEntry.prototype._toBer = function (ber) { - assert.ok(ber); + assert.ok(ber) - ber.writeString(this.objectName.toString()); - ber.startSequence(); + ber.writeString(this.objectName.toString()) + ber.startSequence() this.attributes.forEach(function (a) { // This may or may not be an attribute - ber = Attribute.toBer(a, ber); - }); - ber.endSequence(); + ber = Attribute.toBer(a, ber) + }) + ber.endSequence() - return ber; -}; + return ber +} +/// --- Exports -///--- Exports - -module.exports = SearchEntry; +module.exports = SearchEntry diff --git a/lib/messages/search_reference.js b/lib/messages/search_reference.js index f88b038..d9eb67d 100644 --- a/lib/messages/search_reference.js +++ b/lib/messages/search_reference.js @@ -1,105 +1,101 @@ // Copyright 2011 Mark Cavage, Inc. All rights reserved. -var assert = require('assert-plus'); -var util = require('util'); +var assert = require('assert-plus') +var util = require('util') -var asn1 = require('asn1'); +// var asn1 = require('asn1') -var LDAPMessage = require('./message'); -var Protocol = require('../protocol'); -var dn = require('../dn'); -var url = require('../url'); +var LDAPMessage = require('./message') +var Protocol = require('../protocol') +var dn = require('../dn') +var url = require('../url') +/// --- Globals -///--- Globals +// var BerWriter = asn1.BerWriter +var parseURL = url.parse -var BerWriter = asn1.BerWriter; -var parseURL = url.parse; +/// --- API +function SearchReference (options) { + options = options || {} + assert.object(options) -///--- API + options.protocolOp = Protocol.LDAP_REP_SEARCH_REF + LDAPMessage.call(this, options) -function SearchReference(options) { - options = options || {}; - assert.object(options); - - options.protocolOp = Protocol.LDAP_REP_SEARCH_REF; - LDAPMessage.call(this, options); - - this.uris = options.uris || []; + this.uris = options.uris || [] } -util.inherits(SearchReference, LDAPMessage); +util.inherits(SearchReference, LDAPMessage) Object.defineProperties(SearchReference.prototype, { type: { - get: function getType() { return 'SearchReference'; }, + get: function getType () { return 'SearchReference' }, configurable: false }, _dn: { - get: function getDN() { return new dn.DN(''); }, + get: function getDN () { return new dn.DN('') }, configurable: false }, object: { - get: function getObject() { + get: function getObject () { return { dn: this.dn.toString(), uris: this.uris.slice() - }; + } }, configurable: false }, urls: { - get: function getUrls() { return this.uris; }, - set: function setUrls(val) { - assert.ok(val); - assert.ok(Array.isArray(val)); - this.uris = val.slice(); + get: function getUrls () { return this.uris }, + set: function setUrls (val) { + assert.ok(val) + assert.ok(Array.isArray(val)) + this.uris = val.slice() }, configurable: false } -}); +}) SearchReference.prototype.toObject = function () { - return this.object; -}; + return this.object +} SearchReference.prototype.fromObject = function (obj) { - if (typeof (obj) !== 'object') - throw new TypeError('object required'); + if (typeof (obj) !== 'object') { 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) { - assert.ok(j); - j.uris = this.uris.slice(); - return j; -}; + assert.ok(j) + j.uris = this.uris.slice() + return j +} SearchReference.prototype._parse = function (ber, length) { - assert.ok(ber); + assert.ok(ber) while (ber.offset < length) { - var _url = ber.readString(); - parseURL(_url); - this.uris.push(_url); + var _url = ber.readString() + parseURL(_url) + this.uris.push(_url) } - return true; -}; + return true +} SearchReference.prototype._toBer = function (ber) { - assert.ok(ber); + assert.ok(ber) 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 diff --git a/lib/messages/search_request.js b/lib/messages/search_request.js index d55e59e..829ec7d 100644 --- a/lib/messages/search_request.js +++ b/lib/messages/search_request.js @@ -1,150 +1,152 @@ // Copyright 2011 Mark Cavage, Inc. All rights reserved. -var assert = require('assert-plus'); -var util = require('util'); +var assert = require('assert-plus') +var util = require('util') -var asn1 = require('asn1'); +var asn1 = require('asn1') -var LDAPMessage = require('./message'); -var LDAPResult = require('./result'); -var dn = require('../dn'); -var filters = require('../filters'); -var Protocol = require('../protocol'); +var LDAPMessage = require('./message') +// var LDAPResult = require('./result') +var dn = require('../dn') +var filters = require('../filters') +var Protocol = require('../protocol') +/// --- Globals -///--- Globals +var Ber = asn1.Ber -var Ber = asn1.Ber; +/// --- API +function SearchRequest (options) { + options = options || {} + assert.object(options) -///--- API - -function SearchRequest(options) { - options = options || {}; - assert.object(options); - - options.protocolOp = Protocol.LDAP_REQ_SEARCH; - LDAPMessage.call(this, options); + options.protocolOp = Protocol.LDAP_REQ_SEARCH + LDAPMessage.call(this, options) if (options.baseObject !== undefined) { - this.baseObject = options.baseObject; + this.baseObject = options.baseObject } else { - this.baseObject = dn.parse(''); + this.baseObject = dn.parse('') } - this.scope = options.scope || 'base'; - this.derefAliases = options.derefAliases || Protocol.NEVER_DEREF_ALIASES; - this.sizeLimit = options.sizeLimit || 0; - this.timeLimit = options.timeLimit || 0; - this.typesOnly = options.typesOnly || false; - this.filter = options.filter || null; - this.attributes = options.attributes ? options.attributes.slice(0) : []; + this.scope = options.scope || 'base' + this.derefAliases = options.derefAliases || Protocol.NEVER_DEREF_ALIASES + this.sizeLimit = options.sizeLimit || 0 + this.timeLimit = options.timeLimit || 0 + this.typesOnly = options.typesOnly || false + this.filter = options.filter || null + this.attributes = options.attributes ? options.attributes.slice(0) : [] } -util.inherits(SearchRequest, LDAPMessage); +util.inherits(SearchRequest, LDAPMessage) Object.defineProperties(SearchRequest.prototype, { type: { - get: function getType() { return 'SearchRequest'; }, + get: function getType () { return 'SearchRequest' }, configurable: false }, _dn: { - get: function getDN() { return this.baseObject; }, + get: function getDN () { return this.baseObject }, configurable: false }, scope: { - get: function getScope() { + get: function getScope () { switch (this._scope) { - case Protocol.SCOPE_BASE_OBJECT: return 'base'; - case Protocol.SCOPE_ONE_LEVEL: return 'one'; - case Protocol.SCOPE_SUBTREE: return 'sub'; - default: - throw new Error(this._scope + ' is an invalid search scope'); + case Protocol.SCOPE_BASE_OBJECT: return 'base' + case Protocol.SCOPE_ONE_LEVEL: return 'one' + case Protocol.SCOPE_SUBTREE: return 'sub' + default: + throw new Error(this._scope + ' is an invalid search scope') } }, - set: function setScope(val) { + set: function setScope (val) { if (typeof (val) === 'string') { switch (val) { - case 'base': - this._scope = Protocol.SCOPE_BASE_OBJECT; - break; - case 'one': - this._scope = Protocol.SCOPE_ONE_LEVEL; - break; - case 'sub': - this._scope = Protocol.SCOPE_SUBTREE; - break; - default: - throw new Error(val + ' is an invalid search scope'); + case 'base': + this._scope = Protocol.SCOPE_BASE_OBJECT + break + case 'one': + this._scope = Protocol.SCOPE_ONE_LEVEL + break + case 'sub': + this._scope = Protocol.SCOPE_SUBTREE + break + default: + throw new Error(val + ' is an invalid search scope') } } else { - this._scope = val; + this._scope = val } }, configurable: false } -}); +}) SearchRequest.prototype._parse = function (ber) { - assert.ok(ber); + assert.ok(ber) - this.baseObject = ber.readString(); - this.scope = ber.readEnumeration(); - this.derefAliases = ber.readEnumeration(); - this.sizeLimit = ber.readInt(); - this.timeLimit = ber.readInt(); - this.typesOnly = ber.readBoolean(); + this.baseObject = ber.readString() + this.scope = ber.readEnumeration() + this.derefAliases = ber.readEnumeration() + this.sizeLimit = ber.readInt() + this.timeLimit = ber.readInt() + this.typesOnly = ber.readBoolean() - this.filter = filters.parse(ber); + this.filter = filters.parse(ber) // look for attributes if (ber.peek() === 0x30) { - ber.readSequence(); - var end = ber.offset + ber.length; - while (ber.offset < end) - this.attributes.push(ber.readString().toLowerCase()); + ber.readSequence() + var end = ber.offset + ber.length + while (ber.offset < end) { this.attributes.push(ber.readString().toLowerCase()) } } - return true; -}; + return true +} SearchRequest.prototype._toBer = function (ber) { - assert.ok(ber); + assert.ok(ber) - ber.writeString(this.baseObject.toString()); - ber.writeEnumeration(this._scope); - ber.writeEnumeration(this.derefAliases); - ber.writeInt(this.sizeLimit); - ber.writeInt(this.timeLimit); - ber.writeBoolean(this.typesOnly); + // Format only with commas, since that is what RFC 4514 mandates. + // There's a gotcha here: even though it's called baseObject, + // it can be a string or a DN object. + var formattedDN = dn.DN.isDN(this.baseObject) + ? this.baseObject.format({ skipSpace: true }) + : 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'}); - ber = f.toBer(ber); + var f = this.filter || new filters.PresenceFilter({ attribute: 'objectclass' }) + ber = f.toBer(ber) - ber.startSequence(Ber.Sequence | Ber.Constructor); + ber.startSequence(Ber.Sequence | Ber.Constructor) if (this.attributes && this.attributes.length) { this.attributes.forEach(function (a) { - ber.writeString(a); - }); + ber.writeString(a) + }) } - ber.endSequence(); + ber.endSequence() - return ber; -}; + return ber +} SearchRequest.prototype._json = function (j) { - assert.ok(j); + assert.ok(j) - j.baseObject = this.baseObject; - j.scope = this.scope; - j.derefAliases = this.derefAliases; - j.sizeLimit = this.sizeLimit; - j.timeLimit = this.timeLimit; - j.typesOnly = this.typesOnly; - j.filter = this.filter.toString(); - j.attributes = this.attributes; + j.baseObject = this.baseObject + j.scope = this.scope + j.derefAliases = this.derefAliases + j.sizeLimit = this.sizeLimit + j.timeLimit = this.timeLimit + j.typesOnly = this.typesOnly + j.filter = this.filter.toString() + j.attributes = this.attributes - return j; -}; + return j +} +/// --- Exports -///--- Exports - -module.exports = SearchRequest; +module.exports = SearchRequest diff --git a/lib/messages/search_response.js b/lib/messages/search_response.js index 9c09024..d47e5ea 100644 --- a/lib/messages/search_response.js +++ b/lib/messages/search_response.js @@ -1,32 +1,31 @@ // Copyright 2011 Mark Cavage, Inc. All rights reserved. -var assert = require('assert-plus'); -var util = require('util'); +var assert = require('assert-plus') +var util = require('util') -var LDAPResult = require('./result'); -var SearchEntry = require('./search_entry'); -var SearchReference = require('./search_reference'); +var LDAPResult = require('./result') +var SearchEntry = require('./search_entry') +var SearchReference = require('./search_reference') -var dtrace = require('../dtrace'); -var parseDN = require('../dn').parse; -var parseURL = require('../url').parse; -var Protocol = require('../protocol'); +var dtrace = require('../dtrace') +var parseDN = require('../dn').parse +var parseURL = require('../url').parse +var Protocol = require('../protocol') +/// --- API -///--- API +function SearchResponse (options) { + options = options || {} + assert.object(options) -function SearchResponse(options) { - options = options || {}; - assert.object(options); + options.protocolOp = Protocol.LDAP_REP_SEARCH + LDAPResult.call(this, options) - options.protocolOp = Protocol.LDAP_REP_SEARCH; - LDAPResult.call(this, options); - - this.attributes = options.attributes ? options.attributes.slice() : []; - 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. @@ -36,61 +35,54 @@ util.inherits(SearchResponse, LDAPResult); * Defaults to 'false'. */ SearchResponse.prototype.send = function (entry, nofiltering) { - if (!entry || typeof (entry) !== 'object') - throw new TypeError('entry (SearchEntry) required'); - if (nofiltering === undefined) - nofiltering = false; - if (typeof (nofiltering) !== 'boolean') - throw new TypeError('noFiltering must be a boolean'); + if (!entry || typeof (entry) !== 'object') { throw new TypeError('entry (SearchEntry) required') } + if (nofiltering === undefined) { nofiltering = false } + if (typeof (nofiltering) !== 'boolean') { throw new TypeError('noFiltering must be a boolean') } - var self = this; + var self = this if (entry instanceof SearchEntry || entry instanceof SearchReference) { - if (!entry.messageID) - entry.messageID = this.messageID; - if (entry.messageID !== this.messageID) - throw new Error('SearchEntry messageID mismatch'); + if (!entry.messageID) { entry.messageID = this.messageID } + if (entry.messageID !== this.messageID) { throw new Error('SearchEntry messageID mismatch') } } else { - if (!entry.attributes) - throw new Error('entry.attributes required'); + if (!entry.attributes) { throw new Error('entry.attributes required') } - var savedAttrs = {}; - var all = (self.attributes.indexOf('*') !== -1); + var savedAttrs = {} + var all = (self.attributes.indexOf('*') !== -1) Object.keys(entry.attributes).forEach(function (a) { - var _a = a.toLowerCase(); + var _a = a.toLowerCase() if (!nofiltering && _a.length && _a[0] === '_') { - savedAttrs[a] = entry.attributes[a]; - delete entry.attributes[a]; + savedAttrs[a] = entry.attributes[a] + delete entry.attributes[a] } else if (!nofiltering && self.notAttributes.indexOf(_a) !== -1) { - savedAttrs[a] = entry.attributes[a]; - delete entry.attributes[a]; + savedAttrs[a] = entry.attributes[a] + delete entry.attributes[a] } else if (all) { - return; - } else if (self.attributes.length && self.attributes.indexOf(_a) === -1) { - savedAttrs[a] = entry.attributes[a]; - delete entry.attributes[a]; - } - }); - var save = entry; + } else if (self.attributes.length && self.attributes.indexOf(_a) === -1) { + savedAttrs[a] = entry.attributes[a] + delete entry.attributes[a] + } + }) + + var save = entry entry = new SearchEntry({ objectName: typeof (save.dn) === 'string' ? parseDN(save.dn) : save.dn, messageID: self.messageID, log: self.log - }); - entry.fromObject(save); + }) + entry.fromObject(save) } try { - if (this.log.debug()) - this.log.debug('%s: sending: %j', this.connection.ldap.id, entry.json); + if (this.log.debug) { this.log.debug('%s: sending: %j', this.connection.ldap.id, entry.json) } - this.connection.write(entry.toBer()); - this.sentEntries++; + this.connection.write(entry.toBer()) + this.sentEntries++ if (self._dtraceOp && self._dtraceId) { dtrace.fire('server-search-entry', function () { - var c = self.connection || {ldap: {}}; + var c = self.connection || { ldap: {} } return [ self._dtraceId || 0, (c.remoteAddress || ''), @@ -98,54 +90,49 @@ SearchResponse.prototype.send = function (entry, nofiltering) { (self.requestDN ? self.requestDN.toString() : ''), entry.objectName.toString(), entry.attributes.length - ]; - }); + ] + }) } // Restore attributes Object.keys(savedAttrs || {}).forEach(function (k) { - save.attributes[k] = savedAttrs[k]; - }); - + save.attributes[k] = savedAttrs[k] + }) } catch (e) { 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) { - assert.object(object); + assert.object(object) var entry = new SearchEntry({ messageID: this.messageID, log: this.log, objectName: object.objectName || object.dn - }); - entry.fromObject((object.attributes || object)); - return entry; -}; + }) + entry.fromObject((object.attributes || object)) + return entry +} SearchResponse.prototype.createSearchReference = function (uris) { - if (!uris) - throw new TypeError('uris ([string]) required'); + if (!uris) { throw new TypeError('uris ([string]) required') } - if (!Array.isArray(uris)) - uris = [uris]; + if (!Array.isArray(uris)) { uris = [uris] } for (var i = 0; i < uris.length; i++) { - if (typeof (uris[i]) == 'string') - uris[i] = parseURL(uris[i]); + if (typeof (uris[i]) === 'string') { uris[i] = parseURL(uris[i]) } } - var self = this; + var self = this return new SearchReference({ messageID: self.messageID, log: self.log, uris: uris - }); -}; + }) +} +/// --- Exports -///--- Exports - -module.exports = SearchResponse; +module.exports = SearchResponse diff --git a/lib/messages/unbind_request.js b/lib/messages/unbind_request.js index c1366a6..700b9ba 100644 --- a/lib/messages/unbind_request.js +++ b/lib/messages/unbind_request.js @@ -1,65 +1,62 @@ // Copyright 2011 Mark Cavage, Inc. All rights reserved. -var assert = require('assert-plus'); -var util = require('util'); +var assert = require('assert-plus') +var util = require('util') -var LDAPMessage = require('./message'); -var dn = require('../dn'); -var Protocol = require('../protocol'); +var LDAPMessage = require('./message') +var dn = require('../dn') +var Protocol = require('../protocol') +/// --- Globals -///--- Globals +var DN = dn.DN +var RDN = dn.RDN -var DN = dn.DN; -var RDN = dn.RDN; +/// --- API +function UnbindRequest (options) { + options = options || {} + assert.object(options) -///--- API - -function UnbindRequest(options) { - options = options || {}; - assert.object(options); - - options.protocolOp = Protocol.LDAP_REQ_UNBIND; - LDAPMessage.call(this, options); + options.protocolOp = Protocol.LDAP_REQ_UNBIND + LDAPMessage.call(this, options) } -util.inherits(UnbindRequest, LDAPMessage); +util.inherits(UnbindRequest, LDAPMessage) Object.defineProperties(UnbindRequest.prototype, { type: { - get: function getType() { return 'UnbindRequest'; }, + get: function getType () { return 'UnbindRequest' }, configurable: false }, _dn: { - get: function getDN() { + get: function getDN () { if (this.connection) { - return this.connection.ldap.bindDN; + return this.connection.ldap.bindDN } else { - return new DN([new RDN({cn: 'anonymous'})]); + return new DN([new RDN({ cn: 'anonymous' })]) } }, configurable: false } -}); +}) UnbindRequest.prototype._parse = function (ber) { - assert.ok(ber); + assert.ok(ber) - return true; -}; + return true +} UnbindRequest.prototype._toBer = function (ber) { - assert.ok(ber); + assert.ok(ber) - return ber; -}; + return ber +} UnbindRequest.prototype._json = function (j) { - assert.ok(j); + assert.ok(j) - return j; -}; + return j +} +/// --- Exports -///--- Exports - -module.exports = UnbindRequest; +module.exports = UnbindRequest diff --git a/lib/messages/unbind_response.js b/lib/messages/unbind_response.js index e3ca56c..6e0b9e4 100644 --- a/lib/messages/unbind_response.js +++ b/lib/messages/unbind_response.js @@ -1,33 +1,32 @@ // Copyright 2011 Mark Cavage, Inc. All rights reserved. -var assert = require('assert-plus'); -var util = require('util'); +var assert = require('assert-plus') +var util = require('util') -var dtrace = require('../dtrace'); +var dtrace = require('../dtrace') -var LDAPMessage = require('./result'); -var Protocol = require('../protocol'); +var LDAPMessage = require('./result') +// var Protocol = require('../protocol') - -///--- API +/// --- API // Ok, so there's really no such thing as an unbind 'response', but to make // the framework not suck, I just made this up, and have it stubbed so it's // not such a one-off. -function UnbindResponse(options) { - options = options || {}; - assert.object(options); +function UnbindResponse (options) { + options = options || {} + assert.object(options) - options.protocolOp = 0; - LDAPMessage.call(this, options); + options.protocolOp = 0 + LDAPMessage.call(this, options) } -util.inherits(UnbindResponse, LDAPMessage); +util.inherits(UnbindResponse, LDAPMessage) Object.defineProperties(UnbindResponse.prototype, { type: { - get: function getType() { return 'UnbindResponse'; }, + get: function getType () { return 'UnbindResponse' }, configurable: false } -}); +}) /** * Special override that just ends the connection, if present. @@ -35,16 +34,16 @@ Object.defineProperties(UnbindResponse.prototype, { * @param {Number} status completely ignored. */ 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) { dtrace.fire('server-' + self._dtraceOp + '-done', function () { - var c = self.connection || {ldap: {}}; + var c = self.connection || { ldap: {} } return [ self._dtraceId || 0, (c.remoteAddress || ''), @@ -52,16 +51,15 @@ UnbindResponse.prototype.end = function (status) { (self.requestDN ? self.requestDN.toString() : ''), 0, '' - ]; - }); + ] + }) } -}; +} UnbindResponse.prototype._json = function (j) { - return j; -}; + return j +} +/// --- Exports -///--- Exports - -module.exports = UnbindResponse; +module.exports = UnbindResponse diff --git a/lib/persistent_search.js b/lib/persistent_search.js index 79087aa..a94672f 100644 --- a/lib/persistent_search.js +++ b/lib/persistent_search.js @@ -1,123 +1,109 @@ -///--- Globals +/// --- Globals -var parseDN = require('./dn').parse; +// var parseDN = require('./dn').parse var EntryChangeNotificationControl = - require('./controls').EntryChangeNotificationControl; + require('./controls').EntryChangeNotificationControl -///--- API +/// --- API // Cache used to store connected persistent search clients -function PersistentSearch() { - this.clientList = []; +function PersistentSearch () { + this.clientList = [] } - PersistentSearch.prototype.addClient = function (req, res, callback) { - if (typeof (req) !== 'object') - throw new TypeError('req must be an object'); - if (typeof (res) !== 'object') - throw new TypeError('res must be an object'); - if (callback && typeof (callback) !== 'function') - throw new TypeError('callback must be a function'); + if (typeof (req) !== 'object') { throw new TypeError('req must be an object') } + if (typeof (res) !== 'object') { throw new TypeError('res must be an object') } + if (callback && typeof (callback) !== 'function') { throw new TypeError('callback must be a function') } - var log = req.log; + var log = req.log - var client = {}; - client.req = req; - client.res = res; + var client = {} + client.req = req + 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', - req.logId, this.clientList.length); - if (callback) - callback(client); -}; - + req.logId, this.clientList.length) + if (callback) { callback(client) } +} PersistentSearch.prototype.removeClient = function (req, res, callback) { - if (typeof (req) !== 'object') - throw new TypeError('req must be an object'); - if (typeof (res) !== 'object') - throw new TypeError('res must be an object'); - if (callback && typeof (callback) !== 'function') - throw new TypeError('callback must be a function'); + if (typeof (req) !== 'object') { throw new TypeError('req must be an object') } + if (typeof (res) !== 'object') { throw new TypeError('res must be an object') } + if (callback && typeof (callback) !== 'function') { throw new TypeError('callback must be a function') } - var log = req.log; - log.debug('%s removing client', req.logId); - var client = {}; - client.req = req; - client.res = res; + var log = req.log + log.debug('%s removing client', req.logId) + var client = {} + client.req = req + client.res = res // remove the client if it exists this.clientList.forEach(function (element, index, array) { if (element.req === client.req) { - log.debug('%s removing client from list', req.logId); - array.splice(index, 1); + log.debug('%s removing client from list', req.logId) + array.splice(index, 1) } - }); + }) log.debug('%s number of persistent search clients %s', - req.logId, this.clientList.length); - if (callback) - callback(client); -}; + req.logId, this.clientList.length) + if (callback) { callback(client) } +} - -function getOperationType(requestType) { +function getOperationType (requestType) { switch (requestType) { case 'AddRequest': case 'add': - return 1; + return 1 case 'DeleteRequest': case 'delete': - return 2; + return 2 case 'ModifyRequest': case 'modify': - return 4; + return 4 case 'ModifyDNRequest': case 'modrdn': - return 8; + return 8 default: 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 (req.persistentSearch.value.returnECs) { - var attrs = obj.attributes; - var value = {}; - value.changeType = getOperationType(attrs.changetype); + var attrs = obj.attributes + var value = {} + value.changeType = getOperationType(attrs.changetype) // if it's a modDN request, fill in the previous DN if (value.changeType === 8 && attrs.previousDN) { - value.previousDN = attrs.previousDN; + value.previousDN = attrs.previousDN } - value.changeNumber = attrs.changenumber; - return new EntryChangeNotificationControl({ value: value }); + value.changeNumber = attrs.changenumber + return new EntryChangeNotificationControl({ value: value }) } else { - return false; + return false } } - -function checkChangeType(req, requestType) { +function checkChangeType (req, requestType) { return (req.persistentSearch.value.changeTypes & - getOperationType(requestType)); + getOperationType(requestType)) } - -///--- Exports +/// --- Exports module.exports = { PersistentSearchCache: PersistentSearch, checkChangeType: checkChangeType, getEntryChangeNotificationControl: getEntryChangeNotificationControl -}; +} diff --git a/lib/protocol.js b/lib/protocol.js index 398f7b3..5b7927d 100644 --- a/lib/protocol.js +++ b/lib/protocol.js @@ -1,6 +1,5 @@ // Copyright 2011 Mark Cavage, Inc. All rights reserved. - module.exports = { // Misc @@ -51,4 +50,4 @@ module.exports = { LDAP_REP_MODRDN: 0x6d, LDAP_REP_COMPARE: 0x6f, LDAP_REP_EXTENSION: 0x78 -}; +} diff --git a/lib/server.js b/lib/server.js index 37439b9..38799b1 100644 --- a/lib/server.js +++ b/lib/server.js @@ -1,239 +1,226 @@ // Copyright 2011 Mark Cavage, Inc. All rights reserved. -var assert = require('assert'); -var EventEmitter = require('events').EventEmitter; -var net = require('net'); -var tls = require('tls'); -var util = require('util'); +var assert = require('assert') +var EventEmitter = require('events').EventEmitter +var net = require('net') +var tls = require('tls') +var util = require('util') -var asn1 = require('asn1'); -var VError = require('verror').VError; +// var asn1 = require('asn1') +var VError = require('verror').VError -var dn = require('./dn'); -var dtrace = require('./dtrace'); -var errors = require('./errors'); -var Protocol = require('./protocol'); +var dn = require('./dn') +var dtrace = require('./dtrace') +var errors = require('./errors') +var Protocol = require('./protocol') -var Parser = require('./messages').Parser; -var AbandonResponse = require('./messages/abandon_response'); -var AddResponse = require('./messages/add_response'); -var BindResponse = require('./messages/bind_response'); -var CompareResponse = require('./messages/compare_response'); -var DeleteResponse = require('./messages/del_response'); -var ExtendedResponse = require('./messages/ext_response'); -var LDAPResult = require('./messages/result'); -var ModifyResponse = require('./messages/modify_response'); -var ModifyDNResponse = require('./messages/moddn_response'); -var SearchRequest = require('./messages/search_request'); -var SearchResponse = require('./messages/search_response'); -var UnbindResponse = require('./messages/unbind_response'); +var Parser = require('./messages').Parser +var AbandonResponse = require('./messages/abandon_response') +var AddResponse = require('./messages/add_response') +var BindResponse = require('./messages/bind_response') +var CompareResponse = require('./messages/compare_response') +var DeleteResponse = require('./messages/del_response') +var ExtendedResponse = require('./messages/ext_response') +// var LDAPResult = require('./messages/result') +var ModifyResponse = require('./messages/modify_response') +var ModifyDNResponse = require('./messages/moddn_response') +var SearchRequest = require('./messages/search_request') +var SearchResponse = require('./messages/search_response') +var UnbindResponse = require('./messages/unbind_response') +/// --- Globals +// var Ber = asn1.Ber +// var BerReader = asn1.BerReader +var DN = dn.DN -///--- Globals +// var sprintf = util.format -var Ber = asn1.Ber; -var BerReader = asn1.BerReader; -var DN = dn.DN; +/// --- Helpers -var sprintf = util.format; +function mergeFunctionArgs (argv, start, end) { + assert.ok(argv) + if (!start) { start = 0 } + if (!end) { end = argv.length } -///--- Helpers - -function mergeFunctionArgs(argv, start, end) { - assert.ok(argv); - - if (!start) - start = 0; - if (!end) - end = argv.length; - - var handlers = []; + var handlers = [] for (var i = start; i < end; i++) { if (argv[i] instanceof Array) { - var arr = argv[i]; + var arr = argv[i] for (var j = 0; j < arr.length; j++) { if (!(arr[j] instanceof Function)) { - throw new TypeError('Invalid argument type: ' + typeof (arr[j])); + throw new TypeError('Invalid argument type: ' + typeof (arr[j])) } - handlers.push(arr[j]); + handlers.push(arr[j]) } } else if (argv[i] instanceof Function) { - handlers.push(argv[i]); + handlers.push(argv[i]) } else { - throw new TypeError('Invalid argument type: ' + typeof (argv[i])); + throw new TypeError('Invalid argument type: ' + typeof (argv[i])) } } - return handlers; + return handlers } +function getResponse (req) { + assert.ok(req) -function getResponse(req) { - assert.ok(req); - - var Response; + var Response switch (req.protocolOp) { - case Protocol.LDAP_REQ_BIND: - Response = BindResponse; - break; - case Protocol.LDAP_REQ_ABANDON: - Response = AbandonResponse; - break; - case Protocol.LDAP_REQ_ADD: - Response = AddResponse; - break; - case Protocol.LDAP_REQ_COMPARE: - Response = CompareResponse; - break; - case Protocol.LDAP_REQ_DELETE: - Response = DeleteResponse; - break; - case Protocol.LDAP_REQ_EXTENSION: - Response = ExtendedResponse; - break; - case Protocol.LDAP_REQ_MODIFY: - Response = ModifyResponse; - break; - case Protocol.LDAP_REQ_MODRDN: - Response = ModifyDNResponse; - break; - case Protocol.LDAP_REQ_SEARCH: - Response = SearchResponse; - break; - case Protocol.LDAP_REQ_UNBIND: - Response = UnbindResponse; - break; - default: - return null; + case Protocol.LDAP_REQ_BIND: + Response = BindResponse + break + case Protocol.LDAP_REQ_ABANDON: + Response = AbandonResponse + break + case Protocol.LDAP_REQ_ADD: + Response = AddResponse + break + case Protocol.LDAP_REQ_COMPARE: + Response = CompareResponse + break + case Protocol.LDAP_REQ_DELETE: + Response = DeleteResponse + break + case Protocol.LDAP_REQ_EXTENSION: + Response = ExtendedResponse + break + case Protocol.LDAP_REQ_MODIFY: + Response = ModifyResponse + break + case Protocol.LDAP_REQ_MODRDN: + Response = ModifyDNResponse + break + case Protocol.LDAP_REQ_SEARCH: + Response = SearchResponse + break + case Protocol.LDAP_REQ_UNBIND: + Response = UnbindResponse + break + default: + return null } - assert.ok(Response); + assert.ok(Response) var res = new Response({ messageID: req.messageID, log: req.log, attributes: ((req instanceof SearchRequest) ? req.attributes : undefined) - }); - res.connection = req.connection; - res.logId = req.logId; + }) + res.connection = req.connection + res.logId = req.logId - return res; + return res } +function defaultHandler (req, res, next) { + assert.ok(req) + assert.ok(res) + assert.ok(next) -function defaultHandler(req, res, next) { - assert.ok(req); - assert.ok(res); - assert.ok(next); - - res.matchedDN = req.dn.toString(); - res.errorMessage = 'Server method not implemented'; - res.end(errors.LDAP_OTHER); - return next(); + res.matchedDN = req.dn.toString() + res.errorMessage = 'Server method not implemented' + res.end(errors.LDAP_OTHER) + return next() } +function defaultNoOpHandler (req, res, next) { + assert.ok(req) + assert.ok(res) + assert.ok(next) -function defaultNoOpHandler(req, res, next) { - assert.ok(req); - assert.ok(res); - assert.ok(next); - - res.end(); - return next(); + res.end() + return next() } +function noSuffixHandler (req, res, next) { + assert.ok(req) + assert.ok(res) + assert.ok(next) -function noSuffixHandler(req, res, next) { - assert.ok(req); - assert.ok(res); - assert.ok(next); - - res.errorMessage = 'No tree found for: ' + req.dn.toString(); - res.end(errors.LDAP_NO_SUCH_OBJECT); - return next(); + res.errorMessage = 'No tree found for: ' + req.dn.toString() + res.end(errors.LDAP_NO_SUCH_OBJECT) + return next() } +function noExOpHandler (req, res, next) { + assert.ok(req) + assert.ok(res) + assert.ok(next) -function noExOpHandler(req, res, next) { - assert.ok(req); - assert.ok(res); - assert.ok(next); - - res.errorMessage = req.requestName + ' not supported'; - res.end(errors.LDAP_PROTOCOL_ERROR); - return next(); + res.errorMessage = req.requestName + ' not supported' + res.end(errors.LDAP_PROTOCOL_ERROR) + return next() } +function fireDTraceProbe (req, res) { + assert.ok(req) -function fireDTraceProbe(req, res) { - assert.ok(req); - - req._dtraceId = res._dtraceId = dtrace._nextId(); + req._dtraceId = res._dtraceId = dtrace._nextId() var probeArgs = [ req._dtraceId, req.connection.remoteAddress || 'localhost', req.connection.ldap.bindDN.toString(), req.dn.toString() - ]; + ] - var op; + var op switch (req.protocolOp) { - case Protocol.LDAP_REQ_ABANDON: - op = 'abandon'; - break; - case Protocol.LDAP_REQ_ADD: - op = 'add'; - probeArgs.push(req.attributes.length); - break; - case Protocol.LDAP_REQ_BIND: - op = 'bind'; - break; - case Protocol.LDAP_REQ_COMPARE: - op = 'compare'; - probeArgs.push(req.attribute); - probeArgs.push(req.value); - break; - case Protocol.LDAP_REQ_DELETE: - op = 'delete'; - break; - case Protocol.LDAP_REQ_EXTENSION: - op = 'exop'; - probeArgs.push(req.name); - probeArgs.push(req.value); - break; - case Protocol.LDAP_REQ_MODIFY: - op = 'modify'; - probeArgs.push(req.changes.length); - break; - case Protocol.LDAP_REQ_MODRDN: - op = 'modifydn'; - probeArgs.push(req.newRdn.toString()); - probeArgs.push((req.newSuperior ? req.newSuperior.toString() : '')); - break; - case Protocol.LDAP_REQ_SEARCH: - op = 'search'; - probeArgs.push(req.scope); - probeArgs.push(req.filter.toString()); - break; - case Protocol.LDAP_REQ_UNBIND: - op = 'unbind'; - break; - default: - break; + case Protocol.LDAP_REQ_ABANDON: + op = 'abandon' + break + case Protocol.LDAP_REQ_ADD: + op = 'add' + probeArgs.push(req.attributes.length) + break + case Protocol.LDAP_REQ_BIND: + op = 'bind' + break + case Protocol.LDAP_REQ_COMPARE: + op = 'compare' + probeArgs.push(req.attribute) + probeArgs.push(req.value) + break + case Protocol.LDAP_REQ_DELETE: + op = 'delete' + break + case Protocol.LDAP_REQ_EXTENSION: + op = 'exop' + probeArgs.push(req.name) + probeArgs.push(req.value) + break + case Protocol.LDAP_REQ_MODIFY: + op = 'modify' + probeArgs.push(req.changes.length) + break + case Protocol.LDAP_REQ_MODRDN: + op = 'modifydn' + probeArgs.push(req.newRdn.toString()) + probeArgs.push((req.newSuperior ? req.newSuperior.toString() : '')) + break + case Protocol.LDAP_REQ_SEARCH: + op = 'search' + probeArgs.push(req.scope) + probeArgs.push(req.filter.toString()) + break + case Protocol.LDAP_REQ_UNBIND: + op = 'unbind' + break + default: + break } - res._dtraceOp = op; + res._dtraceOp = op dtrace.fire('server-' + op + '-start', function () { - return probeArgs; - }); + return probeArgs + }) } - - -///--- API +/// --- API /** * Constructs a new server that you can call .listen() on, in the various @@ -250,12 +237,10 @@ function fireDTraceProbe(req, res) { * @param {Object} options (optional) parameterization object. * @throws {TypeError} on bad input. */ -function Server(options) { +function Server (options) { if (options) { - if (typeof (options) !== 'object') - throw new TypeError('options (object) required'); - if (typeof (options.log) !== 'object') - throw new TypeError('options.log must be an object'); + if (typeof (options) !== 'object') { throw new TypeError('options (object) required') } + if (typeof (options.log) !== 'object') { throw new TypeError('options.log must be an object') } if (options.certificate || options.key) { if (!(options.certificate && options.key) || @@ -264,270 +249,257 @@ function Server(options) { (typeof (options.key) !== 'string' && !Buffer.isBuffer(options.key))) { throw new TypeError('options.certificate and options.key ' + - '(string or buffer) are both required for TLS'); + '(string or buffer) are both required for TLS') } } } else { - options = {}; + options = {} } - var self = this; + var self = this - EventEmitter.call(this, options); + EventEmitter.call(this, options) - this._chain = []; - this.log = options.log; - this.strictDN = (options.strictDN !== undefined) ? options.strictDN : true; + this._chain = [] + this.log = options.log + this.strictDN = (options.strictDN !== undefined) ? options.strictDN : true - var log = this.log; + var log = this.log - function setupConnection(c) { - assert.ok(c); + function setupConnection (c) { + assert.ok(c) if (c.type === 'unix') { - c.remoteAddress = self.server.path; - c.remotePort = c.fd; + c.remoteAddress = self.server.path + c.remotePort = c.fd } else if (c.socket) { // TLS - c.remoteAddress = c.socket.remoteAddress; - c.remotePort = c.socket.remotePort; + c.remoteAddress = c.socket.remoteAddress + c.remotePort = c.socket.remotePort } - - var rdn = new dn.RDN({cn: 'anonymous'}); + var rdn = new dn.RDN({ cn: 'anonymous' }) c.ldap = { id: c.remoteAddress + ':' + c.remotePort, config: options, _bindDN: new DN([rdn]) - }; + } c.addListener('timeout', function () { - log.trace('%s timed out', c.ldap.id); - c.destroy(); - }); + log.trace('%s timed out', c.ldap.id) + c.destroy() + }) c.addListener('end', function () { - log.trace('%s shutdown', c.ldap.id); - }); + log.trace('%s shutdown', c.ldap.id) + }) c.addListener('error', function (err) { - log.warn('%s unexpected connection error', c.ldap.id, err); - self.emit('clientError', err); - c.destroy(); - }); - c.addListener('close', function (had_err) { - log.trace('%s close; had_err=%j', c.ldap.id, had_err); - c.end(); - }); + log.warn('%s unexpected connection error', c.ldap.id, err) + self.emit('clientError', err) + c.destroy() + }) + c.addListener('close', function (closeError) { + log.trace('%s close; had_err=%j', c.ldap.id, closeError) + c.end() + }) c.ldap.__defineGetter__('bindDN', function () { - return c.ldap._bindDN; - }); + return c.ldap._bindDN + }) c.ldap.__defineSetter__('bindDN', function (val) { - if (!(val instanceof DN)) - throw new TypeError('DN required'); + if (!(val instanceof DN)) { throw new TypeError('DN required') } - c.ldap._bindDN = val; - return val; - }); - return c; + c.ldap._bindDN = val + return val + }) + return c } - function newConnection(c) { - setupConnection(c); - log.trace('new connection from %s', c.ldap.id); + function newConnection (c) { + setupConnection(c) + log.trace('new connection from %s', c.ldap.id) dtrace.fire('server-connection', function () { - return [c.remoteAddress]; - }); + return [c.remoteAddress] + }) c.parser = new Parser({ log: options.log - }); + }) c.parser.on('message', function (req) { - req.connection = c; - req.logId = c.ldap.id + '::' + req.messageID; - req.startTime = new Date().getTime(); + req.connection = c + req.logId = c.ldap.id + '::' + req.messageID + req.startTime = new Date().getTime() - if (log.debug()) - log.debug('%s: message received: req=%j', c.ldap.id, req.json); + if (log.debug()) { log.debug('%s: message received: req=%j', c.ldap.id, req.json) } - var res = getResponse(req); + var res = getResponse(req) if (!res) { - log.warn('Unimplemented server method: %s', req.type); - c.destroy(); - return false; + log.warn('Unimplemented server method: %s', req.type) + c.destroy() + return false } // parse string DNs for routing/etc try { switch (req.protocolOp) { - case Protocol.LDAP_REQ_BIND: - req.name = dn.parse(req.name); - break; - case Protocol.LDAP_REQ_ADD: - case Protocol.LDAP_REQ_COMPARE: - case Protocol.LDAP_REQ_DELETE: - req.entry = dn.parse(req.entry); - break; - case Protocol.LDAP_REQ_MODIFY: - req.object = dn.parse(req.object); - break; - case Protocol.LDAP_REQ_MODRDN: - req.entry = dn.parse(req.entry); - // TODO: handle newRdn/Superior - break; - case Protocol.LDAP_REQ_SEARCH: - req.baseObject = dn.parse(req.baseObject); - break; - default: - break; + case Protocol.LDAP_REQ_BIND: + req.name = dn.parse(req.name) + break + case Protocol.LDAP_REQ_ADD: + case Protocol.LDAP_REQ_COMPARE: + case Protocol.LDAP_REQ_DELETE: + req.entry = dn.parse(req.entry) + break + case Protocol.LDAP_REQ_MODIFY: + req.object = dn.parse(req.object) + break + case Protocol.LDAP_REQ_MODRDN: + req.entry = dn.parse(req.entry) + // TODO: handle newRdn/Superior + break + case Protocol.LDAP_REQ_SEARCH: + req.baseObject = dn.parse(req.baseObject) + break + default: + break } } catch (e) { if (self.strictDN) { - return res.end(errors.LDAP_INVALID_DN_SYNTAX); + return res.end(errors.LDAP_INVALID_DN_SYNTAX) } } - res.connection = c; - res.logId = req.logId; - res.requestDN = req.dn; + res.connection = c + res.logId = req.logId + res.requestDN = req.dn - var chain = self._getHandlerChain(req, res); + var chain = self._getHandlerChain(req, res) - var i = 0; - return function (err) { - function sendError(err) { - res.status = err.code || errors.LDAP_OPERATIONS_ERROR; - res.matchedDN = req.suffix ? req.suffix.toString() : ''; - res.errorMessage = err.message || ''; - return res.end(); + var i = 0 + return (function messageIIFE (err) { + function sendError (err) { + res.status = err.code || errors.LDAP_OPERATIONS_ERROR + res.matchedDN = req.suffix ? req.suffix.toString() : '' + res.errorMessage = err.message || '' + return res.end() } - function after() { - if (!self._postChain || !self._postChain.length) - return; + function after () { + if (!self._postChain || !self._postChain.length) { return } - function next() {} // stub out next for the post chain + function next () {} // stub out next for the post chain self._postChain.forEach(function (c) { - c.call(self, req, res, next); - }); + c.call(self, req, res, next) + }) } if (err) { - log.trace('%s sending error: %s', req.logId, err.stack || err); - self.emit('clientError', err); - sendError(err); - return after(); + log.trace('%s sending error: %s', req.logId, err.stack || err) + self.emit('clientError', err) + sendError(err) + return after() } try { - var next = arguments.callee; - if (chain.handlers[i]) - return chain.handlers[i++].call(chain.backend, req, res, next); + var next = messageIIFE + if (chain.handlers[i]) { return chain.handlers[i++].call(chain.backend, req, res, next) } - if (req.protocolOp === Protocol.LDAP_REQ_BIND && res.status === 0) - c.ldap.bindDN = req.dn; + if (req.protocolOp === Protocol.LDAP_REQ_BIND && res.status === 0) { c.ldap.bindDN = req.dn } - return after(); + return after() } catch (e) { - if (!e.stack) - e.stack = e.toString(); - log.error('%s uncaught exception: %s', req.logId, e.stack); - return sendError(new errors.OperationsError(e.message)); + if (!e.stack) { e.stack = e.toString() } + log.error('%s uncaught exception: %s', req.logId, e.stack) + return sendError(new errors.OperationsError(e.message)) } - - }(); - }); + }()) + }) c.parser.on('error', function (err, message) { - self.emit('error', new VError(err, 'Parser error for %s', c.ldap.id)); + self.emit('error', new VError(err, 'Parser error for %s', c.ldap.id)) - if (!message) - return c.destroy(); + if (!message) { return c.destroy() } - var res = getResponse(message); - if (!res) - return c.destroy(); + var res = getResponse(message) + if (!res) { return c.destroy() } - res.status = 0x02; // protocol error - res.errorMessage = err.toString(); - return c.end(res.toBer()); - }); + res.status = 0x02 // protocol error + res.errorMessage = err.toString() + return c.end(res.toBer()) + }) c.on('data', function (data) { - if (log.trace()) - log.trace('data on %s: %s', c.ldap.id, util.inspect(data)); - - c.parser.write(data); - }); + if (log.trace()) { log.trace('data on %s: %s', c.ldap.id, util.inspect(data)) } + c.parser.write(data) + }) } // end newConnection - this.routes = {}; + this.routes = {} if ((options.cert || options.certificate) && options.key) { - options.cert = options.cert || options.certificate; - this.server = tls.createServer(options, newConnection); + options.cert = options.cert || options.certificate + this.server = tls.createServer(options, newConnection) } else { - this.server = net.createServer(newConnection); + this.server = net.createServer(newConnection) } - this.server.log = options.log; + this.server.log = options.log this.server.ldap = { config: options - }; + } this.server.on('close', function () { - self.emit('close'); - }); + self.emit('close') + }) this.server.on('error', function (err) { - self.emit('error', err); - }); + self.emit('error', err) + }) } -util.inherits(Server, EventEmitter); +util.inherits(Server, EventEmitter) Object.defineProperties(Server.prototype, { maxConnections: { - get: function getMaxConnections() { - return this.server.maxConnections; + get: function getMaxConnections () { + return this.server.maxConnections }, - set: function setMaxConnections(val) { - this.server.maxConnections = val; + set: function setMaxConnections (val) { + this.server.maxConnections = val }, configurable: false }, connections: { - get: function getConnections() { - return this.server.connections; + get: function getConnections () { + return this.server.connections }, configurable: false }, name: { - get: function getName() { - return 'LDAPServer'; + get: function getName () { + return 'LDAPServer' }, configurable: false }, url: { - get: function getURL() { - var str; - var addr = this.server.address(); + get: function getURL () { + var str + var addr = this.server.address() if (!addr) { - return null; + return null } if (!addr.family) { - str = 'ldapi://'; - str += this.host.replace(new RegExp('/', 'g'), '%2f'); - return str; + str = 'ldapi://' + str += this.host.replace(new RegExp('/', 'g'), '%2f') + return str } if (this.server instanceof tls.Server) { - str = 'ldaps://'; + str = 'ldaps://' } else { - str = 'ldap://'; + str = 'ldap://' } - str += this.host + ':' + this.port; - return str; + str += this.host + ':' + this.port + return str }, configurable: false } -}); -module.exports = Server; - +}) +module.exports = Server /** * Adds a handler (chain) for the LDAP add method. @@ -540,10 +512,9 @@ module.exports = Server; * @throws {TypeError} on bad input */ Server.prototype.add = function (name) { - var args = Array.prototype.slice.call(arguments, 1); - return this._mount(Protocol.LDAP_REQ_ADD, name, args); -}; - + var args = Array.prototype.slice.call(arguments, 1) + return this._mount(Protocol.LDAP_REQ_ADD, name, args) +} /** * Adds a handler (chain) for the LDAP bind method. @@ -556,10 +527,9 @@ Server.prototype.add = function (name) { * @throws {TypeError} on bad input */ Server.prototype.bind = function (name) { - var args = Array.prototype.slice.call(arguments, 1); - return this._mount(Protocol.LDAP_REQ_BIND, name, args); -}; - + var args = Array.prototype.slice.call(arguments, 1) + return this._mount(Protocol.LDAP_REQ_BIND, name, args) +} /** * Adds a handler (chain) for the LDAP compare method. @@ -572,10 +542,9 @@ Server.prototype.bind = function (name) { * @throws {TypeError} on bad input */ Server.prototype.compare = function (name) { - var args = Array.prototype.slice.call(arguments, 1); - return this._mount(Protocol.LDAP_REQ_COMPARE, name, args); -}; - + var args = Array.prototype.slice.call(arguments, 1) + return this._mount(Protocol.LDAP_REQ_COMPARE, name, args) +} /** * Adds a handler (chain) for the LDAP delete method. @@ -588,10 +557,9 @@ Server.prototype.compare = function (name) { * @throws {TypeError} on bad input */ Server.prototype.del = function (name) { - var args = Array.prototype.slice.call(arguments, 1); - return this._mount(Protocol.LDAP_REQ_DELETE, name, args); -}; - + var args = Array.prototype.slice.call(arguments, 1) + return this._mount(Protocol.LDAP_REQ_DELETE, name, args) +} /** * Adds a handler (chain) for the LDAP exop method. @@ -604,10 +572,9 @@ Server.prototype.del = function (name) { * @throws {TypeError} on bad input. */ Server.prototype.exop = function (name) { - var args = Array.prototype.slice.call(arguments, 1); - return this._mount(Protocol.LDAP_REQ_EXTENSION, name, args, true); -}; - + var args = Array.prototype.slice.call(arguments, 1) + return this._mount(Protocol.LDAP_REQ_EXTENSION, name, args, true) +} /** * Adds a handler (chain) for the LDAP modify method. @@ -620,10 +587,9 @@ Server.prototype.exop = function (name) { * @throws {TypeError} on bad input */ Server.prototype.modify = function (name) { - var args = Array.prototype.slice.call(arguments, 1); - return this._mount(Protocol.LDAP_REQ_MODIFY, name, args); -}; - + var args = Array.prototype.slice.call(arguments, 1) + return this._mount(Protocol.LDAP_REQ_MODIFY, name, args) +} /** * Adds a handler (chain) for the LDAP modifyDN method. @@ -636,10 +602,9 @@ Server.prototype.modify = function (name) { * @throws {TypeError} on bad input */ Server.prototype.modifyDN = function (name) { - var args = Array.prototype.slice.call(arguments, 1); - return this._mount(Protocol.LDAP_REQ_MODRDN, name, args); -}; - + var args = Array.prototype.slice.call(arguments, 1) + return this._mount(Protocol.LDAP_REQ_MODRDN, name, args) +} /** * Adds a handler (chain) for the LDAP search method. @@ -652,10 +617,9 @@ Server.prototype.modifyDN = function (name) { * @throws {TypeError} on bad input */ Server.prototype.search = function (name) { - var args = Array.prototype.slice.call(arguments, 1); - return this._mount(Protocol.LDAP_REQ_SEARCH, name, args); -}; - + var args = Array.prototype.slice.call(arguments, 1) + return this._mount(Protocol.LDAP_REQ_SEARCH, name, args) +} /** * Adds a handler (chain) for the LDAP unbind method. @@ -667,139 +631,137 @@ Server.prototype.search = function (name) { * @throws {TypeError} on bad input */ Server.prototype.unbind = function () { - var args = Array.prototype.slice.call(arguments, 0); - return this._mount(Protocol.LDAP_REQ_UNBIND, 'unbind', args, true); -}; + var args = Array.prototype.slice.call(arguments, 0) + return this._mount(Protocol.LDAP_REQ_UNBIND, 'unbind', args, true) +} - -Server.prototype.use = function use() { - var args = Array.prototype.slice.call(arguments); - var chain = mergeFunctionArgs(args, 0, args.length); - var self = this; +Server.prototype.use = function use () { + var args = Array.prototype.slice.call(arguments) + var chain = mergeFunctionArgs(args, 0, args.length) + var self = this chain.forEach(function (c) { - self._chain.push(c); - }); -}; - + self._chain.push(c) + }) +} Server.prototype.after = function () { - if (!this._postChain) - this._postChain = []; + if (!this._postChain) { this._postChain = [] } - var self = this; + var self = this mergeFunctionArgs(arguments).forEach(function (h) { - self._postChain.push(h); - }); -}; - + self._postChain.push(h) + }) +} // All these just reexpose the requisite net.Server APIs Server.prototype.listen = function (port, host, callback) { - if (typeof (port) !== 'number' && typeof (port) !== 'string') - throw new TypeError('port (number or path) required'); + if (typeof (port) !== 'number' && typeof (port) !== 'string') { throw new TypeError('port (number or path) required') } if (typeof (host) === 'function') { - callback = host; - host = '0.0.0.0'; + callback = host + host = '0.0.0.0' } if (typeof (port) === 'string' && /^[0-9]+$/.test(port)) { // Disambiguate between string ports and file paths - port = parseInt(port, 10); + port = parseInt(port, 10) } - var self = this; + var self = this - function cbListen() { + function cbListen () { if (typeof (port) === 'number') { - self.host = self.address().address; - self.port = self.address().port; + self.host = self.address().address + self.port = self.address().port } else { - self.host = port; - self.port = self.server.fd; + self.host = port + self.port = self.server.fd } - if (typeof (callback) === 'function') - callback(); + if (typeof (callback) === 'function') { callback() } } if (typeof (port) === 'number') { - return this.server.listen(port, host, cbListen); + return this.server.listen(port, host, cbListen) } else { - return this.server.listen(port, cbListen); + return this.server.listen(port, cbListen) } -}; +} Server.prototype.listenFD = function (fd) { - this.host = 'unix-domain-socket'; - this.port = fd; - return this.server.listenFD(fd); -}; -Server.prototype.close = function () { - return this.server.close(); -}; + this.host = 'unix-domain-socket' + this.port = fd + return this.server.listenFD(fd) +} +Server.prototype.close = function (callback) { + return this.server.close(callback) +} Server.prototype.address = function () { - return this.server.address(); -}; + return this.server.address() +} +Server.prototype.getConnections = function (callback) { + return this.server.getConnections(callback) +} Server.prototype._getRoute = function (_dn, backend) { - assert.ok(dn); + assert.ok(dn) - if (!backend) - backend = this; + if (!backend) { backend = this } - var name; + var name if (_dn instanceof dn.DN) { - name = _dn.toString(); + name = _dn.toString() } else { - name = _dn; + name = _dn } if (!this.routes[name]) { - this.routes[name] = {}; - this.routes[name].backend = backend; - this.routes[name].dn = _dn; + this.routes[name] = {} + this.routes[name].backend = backend + this.routes[name].dn = _dn // Force regeneration of the route key cache on next request - this._routeKeyCache = null; + this._routeKeyCache = null } - return this.routes[name]; -}; + return this.routes[name] +} - -Server.prototype._sortedRouteKeys = function _sortedRouteKeys() { +Server.prototype._sortedRouteKeys = function _sortedRouteKeys () { // The filtered/sorted route keys are cached to prevent needlessly // regenerating the list for every incoming request. if (!this._routeKeyCache) { - var self = this; - var reversedRDNsToKeys = {}; + var self = this + var reversedRDNsToKeys = {} // Generate mapping of reversedRDNs(DN) -> routeKey Object.keys(this.routes).forEach(function (key) { - var _dn = self.routes[key].dn; + var _dn = self.routes[key].dn // Ignore non-DN routes such as exop or unbind if (_dn instanceof dn.DN) { - var reversed = _dn.clone(); - reversed.rdns.reverse(); - reversedRDNsToKeys[reversed.format()] = key; + var reversed = _dn.clone() + reversed.rdns.reverse() + reversedRDNsToKeys[reversed.format()] = key } - }); - var output = []; + }) + var output = [] // Reverse-sort on reversedRDS(DN) in order to output routeKey list. // This will place more specific DNs in front of their parents: // 1. dc=test, dc=domain, dc=sub // 2. dc=test, dc=domain // 3. dc=other, dc=foobar Object.keys(reversedRDNsToKeys).sort().reverse().forEach(function (_dn) { - output.push(reversedRDNsToKeys[_dn]); - }); - this._routeKeyCache = output; + output.push(reversedRDNsToKeys[_dn]) + }) + this._routeKeyCache = output } - return this._routeKeyCache; -}; + return this._routeKeyCache +} +Server.prototype._getHandlerChain = function _getHandlerChain (req, res) { + assert.ok(req) -Server.prototype._getHandlerChain = function _getHandlerChain(req, res) { - assert.ok(req); + fireDTraceProbe(req, res) - fireDTraceProbe(req, res); + var self = this + var routes = this.routes + var route // check anonymous bind if (req.protocolOp === Protocol.LDAP_REQ_BIND && @@ -808,75 +770,72 @@ Server.prototype._getHandlerChain = function _getHandlerChain(req, res) { return { backend: self, handlers: [defaultNoOpHandler] - }; + } } - var op = '0x' + req.protocolOp.toString(16); - var self = this; - var routes = this.routes; - var route; + var op = '0x' + req.protocolOp.toString(16) // Special cases are exops, unbinds and abandons. Handle those first. if (req.protocolOp === Protocol.LDAP_REQ_EXTENSION) { - route = routes[req.requestName]; + route = routes[req.requestName] if (route) { return { backend: route.backend, handlers: (route[op] ? route[op] : [noExOpHandler]) - }; + } } else { return { backend: self, handlers: [noExOpHandler] - }; + } } } else if (req.protocolOp === Protocol.LDAP_REQ_UNBIND) { - route = routes['unbind']; + route = routes.unbind if (route) { return { backend: route.backend, handlers: route[op] - }; + } } else { return { backend: self, handlers: [defaultNoOpHandler] - }; + } } } else if (req.protocolOp === Protocol.LDAP_REQ_ABANDON) { return { backend: self, handlers: [defaultNoOpHandler] - }; + } } // Otherwise, match via DN rules - assert.ok(req.dn); - var keys = this._sortedRouteKeys(); - var fallbackHandler = [noSuffixHandler]; + assert.ok(req.dn) + var keys = this._sortedRouteKeys() + var fallbackHandler = [noSuffixHandler] // invalid DNs in non-strict mode are routed to the default handler - var testDN = (typeof (req.dn) === 'string') ? '' : req.dn; + var testDN = (typeof (req.dn) === 'string') ? '' : req.dn for (var i = 0; i < keys.length; i++) { - var suffix = keys[i]; - route = routes[suffix]; - assert.ok(route.dn); + var suffix = keys[i] + route = routes[suffix] + assert.ok(route.dn) // Match a valid route or the route wildcard ('') if (route.dn.equals(testDN) || route.dn.parentOf(testDN) || suffix === '') { if (route[op]) { // We should be good to go. - req.suffix = route.dn; + req.suffix = route.dn return { backend: route.backend, handlers: route[op] - }; + } } else { if (suffix === '') { - break; + break } else { // We found a valid suffix but not a valid operation. // There might be a more generic suffix with a legitimate operation. - fallbackHandler = [defaultHandler]; + fallbackHandler = [defaultHandler] } } } @@ -884,34 +843,31 @@ Server.prototype._getHandlerChain = function _getHandlerChain(req, res) { return { backend: self, handlers: fallbackHandler - }; -}; - + } +} Server.prototype._mount = function (op, name, argv, notDN) { - assert.ok(op); - assert.ok(name !== undefined); - assert.ok(argv); + assert.ok(op) + assert.ok(name !== undefined) + assert.ok(argv) - if (typeof (name) !== 'string') - throw new TypeError('name (string) required'); - if (!argv.length) - throw new Error('at least one handler required'); + if (typeof (name) !== 'string') { throw new TypeError('name (string) required') } + if (!argv.length) { throw new Error('at least one handler required') } - var backend = this; - var index = 0; + var backend = this + var index = 0 if (typeof (argv[0]) === 'object' && !Array.isArray(argv[0])) { - backend = argv[0]; - index = 1; + backend = argv[0] + index = 1 } - var route = this._getRoute(notDN ? name : dn.parse(name), backend); + var route = this._getRoute(notDN ? name : dn.parse(name), backend) - var chain = this._chain.slice(); + var chain = this._chain.slice() argv.slice(index).forEach(function (a) { - chain.push(a); - }); - route['0x' + op.toString(16)] = mergeFunctionArgs(chain); + chain.push(a) + }) + route['0x' + op.toString(16)] = mergeFunctionArgs(chain) - return this; -}; + return this +} diff --git a/lib/url.js b/lib/url.js index 29fa14e..6a12575 100644 --- a/lib/url.js +++ b/lib/url.js @@ -1,67 +1,72 @@ -// Copyright 2011 Mark Cavage, Inc. All rights reserved. - -var querystring = require('querystring'); -var url = require('url'); -var util = require('util'); - -var dn = require('./dn'); -var filter = require('./filters/index'); +'use strict' +const querystring = require('querystring') +const url = require('url') +const dn = require('./dn') +const filter = require('./filters/') module.exports = { parse: function (urlStr, parseDN) { - var u = url.parse(urlStr); - if (!u.protocol || !(u.protocol === 'ldap:' || u.protocol === 'ldaps:')) - throw new TypeError(urlStr + ' is an invalid LDAP url (protocol)'); + let parsedURL + try { + 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) - u.hostname = 'localhost'; + const u = { + 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) { - u.port = (u.secure ? 636 : 389); + u.port = (u.secure ? 636 : 389) } else { - u.port = parseInt(u.port, 10); + u.port = parseInt(u.port, 10) } if (u.pathname) { - u.pathname = querystring.unescape(u.pathname.substr(1)); - u.DN = parseDN ? dn.parse(u.pathname) : u.pathname; + u.pathname = querystring.unescape(u.pathname.substr(1)) + u.DN = parseDN ? dn.parse(u.pathname) : u.pathname } if (u.search) { - u.attributes = []; - var tmp = u.search.substr(1).split('?'); + u.attributes = [] + var tmp = u.search.substr(1).split('?') if (tmp && tmp.length) { if (tmp[0]) { tmp[0].split(',').forEach(function (a) { - u.attributes.push(querystring.unescape(a.trim())); - }); + u.attributes.push(querystring.unescape(a.trim())) + }) } } if (tmp[1]) { - if (tmp[1] !== 'base' && tmp[1] !== 'one' && tmp[1] !== 'sub') - throw new TypeError(urlStr + ' is an invalid LDAP url (scope)'); - u.scope = tmp[1]; + if (tmp[1] !== 'base' && tmp[1] !== 'one' && tmp[1] !== 'sub') { throw new TypeError(urlStr + ' is an invalid LDAP url (scope)') } + u.scope = tmp[1] } if (tmp[2]) { - u.filter = querystring.unescape(tmp[2]); + u.filter = querystring.unescape(tmp[2]) } if (tmp[3]) { - u.extensions = querystring.unescape(tmp[3]); + u.extensions = querystring.unescape(tmp[3]) } - if (!u.scope) - u.scope = 'base'; - if (!u.filter) - u.filter = filter.parseString('(objectclass=*)'); - else - u.filter = filter.parseString(u.filter); + if (!u.scope) { u.scope = 'base' } + if (!u.filter) { u.filter = filter.parseString('(objectclass=*)') } else { u.filter = filter.parseString(u.filter) } } - return u; + return u } -}; +} diff --git a/package.json b/package.json index 5ba3257..468dc27 100644 --- a/package.json +++ b/package.json @@ -1,55 +1,53 @@ { - "author": "Mark Cavage ", - "contributors": [ - "Craig Baker", - "Austin King ", - "Mathieu Lecarme >", - "Trent Mick ", - "Yunong Xiao ", - "Denis Vuyka ", - "Pedro Palazón ", - "Patrick Mooney ", - "Matt Simerson " - ], + "originalAuthor": "Mark Cavage ", "name": "ldapjs", "homepage": "http://ldapjs.org", "description": "LDAP client and server APIs", - "version": "1.0.2", + "version": "2.0.0", "license": "MIT", "repository": { "type": "git", - "url": "git://github.com/mcavage/node-ldapjs.git" + "url": "git://github.com/ldapjs/node-ldapjs.git" }, "main": "lib/index.js", "directories": { - "bin": "./bin", "lib": "./lib" }, "engines": { - "node": ">=0.10" + "node": ">=10.13.0" }, "dependencies": { - "asn1": "0.2.3", + "abstract-logging": "^1.0.0", + "asn1": "^0.2.4", "assert-plus": "^1.0.0", - "bunyan": "^1.8.3", - "dashdash": "^1.14.0", "backoff": "^2.5.0", - "ldap-filter": "0.2.2", + "ldap-filter": "^0.3.3", "once": "^1.4.0", - "vasync": "^1.6.4", + "vasync": "^2.2.0", "verror": "^1.8.1" }, - "optionalDependencies": { - "dtrace-provider": "~0.8" - }, "devDependencies": { - "node-uuid": "^1.4.7", - "faucet": "0.0.1", - "istanbul": "^0.4.5", - "tape": "^4.6.2" + "get-port": "^5.1.1", + "husky": "^3.0.4", + "snazzy": "^8.0.0", + "standard": "^14.0.2", + "tap": "14.10.1", + "uuid": "^3.3.3" }, "scripts": { - "report": "./node_modules/.bin/istanbul report html && open ./coverage/lcov-report/index.html", - "test": "./node_modules/.bin/istanbul cover --print none test/test.js | ./node_modules/.bin/faucet" + "test": "tap --no-cov", + "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" + } } } diff --git a/test-integration/client/connect.test.js b/test-integration/client/connect.test.js new file mode 100644 index 0000000..1a82339 --- /dev/null +++ b/test-integration/client/connect.test.js @@ -0,0 +1,21 @@ +'use strict' + +const tap = require('tap') +const ldapjs = require('../../lib') + +const SCHEME = process.env.SCHEME || 'ldap' +const HOST = process.env.HOST || '127.0.0.1' +const PORT = process.env.PORT || 389 + +const baseURL = `${SCHEME}://${HOST}:${PORT}` + +tap.test('connects to a server', t => { + t.plan(2) + + const client = ldapjs.createClient({ url: baseURL }) + client.bind('cn=Philip J. Fry,ou=people,dc=planetexpress,dc=com', 'fry', (err) => { + t.error(err) + t.pass() + client.unbind() + }) +}) diff --git a/test-integration/client/issues.test.js b/test-integration/client/issues.test.js new file mode 100644 index 0000000..4abcc3b --- /dev/null +++ b/test-integration/client/issues.test.js @@ -0,0 +1,91 @@ +'use strict' + +const tap = require('tap') +const ldapjs = require('../../lib') + +const SCHEME = process.env.SCHEME || 'ldap' +const HOST = process.env.HOST || '127.0.0.1' +const PORT = process.env.PORT || 389 + +const baseURL = `${SCHEME}://${HOST}:${PORT}` + +tap.test('modifyDN with long name (issue #480)', t => { + const longStr = 'a292979f2c86d513d48bbb9786b564b3c5228146e5ba46f404724e322544a7304a2b1049168803a5485e2d57a544c6a0d860af91330acb77e5907a9e601ad1227e80e0dc50abe963b47a004f2c90f570450d0e920d15436fdc771e3bdac0487a9735473ed3a79361d1778d7e53a7fb0e5f01f97a75ef05837d1d5496fc86968ff47fcb64' + const targetDN = 'cn=Turanga Leela,ou=people,dc=planetexpress,dc=com' + const client = ldapjs.createClient({ url: baseURL }) + client.bind('cn=admin,dc=planetexpress,dc=com', 'GoodNewsEveryone', bindHandler) + + function bindHandler (err) { + t.error(err) + client.modifyDN( + targetDN, + `cn=${longStr},ou=people,dc=planetexpress,dc=com`, + modifyHandler + ) + } + + function modifyHandler (err, res) { + t.error(err) + t.ok(res) + t.equal(res.status, 0) + + client.modifyDN( + `cn=${longStr},ou=people,dc=planetexpress,dc=com`, + targetDN, + (err, res) => { + t.error(err) + client.unbind(t.end) + } + ) + } +}) + +tap.test('whois works correctly (issue #370)', t => { + const client = ldapjs.createClient({ url: baseURL }) + client.bind('cn=Philip J. Fry,ou=people,dc=planetexpress,dc=com', 'fry', (err) => { + t.error(err) + + client.exop('1.3.6.1.4.1.4203.1.11.3', (err, value, res) => { + t.error(err) + t.ok(value) + t.is(value, 'dn:cn=Philip J. Fry,ou=people,dc=planetexpress,dc=com') + t.ok(res) + t.is(res.status, 0) + + client.unbind(t.end) + }) + }) +}) + +tap.test('can access large groups (issue #582)', t => { + const client = ldapjs.createClient({ url: baseURL }) + client.bind('cn=admin,dc=planetexpress,dc=com ', 'GoodNewsEveryone', (err) => { + t.error(err) + const searchOpts = { + scope: 'sub', + filter: '(&(objectClass=group)(cn=large_group))' + } + client.search('ou=large_ou,dc=planetexpress,dc=com', searchOpts, (err, response) => { + t.error(err) + + const results = [] + response.on('searchEntry', (entry) => { + results.push(entry) + }) + response.on('error', t.error) + response.on('end', (result) => { + t.is(result.status, 0) + t.is(results.length === 1, true) + t.ok(results[0].attributes) + + const memberAttr = results[0].attributes.find(a => a.type === 'member') + t.ok(memberAttr) + t.ok(memberAttr.vals) + t.type(memberAttr.vals, Array) + t.is(memberAttr.vals.length, 2000) + + client.unbind(t.end) + }) + }) + }) +}) diff --git a/test/attribute.test.js b/test/attribute.test.js index 21759d6..800529b 100644 --- a/test/attribute.test.js +++ b/test/attribute.test.js @@ -1,184 +1,163 @@ -// Copyright 2011 Mark Cavage, Inc. All rights reserved. - -var test = require('tape').test; - -var asn1 = require('asn1'); - - -///--- Globals - -var BerReader = asn1.BerReader; -var BerWriter = asn1.BerWriter; -var Attribute; - - -///--- Tests - -test('load library', function (t) { - Attribute = require('../lib/index').Attribute; - t.ok(Attribute); - t.end(); -}); +'use strict' +const { test } = require('tap') +const { BerReader, BerWriter } = require('asn1') +const { Attribute } = require('../lib') test('new no args', function (t) { - t.ok(new Attribute()); - t.end(); -}); - + t.ok(new Attribute()) + t.end() +}) test('new with args', function (t) { - var attr = new Attribute({ + let attr = new Attribute({ type: 'cn', vals: ['foo', 'bar'] - }); - t.ok(attr); - attr.addValue('baz'); - t.equal(attr.type, 'cn'); - t.equal(attr.vals.length, 3); - t.equal(attr.vals[0], 'foo'); - t.equal(attr.vals[1], 'bar'); - t.equal(attr.vals[2], 'baz'); + }) + t.ok(attr) + attr.addValue('baz') + t.equal(attr.type, 'cn') + t.equal(attr.vals.length, 3) + t.equal(attr.vals[0], 'foo') + t.equal(attr.vals[1], 'bar') + t.equal(attr.vals[2], 'baz') t.throws(function () { - attr = new Attribute('not an object'); - }); + attr = new Attribute('not an object') + }) t.throws(function () { - var typeThatIsNotAString = 1; + const typeThatIsNotAString = 1 attr = new Attribute({ type: typeThatIsNotAString - }); - }); - t.end(); -}); - + }) + }) + t.end() +}) test('toBer', function (t) { - var attr = new Attribute({ + const attr = new Attribute({ type: 'cn', vals: ['foo', 'bar'] - }); - t.ok(attr); - var ber = new BerWriter(); - attr.toBer(ber); - var reader = new BerReader(ber.buffer); - t.ok(reader.readSequence()); - t.equal(reader.readString(), 'cn'); - t.equal(reader.readSequence(), 0x31); // lber set - t.equal(reader.readString(), 'foo'); - t.equal(reader.readString(), 'bar'); - t.end(); -}); - + }) + t.ok(attr) + const ber = new BerWriter() + attr.toBer(ber) + const reader = new BerReader(ber.buffer) + t.ok(reader.readSequence()) + t.equal(reader.readString(), 'cn') + t.equal(reader.readSequence(), 0x31) // lber set + t.equal(reader.readString(), 'foo') + t.equal(reader.readString(), 'bar') + t.end() +}) test('parse', function (t) { - var ber = new BerWriter(); - ber.startSequence(); - ber.writeString('cn'); - ber.startSequence(0x31); - ber.writeStringArray(['foo', 'bar']); - ber.endSequence(); - ber.endSequence(); + const ber = new BerWriter() + ber.startSequence() + ber.writeString('cn') + ber.startSequence(0x31) + ber.writeStringArray(['foo', 'bar']) + ber.endSequence() + ber.endSequence() - var attr = new Attribute(); - t.ok(attr); - t.ok(attr.parse(new BerReader(ber.buffer))); + const attr = new Attribute() + t.ok(attr) + t.ok(attr.parse(new BerReader(ber.buffer))) - t.equal(attr.type, 'cn'); - t.equal(attr.vals.length, 2); - t.equal(attr.vals[0], 'foo'); - t.equal(attr.vals[1], 'bar'); - t.end(); -}); + t.equal(attr.type, 'cn') + t.equal(attr.vals.length, 2) + t.equal(attr.vals[0], 'foo') + t.equal(attr.vals[1], 'bar') + t.end() +}) test('parse - without 0x31', function (t) { - var ber = new BerWriter; - ber.startSequence(); - ber.writeString('sn'); - ber.endSequence(); + const ber = new BerWriter() + ber.startSequence() + ber.writeString('sn') + ber.endSequence() - var attr = new Attribute; - t.ok(attr); - t.ok(attr.parse(new BerReader(ber.buffer))); + const attr = new Attribute() + t.ok(attr) + t.ok(attr.parse(new BerReader(ber.buffer))) - t.equal(attr.type, 'sn'); - t.equal(attr.vals.length, 0); + t.equal(attr.type, 'sn') + t.equal(attr.vals.length, 0) - t.end(); -}); + t.end() +}) test('toString', function (t) { - var attr = new Attribute({ + const attr = new Attribute({ type: 'foobar', vals: ['asdf'] - }); - var expected = attr.toString(); - var actual = JSON.stringify(attr.json); - t.equal(actual, expected); - t.end(); -}); + }) + const expected = attr.toString() + const actual = JSON.stringify(attr.json) + t.equal(actual, expected) + t.end() +}) test('isAttribute', function (t) { - var isA = Attribute.isAttribute; - t.notOk(isA(null)); - t.notOk(isA('asdf')); + const isA = Attribute.isAttribute + t.notOk(isA(null)) + t.notOk(isA('asdf')) t.ok(isA(new Attribute({ type: 'foobar', vals: ['asdf'] - }))); + }))) t.ok(isA({ type: 'foo', - vals: ['item', new Buffer(5)], + vals: ['item', Buffer.alloc(5)], toBer: function () { /* placeholder */ } - })); + })) // bad type in vals t.notOk(isA({ type: 'foo', vals: ['item', null], toBer: function () { /* placeholder */ } - })); - - t.end(); -}); + })) + t.end() +}) test('compare', function (t) { - var comp = Attribute.compare; - var a = new Attribute({ + const comp = Attribute.compare + const a = new Attribute({ type: 'foo', vals: ['bar'] - }); - var b = new Attribute({ + }) + const b = new Attribute({ type: 'foo', vals: ['bar'] - }); - var notAnAttribute = 'this is not an attribute'; + }) + const notAnAttribute = 'this is not an attribute' t.throws(function () { - comp(a, notAnAttribute); - }); + comp(a, notAnAttribute) + }) t.throws(function () { - comp(notAnAttribute, b); - }); + comp(notAnAttribute, b) + }) - t.equal(comp(a, b), 0); + t.equal(comp(a, b), 0) // Different types - a.type = 'boo'; - t.equal(comp(a, b), -1); - t.equal(comp(b, a), 1); - a.type = 'foo'; + a.type = 'boo' + t.equal(comp(a, b), -1) + t.equal(comp(b, a), 1) + a.type = 'foo' // Different value counts - a.vals = ['bar', 'baz']; - t.equal(comp(a, b), 1); - t.equal(comp(b, a), -1); + a.vals = ['bar', 'baz'] + t.equal(comp(a, b), 1) + t.equal(comp(b, a), -1) // Different value contents (same count) - a.vals = ['baz']; - t.equal(comp(a, b), 1); - t.equal(comp(b, a), -1); + a.vals = ['baz'] + t.equal(comp(a, b), 1) + t.equal(comp(b, a), -1) - t.end(); -}); + t.end() +}) diff --git a/test/change.test.js b/test/change.test.js index a6ea7f5..829b8b6 100644 --- a/test/change.test.js +++ b/test/change.test.js @@ -1,260 +1,253 @@ -// Copyright 2011 Mark Cavage, Inc. All rights reserved. - -var test = require('tape').test; - -var asn1 = require('asn1'); - - -///--- Globals - -var BerReader = asn1.BerReader; -var BerWriter = asn1.BerWriter; -var Attribute; -var Change; - - -///--- Tests - -test('load library', function (t) { - Attribute = require('../lib/index').Attribute; - Change = require('../lib/index').Change; - t.ok(Attribute); - t.ok(Change); - t.end(); -}); +'use strict' +const fs = require('fs') +const path = require('path') +const { test } = require('tap') +const { BerReader, BerWriter } = require('asn1') +const { Attribute, Change } = require('../lib') test('new no args', function (t) { - t.ok(new Change()); - t.end(); -}); - + t.ok(new Change()) + t.end() +}) test('new with args', function (t) { - var change = new Change({ + const change = new Change({ operation: 'add', modification: new Attribute({ type: 'cn', vals: ['foo', 'bar'] }) - }); - t.ok(change); + }) + t.ok(change) - t.equal(change.operation, 'add'); - t.equal(change.modification.type, 'cn'); - t.equal(change.modification.vals.length, 2); - t.equal(change.modification.vals[0], 'foo'); - t.equal(change.modification.vals[1], 'bar'); + t.equal(change.operation, 'add') + t.equal(change.modification.type, 'cn') + t.equal(change.modification.vals.length, 2) + t.equal(change.modification.vals[0], 'foo') + t.equal(change.modification.vals[1], 'bar') - t.end(); -}); + t.end() +}) +test('new with args and buffer', function (t) { + var img = fs.readFileSync(path.join(__dirname, '/imgs/test.jpg')) + + var change = new Change({ + operation: 'add', + modification: { + thumbnailPhoto: img + } + }) + + t.ok(change) + + t.equal(change.operation, 'add') + t.equal(change.modification.type, 'thumbnailPhoto') + t.equal(change.modification.vals.length, 1) + t.equal(change.modification.buffers[0].compare(img), 0) + + t.end() +}) test('validate fields', function (t) { - var c = new Change(); - t.ok(c); + const c = new Change() + t.ok(c) t.throws(function () { - c.operation = 'bogus'; - }); + c.operation = 'bogus' + }) t.throws(function () { - c.modification = {too: 'many', fields: 'here'}; - }); + c.modification = { too: 'many', fields: 'here' } + }) c.modification = { foo: ['bar', 'baz'] - }; - t.ok(c.modification); - t.end(); -}); - + } + t.ok(c.modification) + t.end() +}) test('GH-31 (multiple attributes per Change)', function (t) { t.throws(function () { - var c = new Change({ + const c = new Change({ operation: 'replace', modification: { cn: 'foo', sn: 'bar' } - }); - t.notOk(c); - }); - t.end(); -}); - + }) + t.notOk(c) + }) + t.end() +}) test('toBer', function (t) { - var change = new Change({ + const change = new Change({ operation: 'Add', modification: new Attribute({ type: 'cn', vals: ['foo', 'bar'] }) - }); - t.ok(change); - - var ber = new BerWriter(); - change.toBer(ber); - var reader = new BerReader(ber.buffer); - t.ok(reader.readSequence()); - t.equal(reader.readEnumeration(), 0x00); - t.ok(reader.readSequence()); - t.equal(reader.readString(), 'cn'); - t.equal(reader.readSequence(), 0x31); // lber set - t.equal(reader.readString(), 'foo'); - t.equal(reader.readString(), 'bar'); - t.end(); -}); + }) + t.ok(change) + const ber = new BerWriter() + change.toBer(ber) + const reader = new BerReader(ber.buffer) + t.ok(reader.readSequence()) + t.equal(reader.readEnumeration(), 0x00) + t.ok(reader.readSequence()) + t.equal(reader.readString(), 'cn') + t.equal(reader.readSequence(), 0x31) // lber set + t.equal(reader.readString(), 'foo') + t.equal(reader.readString(), 'bar') + t.end() +}) test('parse', function (t) { - var ber = new BerWriter(); - ber.startSequence(); - ber.writeEnumeration(0x00); - ber.startSequence(); - ber.writeString('cn'); - ber.startSequence(0x31); - ber.writeStringArray(['foo', 'bar']); - ber.endSequence(); - ber.endSequence(); - ber.endSequence(); + const ber = new BerWriter() + ber.startSequence() + ber.writeEnumeration(0x00) + ber.startSequence() + ber.writeString('cn') + ber.startSequence(0x31) + ber.writeStringArray(['foo', 'bar']) + ber.endSequence() + ber.endSequence() + ber.endSequence() - var change = new Change(); - t.ok(change); - t.ok(change.parse(new BerReader(ber.buffer))); + const change = new Change() + t.ok(change) + t.ok(change.parse(new BerReader(ber.buffer))) - t.equal(change.operation, 'add'); - t.equal(change.modification.type, 'cn'); - t.equal(change.modification.vals.length, 2); - t.equal(change.modification.vals[0], 'foo'); - t.equal(change.modification.vals[1], 'bar'); - - t.end(); -}); + t.equal(change.operation, 'add') + t.equal(change.modification.type, 'cn') + t.equal(change.modification.vals.length, 2) + t.equal(change.modification.vals[0], 'foo') + t.equal(change.modification.vals[1], 'bar') + t.end() +}) test('apply - replace', function (t) { - var res; - var single = new Change({ + let res + const single = new Change({ operation: 'replace', modification: { type: 'cn', vals: ['new'] } - }); - var twin = new Change({ + }) + const twin = new Change({ operation: 'replace', - modification: { - type: 'cn', - vals: ['new', 'two'] - } - }); - var empty = new Change({ + modification: { + type: 'cn', + vals: ['new', 'two'] + } + }) + const empty = new Change({ operation: 'replace', modification: { type: 'cn', vals: [] } - }); + }) // plain - res = Change.apply(single, { cn: ['old'] }); - t.deepEqual(res.cn, ['new']); + res = Change.apply(single, { cn: ['old'] }) + t.deepEqual(res.cn, ['new']) // multiple - res = Change.apply(single, { cn: ['old', 'also'] }); - t.deepEqual(res.cn, ['new']); + res = Change.apply(single, { cn: ['old', 'also'] }) + t.deepEqual(res.cn, ['new']) // empty - res = Change.apply(empty, { cn: ['existing'] }); - t.equal(res.cn, undefined); - t.ok(Object.keys(res).indexOf('cn') === -1); + res = Change.apply(empty, { cn: ['existing'] }) + t.equal(res.cn, undefined) + t.ok(Object.keys(res).indexOf('cn') === -1) - //absent - res = Change.apply(single, { dn: ['otherjunk'] }); - t.deepEqual(res.cn, ['new']); + // absent + res = Change.apply(single, { dn: ['otherjunk'] }) + t.deepEqual(res.cn, ['new']) // scalar formatting "success" - res = Change.apply(single, { cn: 'old' }, true); - t.equal(res.cn, 'new'); + res = Change.apply(single, { cn: 'old' }, true) + t.equal(res.cn, 'new') // scalar formatting "failure" - res = Change.apply(twin, { cn: 'old' }, true); - t.deepEqual(res.cn, ['new', 'two']); - - t.end(); -}); + res = Change.apply(twin, { cn: 'old' }, true) + t.deepEqual(res.cn, ['new', 'two']) + t.end() +}) test('apply - add', function (t) { - var res; - var single = new Change({ + let res + const single = new Change({ operation: 'add', modification: { type: 'cn', vals: ['new'] } - }); + }) // plain - res = Change.apply(single, { cn: ['old'] }); - t.deepEqual(res.cn, ['old', 'new']); + res = Change.apply(single, { cn: ['old'] }) + t.deepEqual(res.cn, ['old', 'new']) // multiple - res = Change.apply(single, { cn: ['old', 'also'] }); - t.deepEqual(res.cn, ['old', 'also', 'new']); + res = Change.apply(single, { cn: ['old', 'also'] }) + t.deepEqual(res.cn, ['old', 'also', 'new']) - //absent - res = Change.apply(single, { dn: ['otherjunk'] }); - t.deepEqual(res.cn, ['new']); + // absent + res = Change.apply(single, { dn: ['otherjunk'] }) + t.deepEqual(res.cn, ['new']) // scalar formatting "success" - res = Change.apply(single, { }, true); - t.equal(res.cn, 'new'); + res = Change.apply(single, { }, true) + t.equal(res.cn, 'new') // scalar formatting "failure" - res = Change.apply(single, { cn: 'old' }, true); - t.deepEqual(res.cn, ['old', 'new']); + res = Change.apply(single, { cn: 'old' }, true) + t.deepEqual(res.cn, ['old', 'new']) // duplicate add - res = Change.apply(single, { cn: 'new' }); - t.deepEqual(res.cn, ['new']); - - t.end(); -}); + res = Change.apply(single, { cn: 'new' }) + t.deepEqual(res.cn, ['new']) + t.end() +}) test('apply - delete', function (t) { - var res; - var single = new Change({ + let res + const single = new Change({ operation: 'delete', modification: { type: 'cn', vals: ['old'] } - }); + }) // plain - res = Change.apply(single, { cn: ['old', 'new'] }); - t.deepEqual(res.cn, ['new']); + res = Change.apply(single, { cn: ['old', 'new'] }) + t.deepEqual(res.cn, ['new']) // empty - res = Change.apply(single, { cn: ['old'] }); - t.equal(res.cn, undefined); - t.ok(Object.keys(res).indexOf('cn') === -1); + res = Change.apply(single, { cn: ['old'] }) + t.equal(res.cn, undefined) + t.ok(Object.keys(res).indexOf('cn') === -1) // scalar formatting "success" - res = Change.apply(single, { cn: ['old', 'one'] }, true); - t.equal(res.cn, 'one'); + res = Change.apply(single, { cn: ['old', 'one'] }, true) + t.equal(res.cn, 'one') // scalar formatting "failure" - res = Change.apply(single, { cn: ['old', 'several', 'items'] }, true); - t.deepEqual(res.cn, ['several', 'items']); + res = Change.apply(single, { cn: ['old', 'several', 'items'] }, true) + t.deepEqual(res.cn, ['several', 'items']) + // absent + res = Change.apply(single, { dn: ['otherjunk'] }) + t.ok(res) + t.equal(res.cn, undefined) - //absent - res = Change.apply(single, { dn: ['otherjunk'] }); - t.ok(res); - t.equal(res.cn, undefined); - - t.end(); -}); + t.end() +}) diff --git a/test/client.test.js b/test/client.test.js index 0683fed..eed6c39 100644 --- a/test/client.test.js +++ b/test/client.test.js @@ -1,124 +1,101 @@ -// Copyright 2011 Mark Cavage, Inc. All rights reserved. +'use strict' -var Logger = require('bunyan'); +const util = require('util') +const assert = require('assert') +const tap = require('tap') +const uuid = require('uuid') +const vasync = require('vasync') +const getPort = require('get-port') +const { getSock } = require('./utils') +const ldap = require('../lib') +const { Attribute, Change } = ldap -var test = require('tape').test; -var uuid = require('node-uuid'); -var vasync = require('vasync'); -var util = require('util'); +const SUFFIX = 'dc=test' +const LDAP_CONNECT_TIMEOUT = process.env.LDAP_CONNECT_TIMEOUT || 0 +const BIND_DN = 'cn=root' +const BIND_PW = 'secret' +tap.beforeEach((done, t) => { + t.context.socketPath = getSock() + t.context.server = ldap.createServer() -///--- Globals - -var BIND_DN = 'cn=root'; -var BIND_PW = 'secret'; -var SOCKET = '/tmp/.' + uuid(); - -var SUFFIX = 'dc=test'; - -var LOG = new Logger({ - name: 'ldapjs_unit_test', - stream: process.stderr, - level: (process.env.LOG_LEVEL || 'info'), - serializers: Logger.stdSerializers, - src: true -}); - -var ldap; -var Attribute; -var Change; -var client; -var server; - - -///--- Tests - -test('setup', function (t) { - ldap = require('../lib/index'); - t.ok(ldap); - t.ok(ldap.createClient); - t.ok(ldap.createServer); - t.ok(ldap.Attribute); - t.ok(ldap.Change); - - Attribute = ldap.Attribute; - Change = ldap.Change; - - server = ldap.createServer(); - t.ok(server); - + const server = t.context.server server.bind(BIND_DN, function (req, res, next) { - if (req.credentials !== BIND_PW) - return next(new ldap.InvalidCredentialsError('Invalid password')); + if (req.credentials !== BIND_PW) { return next(new ldap.InvalidCredentialsError('Invalid password')) } - res.end(); - return next(); - }); + res.end() + return next() + }) server.add(SUFFIX, function (req, res, next) { - res.end(); - return next(); - }); + res.end() + return next() + }) server.compare(SUFFIX, function (req, res, next) { - res.end(req.value === 'test'); - return next(); - }); + res.end(req.value === 'test') + return next() + }) server.del(SUFFIX, function (req, res, next) { - res.end(); - return next(); - }); + res.end() + return next() + }) // LDAP whoami server.exop('1.3.6.1.4.1.4203.1.11.3', function (req, res, next) { - res.value = 'u:xxyyz@EXAMPLE.NET'; - res.end(); - return next(); - }); + res.value = 'u:xxyyz@EXAMPLE.NET' + res.end() + return next() + }) server.modify(SUFFIX, function (req, res, next) { - res.end(); - return next(); - }); + res.end() + return next() + }) server.modifyDN(SUFFIX, function (req, res, next) { - res.end(); - return next(); - }); + res.end() + return next() + }) + + server.modifyDN('cn=issue-480', function (req, res, next) { + assert(req.newRdn.toString().length > 132) + res.end() + return next() + }) server.search('dc=slow', function (req, res, next) { res.send({ dn: 'dc=slow', attributes: { - 'you': 'wish', - 'this': 'was', - 'faster': '.' + you: 'wish', + this: 'was', + faster: '.' } - }); + }) setTimeout(function () { - res.end(); - next(); - }, 250); - }); + res.end() + next() + }, 250) + }) server.search('dc=timeout', function (req, res, next) { - // Haha client! - }); + // Cause the client to timeout by not sending a response. + }) server.search(SUFFIX, function (req, res, next) { - if (req.dn.equals('cn=ref,' + SUFFIX)) { - res.send(res.createSearchReference('ldap://localhost')); + res.send(res.createSearchReference('ldap://localhost')) } else if (req.dn.equals('cn=bin,' + SUFFIX)) { res.send(res.createSearchEntry({ objectName: req.dn, attributes: { 'foo;binary': 'wr0gKyDCvCA9IMK+', - 'gb18030': new Buffer([0xB5, 0xE7, 0xCA, 0xD3, 0xBB, 0xFA]), - 'objectclass': 'binary' + gb18030: Buffer.from([0xB5, 0xE7, 0xCA, 0xD3, 0xBB, 0xFA]), + objectclass: 'binary' } - })); + })) } else { var e = res.createSearchEntry({ objectName: req.dn, @@ -126,25 +103,23 @@ test('setup', function (t) { cn: ['unit', 'test'], SN: 'testy' } - }); - res.send(e); - res.send(e); + }) + res.send(e) + res.send(e) } - - res.end(); - return next(); - }); + res.end() + return next() + }) server.search('cn=sizelimit', function (req, res, next) { - var sizeLimit = 200; - var i; - for (i = 0; i < 1000; i++) { + const sizeLimit = 200 + for (let i = 0; i < 1000; i++) { if (req.sizeLimit > 0 && i >= req.sizeLimit) { - break; + break } else if (i > sizeLimit) { - res.end(ldap.LDAP_SIZE_LIMIT_EXCEEDED); - return next(); + res.end(ldap.LDAP_SIZE_LIMIT_EXCEEDED) + return next() } res.send({ dn: util.format('o=%d, cn=sizelimit', i), @@ -152,20 +127,20 @@ test('setup', function (t) { o: [i], objectclass: ['pagedResult'] } - }); + }) } - res.end(); - return next(); - }); + res.end() + return next() + }) server.search('cn=paged', function (req, res, next) { - var min = 0; - var max = 1000; + const min = 0 + const max = 1000 - function sendResults(start, end) { - start = (start < min) ? min : start; - end = (end > max || end < min) ? max : end; - var i; + function sendResults (start, end) { + start = (start < min) ? min : start + end = (end > max || end < min) ? max : end + let i for (i = start; i < end; i++) { res.send({ dn: util.format('o=%d, cn=paged', i), @@ -173,78 +148,134 @@ test('setup', function (t) { o: [i], objectclass: ['pagedResult'] } - }); + }) } - return i; + return i } - var cookie = null; - var pageSize = 0; + let cookie = null + let pageSize = 0 req.controls.forEach(function (control) { if (control.type === ldap.PagedResultsControl.OID) { - pageSize = control.value.size; - cookie = control.value.cookie; + pageSize = control.value.size + cookie = control.value.cookie } - }); + }) if (cookie && Buffer.isBuffer(cookie)) { // Do simple paging - var first = min; + let first = min if (cookie.length !== 0) { - first = parseInt(cookie.toString(), 10); + first = parseInt(cookie.toString(), 10) } - var last = sendResults(first, first + pageSize); + const last = sendResults(first, first + pageSize) - var resultCookie; + let resultCookie if (last < max) { - resultCookie = new Buffer(last.toString()); + resultCookie = Buffer.from(last.toString()) } else { - resultCookie = new Buffer(''); + resultCookie = Buffer.from('') } res.controls.push(new ldap.PagedResultsControl({ value: { size: pageSize, // correctness not required here cookie: resultCookie } - })); - res.end(); - next(); + })) + res.end() + next() } else { // don't allow non-paged searches for this test endpoint - next(new ldap.UnwillingToPerformError()); + next(new ldap.UnwillingToPerformError()) } - }); + }) + server.search('cn=sssvlv', function (req, res, next) { + const min = 0 + const max = 100 + const results = [] + let o = 'aa' + for (let i = min; i < max; i++) { + results.push({ + dn: util.format('o=%s, cn=sssvlv', o), + attributes: { + o: [o], + objectclass: ['sssvlvResult'] + } + }) + o = ((parseInt(o, 36) + 1).toString(36)).replace(/0/g, 'a') + } + function sendResults (start, end, sortBy, sortDesc) { + start = (start < min) ? min : start + end = (end > max || end < min) ? max : end + const sorted = results.sort((a, b) => { + if (a.attributes[sortBy][0] < b.attributes[sortBy][0]) { + return sortDesc ? 1 : -1 + } else if (a.attributes[sortBy][0] > b.attributes[sortBy][0]) { + return sortDesc ? -1 : 1 + } + return 0 + }) + for (let i = start; i < end; i++) { + res.send(sorted[i]) + } + } + let sortBy = null + let sortDesc = null + let afterCount = null + let targetOffset = null + req.controls.forEach(function (control) { + if (control.type === ldap.ServerSideSortingRequestControl.OID) { + sortBy = control.value[0].attributeType + sortDesc = control.value[0].reverseOrder + } + if (control.type === ldap.VirtualListViewRequestControl.OID) { + afterCount = control.value.afterCount + targetOffset = control.value.targetOffset + } + }) + if (sortBy) { + if (afterCount && targetOffset) { + sendResults(targetOffset - 1, (targetOffset + afterCount), sortBy, sortDesc) + } else { + sendResults(min, max, sortBy, sortDesc) + } + res.end() + next() + } else { + next(new ldap.UnwillingToPerformError()) + } + }) server.search('cn=pagederr', function (req, res, next) { - var cookie = null; + let cookie = null req.controls.forEach(function (control) { if (control.type === ldap.PagedResultsControl.OID) { - cookie = control.value.cookie; + cookie = control.value.cookie } - }); + }) if (cookie && Buffer.isBuffer(cookie) && cookie.length === 0) { // send first "page" - res.send({ - dn: util.format('o=result, cn=pagederr'), - attributes: { - o: 'result', - objectclass: ['pagedResult'] - } - }); - res.controls.push(new ldap.PagedResultsControl({ - value: { - size: 2, - cookie: new Buffer('a') - } - })); - res.end(); - return next(); + res.send({ + dn: util.format('o=result, cn=pagederr'), + attributes: { + o: 'result', + objectclass: ['pagedResult'] + } + }) + res.controls.push(new ldap.PagedResultsControl({ + value: { + size: 2, + cookie: Buffer.from('a') + } + })) + res.end() + return next() } else { // send error instead of second page - res.end(ldap.LDAP_SIZE_LIMIT_EXCEEDED); - return next(); + res.end(ldap.LDAP_SIZE_LIMIT_EXCEEDED) + return next() } - }); + }) server.search('dc=empty', function (req, res, next) { res.send({ @@ -253,14 +284,14 @@ test('setup', function (t) { member: [], 'member;range=0-1': ['cn=user1, dc=empty', 'cn=user2, dc=empty'] } - }); - res.end(); - return next(); - }); + }) + res.end() + return next() + }) server.search('cn=busy', function (req, res, next) { - next(new ldap.BusyError('too much to do')); - }); + next(new ldap.BusyError('too much to do')) + }) server.search('', function (req, res, next) { if (req.dn.toString() === '') { @@ -269,253 +300,316 @@ test('setup', function (t) { attributes: { objectclass: ['RootDSE', 'top'] } - }); - res.end(); + }) + res.end() } else { // Turn away any other requests (since '' is the fallthrough route) - res.errorMessage = 'No tree found for: ' + req.dn.toString(); - res.end(ldap.LDAP_NO_SUCH_OBJECT); + res.errorMessage = 'No tree found for: ' + req.dn.toString() + res.end(ldap.LDAP_NO_SUCH_OBJECT) } - return next(); - }); + return next() + }) server.unbind(function (req, res, next) { - res.end(); - return next(); - }); + res.end() + return next() + }) - server.listen(SOCKET, function () { - client = ldap.createClient({ - connectTimeout: parseInt(process.env.LDAP_CONNECT_TIMEOUT || 0, 10), - socketPath: SOCKET, - log: LOG - }); - t.ok(client); - t.end(); - }); + server.listen(t.context.socketPath, function () { + const client = ldap.createClient({ + connectTimeout: parseInt(LDAP_CONNECT_TIMEOUT, 10), + socketPath: t.context.socketPath + }) + t.context.client = client + client.on('connect', () => done()) + }) +}) -}); +tap.afterEach((done, t) => { + t.context.client.unbind((err) => { + t.error(err) + t.context.server.close(() => done()) + }) +}) +tap.test('createClient', t => { + t.test('requires an options object', async t => { + const match = /options.+required/ + t.throws(() => ldap.createClient(), match) + t.throws(() => ldap.createClient([]), match) + t.throws(() => ldap.createClient(''), match) + t.throws(() => ldap.createClient(42), match) + }) -test('simple bind failure', function (t) { - client.bind(BIND_DN, uuid(), function (err, res) { - t.ok(err); - t.notOk(res); + t.test('url must be a string', async t => { + const match = /options\.url \(string\) required/ + t.throws(() => ldap.createClient({ url: {} }), match) + t.throws(() => ldap.createClient({ url: [] }), match) + t.throws(() => ldap.createClient({ url: 42 }), match) + }) - t.ok(err instanceof ldap.InvalidCredentialsError); - t.ok(err instanceof Error); - t.ok(err.dn); - t.ok(err.message); - t.ok(err.stack); + t.test('socketPath must be a string', async t => { + const match = /options\.socketPath must be a string/ + t.throws(() => ldap.createClient({ socketPath: {} }), match) + t.throws(() => ldap.createClient({ socketPath: [] }), match) + t.throws(() => ldap.createClient({ socketPath: 42 }), match) + }) - t.end(); - }); -}); + t.test('cannot supply both url and socketPath', async t => { + t.throws( + () => ldap.createClient({ url: 'foo', socketPath: 'bar' }), + /options\.url \^ options\.socketPath \(String\) required/ + ) + }) + t.test('must supply at least url or socketPath', async t => { + t.throws( + () => ldap.createClient({}), + /options\.url \^ options\.socketPath \(String\) required/ + ) + }) -test('simple bind success', function (t) { - client.bind(BIND_DN, BIND_PW, function (err, res) { - t.ifError(err); - t.ok(res); - t.equal(res.status, 0); - t.end(); - }); -}); + tap.test('exception from bad createClient parameter (issue #418)', t => { + try { + // This port number is totally invalid. It will cause the URL parser + // to throw an exception that should be caught. + ldap.createClient({ url: 'ldap://127.0.0.1:13891389' }) + } catch (error) { + t.ok(error) + t.end() + } + }) + // TODO: this test is really flaky. It would be better if we could validate + // the options _withouth_ having to connect to a server. + // t.test('attaches a child function to logger', async t => { + // /* eslint-disable-next-line */ + // let client + // const logger = Object.create(require('abstract-logging')) + // const socketPath = getSock() + // const server = ldap.createServer() + // server.listen(socketPath, () => {}) + // t.tearDown(() => { + // client.unbind(() => server.close()) + // }) -test('simple anonymous bind (empty credentials)', function (t) { - client.bind('', '', function (err, res) { - t.ifError(err); - t.ok(res); - t.equal(res.status, 0); - t.end(); - }); -}); + // client = ldap.createClient({ socketPath, log: logger }) + // t.ok(logger.child) + // t.true(typeof client.log.child === 'function') + // }) + t.end() +}) -test('auto-bind bad credentials', function (t) { - var clt = ldap.createClient({ - socketPath: SOCKET, +tap.test('simple bind failure', function (t) { + t.context.client.bind(BIND_DN, uuid(), function (err, res) { + t.ok(err) + t.notOk(res) + + t.ok(err instanceof ldap.InvalidCredentialsError) + t.ok(err instanceof Error) + t.ok(err.dn) + t.ok(err.message) + t.ok(err.stack) + + t.end() + }) +}) + +tap.test('simple bind success', function (t) { + t.context.client.bind(BIND_DN, BIND_PW, function (err, res) { + t.error(err) + t.ok(res) + t.equal(res.status, 0) + t.end() + }) +}) + +tap.test('simple anonymous bind (empty credentials)', function (t) { + t.context.client.bind('', '', function (err, res) { + t.error(err) + t.ok(res) + t.equal(res.status, 0) + t.end() + }) +}) + +tap.test('auto-bind bad credentials', function (t) { + const clt = ldap.createClient({ + socketPath: t.context.socketPath, bindDN: BIND_DN, - bindCredentials: 'totallybogus', - log: LOG - }); + bindCredentials: 'totallybogus' + }) clt.once('error', function (err) { - t.equal(err.code, ldap.LDAP_INVALID_CREDENTIALS); - clt.destroy(); - t.end(); - }); -}); + t.equal(err.code, ldap.LDAP_INVALID_CREDENTIALS) + t.ok(clt._socket.destroyed, 'expect socket to be destroyed') + clt.destroy() + t.end() + }) +}) - -test('auto-bind success', function (t) { - var clt = ldap.createClient({ - socketPath: SOCKET, +tap.test('auto-bind success', function (t) { + const clt = ldap.createClient({ + socketPath: t.context.socketPath, bindDN: BIND_DN, - bindCredentials: BIND_PW, - log: LOG - }); + bindCredentials: BIND_PW + }) clt.once('connect', function () { - t.ok(clt); - clt.destroy(); - t.end(); - }); -}); + t.ok(clt) + clt.destroy() + t.end() + }) +}) - -test('add success', function (t) { - var attrs = [ +tap.test('add success', function (t) { + const attrs = [ new Attribute({ type: 'cn', vals: ['test'] }) - ]; - client.add('cn=add, ' + SUFFIX, attrs, function (err, res) { - t.ifError(err); - t.ok(res); - t.equal(res.status, 0); - t.end(); - }); -}); + ] + t.context.client.add('cn=add, ' + SUFFIX, attrs, function (err, res) { + t.error(err) + t.ok(res) + t.equal(res.status, 0) + t.end() + }) +}) - -test('add success with object', function (t) { - var entry = { +tap.test('add success with object', function (t) { + const entry = { cn: ['unit', 'add'], sn: 'test' - }; - client.add('cn=add, ' + SUFFIX, entry, function (err, res) { - t.ifError(err); - t.ok(res); - t.equal(res.status, 0); - t.end(); - }); -}); + } + t.context.client.add('cn=add, ' + SUFFIX, entry, function (err, res) { + t.error(err) + t.ok(res) + t.equal(res.status, 0) + t.end() + }) +}) +tap.test('compare success', function (t) { + t.context.client.compare('cn=compare, ' + SUFFIX, 'cn', 'test', function (err, matched, res) { + t.error(err) + t.ok(matched) + t.ok(res) + t.end() + }) +}) -test('compare success', function (t) { - client.compare('cn=compare, ' + SUFFIX, 'cn', 'test', function (err, - matched, - res) { - t.ifError(err); - t.ok(matched); - t.ok(res); - t.end(); - }); -}); +tap.test('compare false', function (t) { + t.context.client.compare('cn=compare, ' + SUFFIX, 'cn', 'foo', function (err, matched, res) { + t.error(err) + t.notOk(matched) + t.ok(res) + t.end() + }) +}) +tap.test('compare bad suffix', function (t) { + t.context.client.compare('cn=' + uuid(), 'cn', 'foo', function (err, matched, res) { + t.ok(err) + t.ok(err instanceof ldap.NoSuchObjectError) + t.notOk(matched) + t.notOk(res) + t.end() + }) +}) -test('compare false', function (t) { - client.compare('cn=compare, ' + SUFFIX, 'cn', 'foo', function (err, - matched, - res) { - t.ifError(err); - t.notOk(matched); - t.ok(res); - t.end(); - }); -}); +tap.test('delete success', function (t) { + t.context.client.del('cn=delete, ' + SUFFIX, function (err, res) { + t.error(err) + t.ok(res) + t.end() + }) +}) - -test('compare bad suffix', function (t) { - client.compare('cn=' + uuid(), 'cn', 'foo', function (err, - matched, - res) { - t.ok(err); - t.ok(err instanceof ldap.NoSuchObjectError); - t.notOk(matched); - t.notOk(res); - t.end(); - }); -}); - - -test('delete success', function (t) { - client.del('cn=delete, ' + SUFFIX, function (err, res) { - t.ifError(err); - t.ok(res); - t.end(); - }); -}); - - -test('delete with control (GH-212)', function (t) { - var control = new ldap.Control({ +tap.test('delete with control (GH-212)', function (t) { + const control = new ldap.Control({ type: '1.2.3.4', criticality: false - }); - client.del('cn=delete, ' + SUFFIX, control, function (err, res) { - t.ifError(err); - t.ok(res); - t.end(); - }); -}); + }) + t.context.client.del('cn=delete, ' + SUFFIX, control, function (err, res) { + t.error(err) + t.ok(res) + t.end() + }) +}) +tap.test('exop success', function (t) { + t.context.client.exop('1.3.6.1.4.1.4203.1.11.3', function (err, value, res) { + t.error(err) + t.ok(value) + t.ok(res) + t.equal(value, 'u:xxyyz@EXAMPLE.NET') + t.end() + }) +}) -test('exop success', function (t) { - client.exop('1.3.6.1.4.1.4203.1.11.3', function (err, value, res) { - t.ifError(err); - t.ok(value); - t.ok(res); - t.equal(value, 'u:xxyyz@EXAMPLE.NET'); - t.end(); - }); -}); +tap.test('exop invalid', function (t) { + t.context.client.exop('1.2.3.4', function (err, res) { + t.ok(err) + t.ok(err instanceof ldap.ProtocolError) + t.notOk(res) + t.end() + }) +}) +tap.test('bogus exop (GH-17)', function (t) { + t.context.client.exop('cn=root', function (err) { + t.ok(err) + t.end() + }) +}) -test('exop invalid', function (t) { - client.exop('1.2.3.4', function (err, res) { - t.ok(err); - t.ok(err instanceof ldap.ProtocolError); - t.notOk(res); - t.end(); - }); -}); - - -test('bogus exop (GH-17)', function (t) { - client.exop('cn=root', function (err, value) { - t.ok(err); - t.end(); - }); -}); - - -test('modify success', function (t) { - var change = new Change({ +tap.test('modify success', function (t) { + const change = new Change({ type: 'Replace', modification: new Attribute({ type: 'cn', vals: ['test'] }) - }); - client.modify('cn=modify, ' + SUFFIX, change, function (err, res) { - t.ifError(err); - t.ok(res); - t.equal(res.status, 0); - t.end(); - }); -}); + }) + t.context.client.modify('cn=modify, ' + SUFFIX, change, function (err, res) { + t.error(err) + t.ok(res) + t.equal(res.status, 0) + t.end() + }) +}) - -test('modify change plain object success', function (t) { - var change = new Change({ +tap.test('modify change plain object success', function (t) { + const change = new Change({ type: 'Replace', modification: { cn: 'test' } - }); - client.modify('cn=modify, ' + SUFFIX, change, function (err, res) { - t.ifError(err); - t.ok(res); - t.equal(res.status, 0); - t.end(); - }); -}); + }) + t.context.client.modify('cn=modify, ' + SUFFIX, change, function (err, res) { + t.error(err) + t.ok(res) + t.equal(res.status, 0) + t.end() + }) +}) +// https://github.com/ldapjs/node-ldapjs/pull/435 +tap.test('can delete attributes', function (t) { + const change = new Change({ + type: 'Delete', + modification: { cn: null } + }) + t.context.client.modify('cn=modify,' + SUFFIX, change, function (err, res) { + t.error(err) + t.ok(res) + t.equal(res.status, 0) + t.end() + }) +}) -test('modify array success', function (t) { - var changes = [ +tap.test('modify array success', function (t) { + const changes = [ new Change({ operation: 'Replace', modification: new Attribute({ @@ -529,719 +623,917 @@ test('modify array success', function (t) { type: 'sn' }) }) - ]; - client.modify('cn=modify, ' + SUFFIX, changes, function (err, res) { - t.ifError(err); - t.ok(res); - t.equal(res.status, 0); - t.end(); - }); -}); + ] + t.context.client.modify('cn=modify, ' + SUFFIX, changes, function (err, res) { + t.error(err) + t.ok(res) + t.equal(res.status, 0) + t.end() + }) +}) - -test('modify change plain object success (GH-31)', function (t) { - var change = { +tap.test('modify change plain object success (GH-31)', function (t) { + const change = { type: 'replace', modification: { cn: 'test', sn: 'bar' } - }; - client.modify('cn=modify, ' + SUFFIX, change, function (err, res) { - t.ifError(err); - t.ok(res); - t.equal(res.status, 0); - t.end(); - }); -}); + } + t.context.client.modify('cn=modify, ' + SUFFIX, change, function (err, res) { + t.error(err) + t.ok(res) + t.equal(res.status, 0) + t.end() + }) +}) +tap.test('modify DN new RDN only', function (t) { + t.context.client.modifyDN('cn=old, ' + SUFFIX, 'cn=new', function (err, res) { + t.error(err) + t.ok(res) + t.equal(res.status, 0) + t.end() + }) +}) -test('modify DN new RDN only', function (t) { - client.modifyDN('cn=old, ' + SUFFIX, 'cn=new', function (err, res) { - t.ifError(err); - t.ok(res); - t.equal(res.status, 0); - t.end(); - }); -}); +tap.test('modify DN new superior', function (t) { + t.context.client.modifyDN('cn=old, ' + SUFFIX, 'cn=new, dc=foo', function (err, res) { + t.error(err) + t.ok(res) + t.equal(res.status, 0) + t.end() + }) +}) +tap.test('modify DN excessive length (GH-480)', function (t) { + t.context.client.modifyDN('cn=issue-480', 'cn=a292979f2c86d513d48bbb9786b564b3c5228146e5ba46f404724e322544a7304a2b1049168803a5485e2d57a544c6a0d860af91330acb77e5907a9e601ad1227e80e0dc50abe963b47a004f2c90f570450d0e920d15436fdc771e3bdac0487a9735473ed3a79361d1778d7e53a7fb0e5f01f97a75ef05837d1d5496fc86968ff47fcb64', function (err, res) { + t.error(err) + t.ok(res) + t.equal(res.status, 0) + t.end() + }) +}) -test('modify DN new superior', function (t) { - client.modifyDN('cn=old, ' + SUFFIX, 'cn=new, dc=foo', function (err, res) { - t.ifError(err); - t.ok(res); - t.equal(res.status, 0); - t.end(); - }); -}); - - -test('search basic', function (t) { - client.search('cn=test, ' + SUFFIX, '(objectclass=*)', function (err, res) { - t.ifError(err); - t.ok(res); - var gotEntry = 0; +tap.test('search basic', function (t) { + t.context.client.search('cn=test, ' + SUFFIX, '(objectclass=*)', function (err, res) { + t.error(err) + t.ok(res) + let gotEntry = 0 res.on('searchEntry', function (entry) { - t.ok(entry); - t.ok(entry instanceof ldap.SearchEntry); - t.equal(entry.dn.toString(), 'cn=test, ' + SUFFIX); - t.ok(entry.attributes); - t.ok(entry.attributes.length); - t.equal(entry.attributes[0].type, 'cn'); - t.equal(entry.attributes[1].type, 'SN'); - t.ok(entry.object); - gotEntry++; - }); + t.ok(entry) + t.ok(entry instanceof ldap.SearchEntry) + t.equal(entry.dn.toString(), 'cn=test, ' + SUFFIX) + t.ok(entry.attributes) + t.ok(entry.attributes.length) + t.equal(entry.attributes[0].type, 'cn') + t.equal(entry.attributes[1].type, 'SN') + t.ok(entry.object) + gotEntry++ + }) res.on('error', function (err) { - t.fail(err); - }); + t.fail(err) + }) res.on('end', function (res) { - t.ok(res); - t.ok(res instanceof ldap.SearchResponse); - t.equal(res.status, 0); - t.equal(gotEntry, 2); - t.end(); - }); - }); -}); + t.ok(res) + t.ok(res instanceof ldap.SearchResponse) + t.equal(res.status, 0) + t.equal(gotEntry, 2) + t.end() + }) + }) +}) +tap.test('GH-602 search basic with delayed event listener binding', function (t) { + t.context.client.search('cn=test, ' + SUFFIX, '(objectclass=*)', function (err, res) { + t.error(err) + setTimeout(() => { + let gotEntry = 0 + res.on('searchEntry', function (entry) { + gotEntry++ + }) + res.on('error', function (err) { + t.fail(err) + }) + res.on('end', function (res) { + t.equal(gotEntry, 2) + t.end() + }) + }, 100) + }) +}) -test('search sizeLimit', function (t) { +tap.test('search sizeLimit', function (t) { t.test('over limit', function (t2) { - client.search('cn=sizelimit', {}, function (err, res) { - t2.ifError(err); + t.context.client.search('cn=sizelimit', {}, function (err, res) { + t2.error(err) res.on('error', function (error) { - t2.equal(error.name, 'SizeLimitExceededError'); - t2.end(); - }); - }); - }); + t2.equal(error.name, 'SizeLimitExceededError') + t2.end() + }) + }) + }) t.test('under limit', function (t2) { - var limit = 100; - client.search('cn=sizelimit', {sizeLimit: limit}, function (err, res) { - t2.ifError(err); - var count = 0; + const limit = 100 + t.context.client.search('cn=sizelimit', { sizeLimit: limit }, function (err, res) { + t2.error(err) + let count = 0 res.on('searchEntry', function (entry) { - count++; - }); + count++ + }) res.on('end', function () { - t2.pass(); - t2.equal(count, limit); - t2.end(); - }); - res.on('error', t2.ifError.bind(t)); - }); - }); -}); + t2.pass() + t2.equal(count, limit) + t2.end() + }) + res.on('error', t2.error.bind(t)) + }) + }) + t.end() +}) -test('search paged', function (t) { +tap.test('search paged', { timeout: 10000 }, function (t) { t.test('paged - no pauses', function (t2) { - var countEntries = 0; - var countPages = 0; - client.search('cn=paged', {paged: {pageSize: 100}}, function (err, res) { - t2.ifError(err); - res.on('searchEntry', function () { - countEntries++; - }); - res.on('page', function () { - countPages++; - }); - res.on('error', t2.ifError.bind(t2)); + let countEntries = 0 + let countPages = 0 + t.context.client.search('cn=paged', { paged: { pageSize: 100 } }, function (err, res) { + t2.error(err) + res.on('searchEntry', entryListener) + res.on('page', pageListener) + res.on('error', (err) => t2.error(err)) res.on('end', function () { - t2.equal(countEntries, 1000); - t2.equal(countPages, 10); - t2.end(); - }); - }); - }); + t2.equal(countEntries, 1000) + t2.equal(countPages, 10) + t2.end() + }) + + t2.tearDown(() => { + res.removeListener('searchEntry', entryListener) + res.removeListener('page', pageListener) + }) + + function entryListener () { + countEntries += 1 + } + + function pageListener () { + countPages += 1 + } + }) + }) t.test('paged - pauses', function (t2) { - var countPages = 0; - client.search('cn=paged', { + let countPages = 0 + t.context.client.search('cn=paged', { paged: { pageSize: 100, pagePause: true } }, function (err, res) { - t2.ifError(err); - res.on('page', function (result, cb) { - countPages++; + t2.error(err) + res.on('page', pageListener) + res.on('error', (err) => t2.error(err)) + res.on('end', function () { + t2.equal(countPages, 9) + t2.end() + }) + + function pageListener (result, cb) { + countPages++ // cancel after 9 to verify callback usage if (countPages === 9) { // another page should never be encountered - res.removeAllListeners('page') - .on('page', t2.fail.bind(null, 'unexpected page')); - return cb(new Error()); + res.removeListener('page', pageListener) + .on('page', t2.fail.bind(null, 'unexpected page')) + return cb(new Error()) } - return cb(); - }); - res.on('error', t2.ifError.bind(t2)); - res.on('end', function () { - t2.equal(countPages, 9); - t2.end(); - }); - }); - }); + return cb() + } + }) + }) t.test('paged - no support (err handled)', function (t2) { - client.search(SUFFIX, { + t.context.client.search(SUFFIX, { paged: { pageSize: 100 } }, function (err, res) { - t2.ifError(err); - res.on('pageError', t2.ok.bind(t2)); + t2.error(err) + res.on('pageError', t2.ok.bind(t2)) res.on('end', function () { - t2.pass(); - t2.end(); - }); - }); - }); + t2.pass() + t2.end() + }) + }) + }) t.test('paged - no support (err not handled)', function (t2) { - client.search(SUFFIX, { + t.context.client.search(SUFFIX, { paged: { pageSize: 100 } }, function (err, res) { - t2.ifError(err); - res.on('end', t2.fail.bind(t2)); + t2.error(err) + res.on('end', t2.fail.bind(t2)) res.on('error', function (error) { - t2.ok(error); - t2.end(); - }); - }); - }); + t2.ok(error) + t2.end() + }) + }) + }) t.test('paged - redundant control', function (t2) { try { - client.search(SUFFIX, { + t.context.client.search(SUFFIX, { paged: { pageSize: 100 } }, new ldap.PagedResultsControl(), - function (err, res) { - t2.fail(); - }); + function (err) { + t.error(err) + t2.fail() + }) } catch (e) { - t2.ok(e); - t2.end(); + t2.ok(e) + t2.end() } - }); + }) t.test('paged - handle later error', function (t2) { - var countEntries = 0; - var countPages = 0; - client.search('cn=pagederr', { + let countEntries = 0 + let countPages = 0 + t.context.client.search('cn=pagederr', { paged: { pageSize: 1 } }, function (err, res) { - t2.ifError(err); + t2.error(err) res.on('searchEntry', function () { - t2.ok(++countEntries); - }); + t2.ok(++countEntries) + }) res.on('page', function () { - t2.ok(++countPages); - }); + t2.ok(++countPages) + }) res.on('error', function (error) { - t2.equal(countEntries, 1); - t2.equal(countPages, 1); - t2.end(); - }); + t2.ok(error) + t2.equal(countEntries, 1) + t2.equal(countPages, 1) + t2.end() + }) res.on('end', function () { - t2.fail('should not be reached'); - }); - }); - }); + t2.fail('should not be reached') + }) + }) + }) - t.end(); -}); + t.end() +}) +tap.test('search - sssvlv', { timeout: 10000 }, function (t) { + t.test('ssv - asc', function (t2) { + let preventry = null + const sssrcontrol = new ldap.ServerSideSortingRequestControl( + { + value: { + attributeType: 'o', + orderingRule: 'caseIgnoreOrderingMatch', + reverseOrder: false + } + } + ) + t.context.client.search('cn=sssvlv', {}, sssrcontrol, function (err, res) { + t2.error(err) + res.on('searchEntry', function (entry) { + t2.ok(entry) + t2.ok(entry instanceof ldap.SearchEntry) + t2.ok(entry.attributes) + t2.ok(entry.attributes.length) + if (preventry != null) { + t2.ok(entry.attributes[0]._vals[0] >= preventry.attributes[0]._vals[0]) + } + preventry = entry + }) + res.on('error', (err) => t2.error(err)) + res.on('end', function () { + t2.end() + }) + }) + }) + t.test('ssv - desc', function (t2) { + let preventry = null + const sssrcontrol = new ldap.ServerSideSortingRequestControl( + { + value: { + attributeType: 'o', + orderingRule: 'caseIgnoreOrderingMatch', + reverseOrder: true + } + } + ) + t.context.client.search('cn=sssvlv', {}, sssrcontrol, function (err, res) { + t2.error(err) + res.on('searchEntry', function (entry) { + t2.ok(entry) + t2.ok(entry instanceof ldap.SearchEntry) + t2.ok(entry.attributes) + t2.ok(entry.attributes.length) + if (preventry != null) { + t2.ok(entry.attributes[0]._vals[0] <= preventry.attributes[0]._vals[0]) + } + preventry = entry + }) + res.on('error', (err) => t2.error(err)) + res.on('end', function () { + t2.end() + }) + }) + }) -test('search referral', function (t) { - client.search('cn=ref, ' + SUFFIX, '(objectclass=*)', function (err, res) { - t.ifError(err); - t.ok(res); - var gotEntry = 0; - var gotReferral = false; + t.test('vlv - first page', function (t2) { + const sssrcontrol = new ldap.ServerSideSortingRequestControl( + { + value: { + attributeType: 'o', + orderingRule: 'caseIgnoreOrderingMatch', + reverseOrder: false + } + } + ) + const vlvrcontrol = new ldap.VirtualListViewRequestControl( + { + value: { + beforeCount: 0, + afterCount: 9, + targetOffset: 1, + contentCount: 0 + } + } + ) + let count = 0 + let preventry = null + t.context.client.search('cn=sssvlv', {}, [sssrcontrol, vlvrcontrol], function (err, res) { + t2.error(err) + res.on('searchEntry', function (entry) { + t2.ok(entry) + t2.ok(entry instanceof ldap.SearchEntry) + t2.ok(entry.attributes) + t2.ok(entry.attributes.length) + if (preventry != null) { + t2.ok(entry.attributes[0]._vals[0] >= preventry.attributes[0]._vals[0]) + } + preventry = entry + count++ + }) + res.on('error', (err) => t2.error(err)) + res.on('end', function (result) { + t2.equals(count, 10) + t2.end() + }) + }) + }) + t.test('vlv - last page', function (t2) { + const sssrcontrol = new ldap.ServerSideSortingRequestControl( + { + value: { + attributeType: 'o', + orderingRule: 'caseIgnoreOrderingMatch', + reverseOrder: false + } + } + ) + const vlvrcontrol = new ldap.VirtualListViewRequestControl( + { + value: { + beforeCount: 0, + afterCount: 9, + targetOffset: 91, + contentCount: 0 + } + } + ) + let count = 0 + let preventry = null + t.context.client.search('cn=sssvlv', {}, [sssrcontrol, vlvrcontrol], function (err, res) { + t2.error(err) + res.on('searchEntry', function (entry) { + t2.ok(entry) + t2.ok(entry instanceof ldap.SearchEntry) + t2.ok(entry.attributes) + t2.ok(entry.attributes.length) + if (preventry != null) { + t2.ok(entry.attributes[0]._vals[0] >= preventry.attributes[0]._vals[0]) + } + preventry = entry + count++ + }) + res.on('error', (err) => t2.error(err)) + res.on('end', function (result) { + t2.equals(count, 10) + t2.end() + }) + }) + }) + t.end() +}) + +tap.test('search referral', function (t) { + t.context.client.search('cn=ref, ' + SUFFIX, '(objectclass=*)', function (err, res) { + t.error(err) + t.ok(res) + let gotEntry = 0 + let gotReferral = false res.on('searchEntry', function (entry) { - gotEntry++; - }); + gotEntry++ + }) res.on('searchReference', function (referral) { - gotReferral = true; - t.ok(referral); - t.ok(referral instanceof ldap.SearchReference); - t.ok(referral.uris); - t.ok(referral.uris.length); - }); + gotReferral = true + t.ok(referral) + t.ok(referral instanceof ldap.SearchReference) + t.ok(referral.uris) + t.ok(referral.uris.length) + }) res.on('error', function (err) { - t.fail(err); - }); + t.fail(err) + }) res.on('end', function (res) { - t.ok(res); - t.ok(res instanceof ldap.SearchResponse); - t.equal(res.status, 0); - t.equal(gotEntry, 0); - t.ok(gotReferral); - t.end(); - }); - }); -}); + t.ok(res) + t.ok(res instanceof ldap.SearchResponse) + t.equal(res.status, 0) + t.equal(gotEntry, 0) + t.ok(gotReferral) + t.end() + }) + }) +}) - -test('search rootDSE', function (t) { - client.search('', '(objectclass=*)', function (err, res) { - t.ifError(err); - t.ok(res); +tap.test('search rootDSE', function (t) { + t.context.client.search('', '(objectclass=*)', function (err, res) { + t.error(err) + t.ok(res) res.on('searchEntry', function (entry) { - t.ok(entry); - t.equal(entry.dn.toString(), ''); - t.ok(entry.attributes); - t.ok(entry.object); - }); + t.ok(entry) + t.equal(entry.dn.toString(), '') + t.ok(entry.attributes) + t.ok(entry.object) + }) res.on('error', function (err) { - t.fail(err); - }); + t.fail(err) + }) res.on('end', function (res) { - t.ok(res); - t.ok(res instanceof ldap.SearchResponse); - t.equal(res.status, 0); - t.end(); - }); - }); -}); + t.ok(res) + t.ok(res instanceof ldap.SearchResponse) + t.equal(res.status, 0) + t.end() + }) + }) +}) - -test('search empty attribute', function (t) { - client.search('dc=empty', '(objectclass=*)', function (err, res) { - t.ifError(err); - t.ok(res); - var gotEntry = 0; +tap.test('search empty attribute', function (t) { + t.context.client.search('dc=empty', '(objectclass=*)', function (err, res) { + t.error(err) + t.ok(res) + let gotEntry = 0 res.on('searchEntry', function (entry) { - var obj = entry.toObject(); - t.equal('dc=empty', obj.dn); - t.ok(obj.member); - t.equal(obj.member.length, 0); - t.ok(obj['member;range=0-1']); - t.ok(obj['member;range=0-1'].length); - gotEntry++; - }); + var obj = entry.toObject() + t.equal('dc=empty', obj.dn) + t.ok(obj.member) + t.equal(obj.member.length, 0) + t.ok(obj['member;range=0-1']) + t.ok(obj['member;range=0-1'].length) + gotEntry++ + }) res.on('error', function (err) { - t.fail(err); - }); + t.fail(err) + }) res.on('end', function (res) { - t.ok(res); - t.ok(res instanceof ldap.SearchResponse); - t.equal(res.status, 0); - t.equal(gotEntry, 1); - t.end(); - }); - }); -}); + t.ok(res) + t.ok(res instanceof ldap.SearchResponse) + t.equal(res.status, 0) + t.equal(gotEntry, 1) + t.end() + }) + }) +}) - -test('GH-21 binary attributes', function (t) { - client.search('cn=bin, ' + SUFFIX, '(objectclass=*)', function (err, res) { - t.ifError(err); - t.ok(res); - var gotEntry = 0; - var expect = new Buffer('\u00bd + \u00bc = \u00be', 'utf8'); - var expect2 = new Buffer([0xB5, 0xE7, 0xCA, 0xD3, 0xBB, 0xFA]); +tap.test('GH-21 binary attributes', function (t) { + t.context.client.search('cn=bin, ' + SUFFIX, '(objectclass=*)', function (err, res) { + t.error(err) + t.ok(res) + let gotEntry = 0 + const expect = Buffer.from('\u00bd + \u00bc = \u00be', 'utf8') + const expect2 = Buffer.from([0xB5, 0xE7, 0xCA, 0xD3, 0xBB, 0xFA]) res.on('searchEntry', function (entry) { - t.ok(entry); - t.ok(entry instanceof ldap.SearchEntry); - t.equal(entry.dn.toString(), 'cn=bin, ' + SUFFIX); - t.ok(entry.attributes); - t.ok(entry.attributes.length); - t.equal(entry.attributes[0].type, 'foo;binary'); - t.equal(entry.attributes[0].vals[0], expect.toString('base64')); + t.ok(entry) + t.ok(entry instanceof ldap.SearchEntry) + t.equal(entry.dn.toString(), 'cn=bin, ' + SUFFIX) + t.ok(entry.attributes) + t.ok(entry.attributes.length) + t.equal(entry.attributes[0].type, 'foo;binary') + t.equal(entry.attributes[0].vals[0], expect.toString('base64')) t.equal(entry.attributes[0].buffers[0].toString('base64'), - expect.toString('base64')); + expect.toString('base64')) - t.ok(entry.attributes[1].type, 'gb18030'); - t.equal(entry.attributes[1].buffers.length, 1); - t.equal(expect2.length, entry.attributes[1].buffers[0].length); - for (var i = 0; i < expect2.length; i++) - t.equal(expect2[i], entry.attributes[1].buffers[0][i]); + t.ok(entry.attributes[1].type, 'gb18030') + t.equal(entry.attributes[1].buffers.length, 1) + t.equal(expect2.length, entry.attributes[1].buffers[0].length) + for (var i = 0; i < expect2.length; i++) { t.equal(expect2[i], entry.attributes[1].buffers[0][i]) } - t.ok(entry.object); - gotEntry++; - }); + t.ok(entry.object) + gotEntry++ + }) res.on('error', function (err) { - t.fail(err); - }); + t.fail(err) + }) res.on('end', function (res) { - t.ok(res); - t.ok(res instanceof ldap.SearchResponse); - t.equal(res.status, 0); - t.equal(gotEntry, 1); - t.end(); - }); - }); -}); + t.ok(res) + t.ok(res instanceof ldap.SearchResponse) + t.equal(res.status, 0) + t.equal(gotEntry, 1) + t.end() + }) + }) +}) - -test('GH-23 case insensitive attribute filtering', function (t) { - var opts = { +tap.test('GH-23 case insensitive attribute filtering', function (t) { + const opts = { filter: '(objectclass=*)', attributes: ['Cn'] - }; - client.search('cn=test, ' + SUFFIX, opts, function (err, res) { - t.ifError(err); - t.ok(res); - var gotEntry = 0; + } + t.context.client.search('cn=test, ' + SUFFIX, opts, function (err, res) { + t.error(err) + t.ok(res) + let gotEntry = 0 res.on('searchEntry', function (entry) { - t.ok(entry); - t.ok(entry instanceof ldap.SearchEntry); - t.equal(entry.dn.toString(), 'cn=test, ' + SUFFIX); - t.ok(entry.attributes); - t.ok(entry.attributes.length); - t.equal(entry.attributes[0].type, 'cn'); - t.ok(entry.object); - gotEntry++; - }); + t.ok(entry) + t.ok(entry instanceof ldap.SearchEntry) + t.equal(entry.dn.toString(), 'cn=test, ' + SUFFIX) + t.ok(entry.attributes) + t.ok(entry.attributes.length) + t.equal(entry.attributes[0].type, 'cn') + t.ok(entry.object) + gotEntry++ + }) res.on('error', function (err) { - t.fail(err); - }); + t.fail(err) + }) res.on('end', function (res) { - t.ok(res); - t.ok(res instanceof ldap.SearchResponse); - t.equal(res.status, 0); - t.equal(gotEntry, 2); - t.end(); - }); - }); -}); + t.ok(res) + t.ok(res instanceof ldap.SearchResponse) + t.equal(res.status, 0) + t.equal(gotEntry, 2) + t.end() + }) + }) +}) - -test('GH-24 attribute selection of *', function (t) { - var opts = { +tap.test('GH-24 attribute selection of *', function (t) { + const opts = { filter: '(objectclass=*)', attributes: ['*'] - }; - client.search('cn=test, ' + SUFFIX, opts, function (err, res) { - t.ifError(err); - t.ok(res); - var gotEntry = 0; - res.on('searchEntry', function (entry) { - t.ok(entry); - t.ok(entry instanceof ldap.SearchEntry); - t.equal(entry.dn.toString(), 'cn=test, ' + SUFFIX); - t.ok(entry.attributes); - t.ok(entry.attributes.length); - t.equal(entry.attributes[0].type, 'cn'); - t.equal(entry.attributes[1].type, 'SN'); - t.ok(entry.object); - gotEntry++; - }); - res.on('error', function (err) { - t.fail(err); - }); - res.on('end', function (res) { - t.ok(res); - t.ok(res instanceof ldap.SearchResponse); - t.equal(res.status, 0); - t.equal(gotEntry, 2); - t.end(); - }); - }); -}); - - -test('idle timeout', function (t) { - client.idleTimeout = 250; - function premature() { - t.ifError(true); } - client.on('idle', premature); - client.search('dc=slow', 'objectclass=*', function (err, res) { - t.ifError(err); - res.on('searchEntry', function (res) { - t.ok(res); - }); + t.context.client.search('cn=test, ' + SUFFIX, opts, function (err, res) { + t.error(err) + t.ok(res) + let gotEntry = 0 + res.on('searchEntry', function (entry) { + t.ok(entry) + t.ok(entry instanceof ldap.SearchEntry) + t.equal(entry.dn.toString(), 'cn=test, ' + SUFFIX) + t.ok(entry.attributes) + t.ok(entry.attributes.length) + t.equal(entry.attributes[0].type, 'cn') + t.equal(entry.attributes[1].type, 'SN') + t.ok(entry.object) + gotEntry++ + }) res.on('error', function (err) { - t.ifError(err); - }); + t.fail(err) + }) + res.on('end', function (res) { + t.ok(res) + t.ok(res instanceof ldap.SearchResponse) + t.equal(res.status, 0) + t.equal(gotEntry, 2) + t.end() + }) + }) +}) + +tap.test('idle timeout', function (t) { + t.context.client.idleTimeout = 250 + function premature () { + t.error(true) + } + t.context.client.on('idle', premature) + t.context.client.search('dc=slow', 'objectclass=*', function (err, res) { + t.error(err) + res.on('searchEntry', function (res) { + t.ok(res) + }) + res.on('error', function (err) { + t.error(err) + }) res.on('end', function () { var late = setTimeout(function () { - t.ifError(false, 'too late'); - }, 500); + t.fail('too late') + }, 500) // It's ok to go idle now - client.removeListener('idle', premature); - client.on('idle', function () { - clearTimeout(late); - client.removeAllListeners('idle'); - client.idleTimeout = 0; - t.end(); - }); - }); - }); -}); + t.context.client.removeListener('idle', premature) + t.context.client.on('idle', function () { + clearTimeout(late) + t.context.client.removeAllListeners('idle') + t.context.client.idleTimeout = 0 + t.end() + }) + }) + }) +}) - -test('setup action', function (t) { - var setupClient = ldap.createClient({ - connectTimeout: parseInt(process.env.LDAP_CONNECT_TIMEOUT || 0, 10), - socketPath: SOCKET, - log: LOG - }); +tap.test('setup action', function (t) { + const setupClient = ldap.createClient({ + connectTimeout: parseInt(LDAP_CONNECT_TIMEOUT, 10), + socketPath: t.context.socketPath + }) setupClient.on('setup', function (clt, cb) { clt.bind(BIND_DN, BIND_PW, function (err, res) { - t.ifError(err); - cb(err); - }); - }); - setupClient.search(SUFFIX, {scope: 'base'}, function (err, res) { - t.ifError(err); - t.ok(res); + t.error(err) + cb(err) + }) + }) + setupClient.search(SUFFIX, { scope: 'base' }, function (err, res) { + t.error(err) + t.ok(res) res.on('end', function () { - setupClient.destroy(); - t.end(); - }); - }); -}); + setupClient.destroy() + t.end() + }) + }) +}) - -test('setup reconnect', function (t) { - var rClient = ldap.createClient({ - connectTimeout: parseInt(process.env.LDAP_CONNECT_TIMEOUT || 0, 10), - socketPath: SOCKET, - reconnect: true, - log: LOG - }); +tap.test('setup reconnect', function (t) { + const rClient = ldap.createClient({ + connectTimeout: parseInt(LDAP_CONNECT_TIMEOUT, 10), + socketPath: t.context.socketPath, + reconnect: true + }) rClient.on('setup', function (clt, cb) { clt.bind(BIND_DN, BIND_PW, function (err, res) { - t.ifError(err); - cb(err); - }); - }); + t.error(err) + cb(err) + }) + }) - function doSearch(_, cb) { - rClient.search(SUFFIX, {scope: 'base'}, function (err, res) { - t.ifError(err); + function doSearch (_, cb) { + rClient.search(SUFFIX, { scope: 'base' }, function (err, res) { + t.error(err) res.on('end', function () { - cb(); - }); - }); + cb() + }) + }) } + vasync.pipeline({ funcs: [ doSearch, - function cleanDisconnect(_, cb) { - t.ok(rClient.connected); - rClient.once('close', function (had_err) { - t.ifError(had_err); - t.equal(rClient.connected, false); - cb(); - }); - rClient.unbind(); + function cleanDisconnect (_, cb) { + t.ok(rClient.connected) + rClient.once('close', function (err) { + t.error(err) + t.equal(rClient.connected, false) + cb() + }) + rClient.unbind() }, doSearch, - function simulateError(_, cb) { - var msg = 'fake socket error'; + function simulateError (_, cb) { + const msg = 'fake socket error' rClient.once('error', function (err) { - t.equal(err.message, msg); - t.ok(err); - }); - rClient.once('close', function (had_err) { + t.equal(err.message, msg) + t.ok(err) + }) + rClient.once('close', function () { // can't test had_err because the socket error is being faked - cb(); - }); - rClient._socket.emit('error', new Error(msg)); + cb() + }) + rClient._socket.emit('error', new Error(msg)) }, doSearch ] - }, function (err, res) { - t.ifError(err); - rClient.destroy(); - t.end(); - }); -}); + }, function (err) { + t.error(err) + rClient.destroy() + t.end() + }) +}) - -test('setup abort', function (t) { - var setupClient = ldap.createClient({ - connectTimeout: parseInt(process.env.LDAP_CONNECT_TIMEOUT || 0, 10), - socketPath: SOCKET, - reconnect: true, - log: LOG - }); - var message = 'It\'s a trap!'; +tap.test('setup abort', function (t) { + const setupClient = ldap.createClient({ + connectTimeout: parseInt(LDAP_CONNECT_TIMEOUT, 10), + socketPath: t.context.socketPath, + reconnect: true + }) + const message = "It's a trap!" setupClient.on('setup', function (clt, cb) { // simulate failure - t.ok(clt); - cb(new Error(message)); - }); + t.ok(clt) + cb(new Error(message)) + }) setupClient.on('setupError', function (err) { - t.ok(true); - t.equal(err.message, message); - setupClient.destroy(); - t.end(); - }); -}); + t.ok(true) + t.equal(err.message, message) + setupClient.destroy() + t.end() + }) +}) - -test('abort reconnect', function (t) { - var abortClient = ldap.createClient({ - connectTimeout: parseInt(process.env.LDAP_CONNECT_TIMEOUT || 0, 10), - socketPath: '/dev/null', - reconnect: true, - log: LOG - }); - var retryCount = 0; +tap.test('abort reconnect', function (t) { + const abortClient = ldap.createClient({ + connectTimeout: parseInt(LDAP_CONNECT_TIMEOUT, 10), + socketPath: 'an invalid path', + reconnect: true + }) + var retryCount = 0 abortClient.on('connectError', function () { - ++retryCount; - }); + ++retryCount + }) abortClient.once('connectError', function () { - t.ok(true); + t.ok(true) abortClient.once('destroy', function () { - t.ok(retryCount < 3); - t.end(); - }); - abortClient.destroy(); - }); -}); + t.ok(retryCount < 3) + t.end() + }) + abortClient.destroy() + }) +}) - -test('reconnect max retries', function (t) { - var RETRIES = 5; - var rClient = ldap.createClient({ +tap.test('reconnect max retries', function (t) { + const RETRIES = 5 + const rClient = ldap.createClient({ connectTimeout: 100, - socketPath: '/dev/null', + socketPath: 'an invalid path', reconnect: { failAfter: RETRIES, // Keep the test duration low initialDelay: 10, maxDelay: 100 - }, - log: LOG - }); - var count = 0; + } + }) + let count = 0 rClient.on('connectError', function () { - count++; - }); + count++ + }) rClient.on('error', function (err) { - t.equal(count, RETRIES); - rClient.destroy(); - t.end(); - }); -}); + t.ok(err) + t.equal(count, RETRIES) + rClient.destroy() + t.end() + }) +}) - -test('reconnect on server close', function (t) { - var clt = ldap.createClient({ - socketPath: SOCKET, - reconnect: true, - log: LOG - }); +tap.test('reconnect on server close', function (t) { + const clt = ldap.createClient({ + socketPath: t.context.socketPath, + reconnect: true + }) clt.on('setup', function (sclt, cb) { sclt.bind(BIND_DN, BIND_PW, function (err, res) { - t.ifError(err); - cb(err); - }); - }); + t.error(err) + cb(err) + }) + }) clt.once('connect', function () { - t.ok(clt._socket); + t.ok(clt._socket) clt.once('connect', function () { - t.ok(true, 'successful reconnect'); - clt.destroy(); - t.end(); - }); + t.ok(true, 'successful reconnect') + clt.destroy() + t.end() + }) // Simulate server-side close - clt._socket.destroy(); - }); -}); + clt._socket.destroy() + }) +}) - -test('no auto-reconnect on unbind', function (t) { - var clt = ldap.createClient({ - socketPath: SOCKET, - reconnect: true, - log: LOG - }); +tap.test('no auto-reconnect on unbind', function (t) { + const clt = ldap.createClient({ + socketPath: t.context.socketPath, + reconnect: true + }) clt.on('setup', function (sclt, cb) { sclt.bind(BIND_DN, BIND_PW, function (err, res) { - t.ifError(err); - cb(err); - }); - }); + t.error(err) + cb(err) + }) + }) clt.once('connect', function () { clt.once('connect', function () { - t.ifError(new Error('client should not reconnect')); - }); + t.error(new Error('client should not reconnect')) + }) clt.once('close', function () { - t.ok(true, 'initial close'); + t.ok(true, 'initial close') setImmediate(function () { - t.ok(!clt.connected, 'should not be connected'); - t.ok(!clt.connecting, 'should not be connecting'); - clt.destroy(); - t.end(); - }); - }); + t.ok(!clt.connected, 'should not be connected') + t.ok(!clt.connecting, 'should not be connecting') + clt.destroy() + t.end() + }) + }) - clt.unbind(); - }); -}); + clt.unbind() + }) +}) - -test('abandon (GH-27)', function (t) { +tap.test('abandon (GH-27)', function (t) { // FIXME: test abandoning a real request - client.abandon(401876543, function (err) { - t.ifError(err); - t.end(); - }); -}); + t.context.client.abandon(401876543, function (err) { + t.error(err) + t.end() + }) +}) - -test('search timeout (GH-51)', function (t) { - client.timeout = 250; - client.search('dc=timeout', 'objectclass=*', function (err, res) { - t.ifError(err); +tap.test('search timeout (GH-51)', function (t) { + t.context.client.timeout = 250 + t.context.client.search('dc=timeout', 'objectclass=*', function (err, res) { + t.error(err) res.on('error', function () { - t.end(); - }); - }); -}); + t.end() + }) + }) +}) +tap.test('resultError handling', function (t) { + const client = t.context.client + vasync.pipeline({ funcs: [errSearch, cleanSearch] }, function (err) { + t.error(err) + client.removeListener('resultError', error1) + client.removeListener('resultError', error2) + t.end() + }) -test('resultError handling', function (t) { - t.plan(3); - vasync.pipeline({ - funcs: [ - function errSearch(_, cb) { - client.once('resultError', function (error) { - t.equal(error.name, 'BusyError'); - }); - client.search('cn=busy', {}, function (err, res) { - res.once('error', function (error) { - t.equal(error.name, 'BusyError'); - cb(); - }); - }); - }, - function cleanSearch(_, cb) { - client.on('resultError', t.ifError.bind(null)); - client.search(SUFFIX, {}, function (err, res) { - res.once('end', function () { - t.ok(true); - cb(); - }); - }); + function errSearch (_, cb) { + client.once('resultError', error1) + client.search('cn=busy', {}, function (err, res) { + t.error(err) + res.once('error', function (error) { + t.equal(error.name, 'BusyError') + cb() + }) + }) + } + + function cleanSearch (_, cb) { + client.on('resultError', error2) + client.search(SUFFIX, {}, function (err, res) { + t.error(err) + res.once('end', function () { + t.pass() + cb() + }) + }) + } + + function error1 (error) { + t.equal(error.name, 'BusyError') + } + + function error2 () { + t.fail('should not get error') + } +}) + +tap.test('connection refused', function (t) { + getPort().then(function (unusedPortNumber) { + const client = ldap.createClient({ + url: `ldap://0.0.0.0:${unusedPortNumber}` + }) + + client.bind('cn=root', 'secret', function (err, res) { + t.true(err) + t.type(err, Error) + t.equals(err.code, 'ECONNREFUSED') + t.false(res) + t.end() + }) + }) +}) + +tap.test('connection timeout', function (t) { + getPort().then(function (unusedPortNumber) { + const client = ldap.createClient({ + url: `ldap://example.org:${unusedPortNumber}`, + connectTimeout: 1, + timeout: 1 + }) + + var done = false + + setTimeout(function () { + if (!done) { + throw new Error('LDAPJS waited for the server for too long') } - ] - }, function (err, res) { - client.removeAllListeners('resultError'); - }); -}); + }, 2000) - -test('unbind (GH-30)', function (t) { - client.unbind(function (err) { - t.ifError(err); - t.end(); - }); -}); - - -test('shutdown', function (t) { - server.on('close', function () { - t.end(); - }); - server.close(); -}); + client.bind('cn=root', 'secret', function (err, res) { + t.true(err) + t.type(err, Error) + t.equals(err.message, 'connection timeout') + done = true + t.false(res) + t.end() + }) + }) +}) diff --git a/test/controls/control.test.js b/test/controls/control.test.js index 86bdfac..6175974 100644 --- a/test/controls/control.test.js +++ b/test/controls/control.test.js @@ -1,76 +1,53 @@ -// Copyright 2011 Mark Cavage, Inc. All rights reserved. - -var test = require('tape').test; - -var asn1 = require('asn1'); - - -///--- Globals - -var BerReader = asn1.BerReader; -var BerWriter = asn1.BerWriter; -var Control; -var getControl; - - -///--- Tests - -test('load library', function (t) { - Control = require('../../lib/index').Control; - t.ok(Control); - getControl = require('../../lib/index').getControl; - t.ok(getControl); - t.end(); -}); +'use strict' +const { test } = require('tap') +const { BerReader, BerWriter } = require('asn1') +const { Control, getControl } = require('../../lib') test('new no args', function (t) { - t.ok(new Control()); - t.end(); -}); - + t.ok(new Control()) + t.end() +}) test('new with args', function (t) { - var c = new Control({ + const c = new Control({ type: '2.16.840.1.113730.3.4.2', criticality: true - }); - t.ok(c); - t.equal(c.type, '2.16.840.1.113730.3.4.2'); - t.ok(c.criticality); - t.end(); -}); - + }) + t.ok(c) + t.equal(c.type, '2.16.840.1.113730.3.4.2') + t.ok(c.criticality) + t.end() +}) test('parse', function (t) { - var ber = new BerWriter(); - ber.startSequence(); - ber.writeString('2.16.840.1.113730.3.4.2'); - ber.writeBoolean(true); - ber.writeString('foo'); - ber.endSequence(); + const ber = new BerWriter() + ber.startSequence() + ber.writeString('2.16.840.1.113730.3.4.2') + ber.writeBoolean(true) + ber.writeString('foo') + ber.endSequence() - var c = getControl(new BerReader(ber.buffer)); - - t.ok(c); - t.equal(c.type, '2.16.840.1.113730.3.4.2'); - t.ok(c.criticality); - t.equal(c.value.toString('utf8'), 'foo'); - t.end(); -}); + const c = getControl(new BerReader(ber.buffer)) + t.ok(c) + t.equal(c.type, '2.16.840.1.113730.3.4.2') + t.ok(c.criticality) + t.equal(c.value.toString('utf8'), 'foo') + t.end() +}) test('parse no value', function (t) { - var ber = new BerWriter(); - ber.startSequence(); - ber.writeString('2.16.840.1.113730.3.4.2'); - ber.endSequence(); + const ber = new BerWriter() + ber.startSequence() + ber.writeString('2.16.840.1.113730.3.4.2') + ber.endSequence() - var c = getControl(new BerReader(ber.buffer)); + const c = getControl(new BerReader(ber.buffer)) - t.ok(c); - t.equal(c.type, '2.16.840.1.113730.3.4.2'); - t.equal(c.criticality, false); - t.notOk(c.value, null); - t.end(); -}); + t.ok(c) + t.equal(c.type, '2.16.840.1.113730.3.4.2') + t.equal(c.criticality, false) + t.notOk(c.value, null) + t.end() +}) diff --git a/test/controls/entry_change_notification_control.test.js b/test/controls/entry_change_notification_control.test.js new file mode 100644 index 0000000..c29a97b --- /dev/null +++ b/test/controls/entry_change_notification_control.test.js @@ -0,0 +1,66 @@ +'use strict' + +const { test } = require('tap') +const { BerReader, BerWriter } = require('asn1') +const { getControl, EntryChangeNotificationControl } = require('../../lib') + +test('new no args', function (t) { + t.ok(new EntryChangeNotificationControl()) + t.end() +}) + +test('new with args', function (t) { + const c = new EntryChangeNotificationControl({ + type: '2.16.840.1.113730.3.4.7', + criticality: true, + value: { + changeType: 8, + previousDN: 'cn=foobarbazcar', + changeNumber: 123456789 + } + }) + t.ok(c) + t.equal(c.type, '2.16.840.1.113730.3.4.7') + t.ok(c.criticality) + t.equal(c.value.changeType, 8) + t.equal(c.value.previousDN, 'cn=foobarbazcar') + t.equal(c.value.changeNumber, 123456789) + + const writer = new BerWriter() + c.toBer(writer) + const reader = new BerReader(writer.buffer) + const psc = getControl(reader) + t.ok(psc) + t.equal(psc.type, '2.16.840.1.113730.3.4.7') + t.ok(psc.criticality) + t.equal(psc.value.changeType, 8) + t.equal(psc.value.previousDN, 'cn=foobarbazcar') + t.equal(psc.value.changeNumber, 123456789) + + t.end() +}) + +test('tober', function (t) { + const psc = new EntryChangeNotificationControl({ + type: '2.16.840.1.113730.3.4.7', + criticality: true, + value: { + changeType: 8, + previousDN: 'cn=foobarbazcar', + changeNumber: 123456789 + } + }) + + const ber = new BerWriter() + psc.toBer(ber) + + const c = getControl(new BerReader(ber.buffer)) + t.ok(c) + t.equal(c.type, '2.16.840.1.113730.3.4.7') + t.ok(c.criticality) + t.equal(c.value.changeType, 8) + t.equal(c.value.previousDN, 'cn=foobarbazcar') + t.equal(c.value.changeNumber, 123456789) + + t.end() +}) diff --git a/test/controls/entry_change_notification_control_test.js b/test/controls/entry_change_notification_control_test.js deleted file mode 100644 index e9a9965..0000000 --- a/test/controls/entry_change_notification_control_test.js +++ /dev/null @@ -1,88 +0,0 @@ - -var test = require('tap').test; - -var asn1 = require('asn1'); - -var BerReader = asn1.BerReader; -var BerWriter = asn1.BerWriter; -var getControl; -var EntryChangeNotificationControl; - - - -///--- Tests - - -test('load library', function (t) { - EntryChangeNotificationControl = - require('../../lib').EntryChangeNotificationControl; - t.ok(EntryChangeNotificationControl); - getControl = require('../../lib').getControl; - t.ok(getControl); - t.end(); -}); - - -test('new no args', function (t) { - t.ok(new EntryChangeNotificationControl()); - t.end(); -}); - - -test('new with args', function (t) { - var c = new EntryChangeNotificationControl({ - type: '2.16.840.1.113730.3.4.7', - criticality: true, - value: { - changeType: 8, - previousDN: 'cn=foobarbazcar', - changeNumber: 123456789 - } - }); - t.ok(c); - t.equal(c.type, '2.16.840.1.113730.3.4.7'); - t.ok(c.criticality); - t.equal(c.value.changeType, 8); - t.equal(c.value.previousDN, 'cn=foobarbazcar'); - t.equal(c.value.changeNumber, 123456789); - - - var writer = new BerWriter(); - c.toBer(writer); - var reader = new BerReader(writer.buffer); - var psc = getControl(reader); - t.ok(psc); - console.log('psc', psc.value); - t.equal(psc.type, '2.16.840.1.113730.3.4.7'); - t.ok(psc.criticality); - t.equal(psc.value.changeType, 8); - t.equal(psc.value.previousDN, 'cn=foobarbazcar'); - t.equal(psc.value.changeNumber, 123456789); - - t.end(); -}); - -test('tober', function (t) { - var psc = new EntryChangeNotificationControl({ - type: '2.16.840.1.113730.3.4.7', - criticality: true, - value: { - changeType: 8, - previousDN: 'cn=foobarbazcar', - changeNumber: 123456789 - } - }); - - var ber = new BerWriter(); - psc.toBer(ber); - - var c = getControl(new BerReader(ber.buffer)); - t.ok(c); - t.equal(c.type, '2.16.840.1.113730.3.4.7'); - t.ok(c.criticality); - t.equal(c.value.changeType, 8); - t.equal(c.value.previousDN, 'cn=foobarbazcar'); - t.equal(c.value.changeNumber, 123456789); - - t.end(); -}); diff --git a/test/controls/paged_results_control.test.js b/test/controls/paged_results_control.test.js new file mode 100644 index 0000000..39b3fb1 --- /dev/null +++ b/test/controls/paged_results_control.test.js @@ -0,0 +1,61 @@ +'use strict' + +const { test } = require('tap') +const { BerReader, BerWriter } = require('asn1') +const { getControl, PagedResultsControl } = require('../../lib') + +test('new no args', function (t) { + t.ok(new PagedResultsControl()) + t.end() +}) + +test('new with args', function (t) { + const c = new PagedResultsControl({ + type: '1.2.840.113556.1.4.319', + criticality: true, + value: { + size: 1000, + cookie: Buffer.from([1, 2, 3]) + } + }) + t.ok(c) + t.equal(c.type, '1.2.840.113556.1.4.319') + t.ok(c.criticality) + t.equal(c.value.size, 1000) + t.is(Buffer.compare(c.value.cookie, Buffer.from([1, 2, 3])), 0) + + const writer = new BerWriter() + c.toBer(writer) + const reader = new BerReader(writer.buffer) + const psc = getControl(reader) + t.ok(psc) + t.equal(psc.type, '1.2.840.113556.1.4.319') + t.ok(psc.criticality) + t.equal(psc.value.size, 1000) + t.is(Buffer.compare(psc.value.cookie, Buffer.from([1, 2, 3])), 0) + + t.end() +}) + +test('tober', function (t) { + const psc = new PagedResultsControl({ + type: '1.2.840.113556.1.4.319', + criticality: true, + value: { + size: 20, + cookie: Buffer.alloc(0) + } + }) + + const ber = new BerWriter() + psc.toBer(ber) + + const c = getControl(new BerReader(ber.buffer)) + t.ok(c) + t.equal(c.type, '1.2.840.113556.1.4.319') + t.ok(c.criticality) + t.equal(c.value.size, 20) + t.is(Buffer.compare(c.value.cookie, Buffer.alloc(0)), 0) + + t.end() +}) diff --git a/test/controls/paged_results_control_test.js b/test/controls/paged_results_control_test.js deleted file mode 100644 index e8c2f07..0000000 --- a/test/controls/paged_results_control_test.js +++ /dev/null @@ -1,86 +0,0 @@ - -var test = require('tap').test; - -var asn1 = require('asn1'); - -var BerReader = asn1.BerReader; -var BerWriter = asn1.BerWriter; -var getControl; -var PagedResultsControl; - -function bufferEqual(t, a, b) { - t.equal(a.toString('hex'), b.toString('hex')); -} - - -///--- Tests - - -test('load library', function (t) { - PagedResultsControl = - require('../../lib').PagedResultsControl; - t.ok(PagedResultsControl); - getControl = require('../../lib').getControl; - t.ok(getControl); - t.end(); -}); - - -test('new no args', function (t) { - t.ok(new PagedResultsControl()); - t.end(); -}); - - -test('new with args', function (t) { - var c = new PagedResultsControl({ - type: '1.2.840.113556.1.4.319', - criticality: true, - value: { - size: 1000, - cookie: new Buffer([1, 2, 3]) - } - }); - t.ok(c); - t.equal(c.type, '1.2.840.113556.1.4.319'); - t.ok(c.criticality); - t.equal(c.value.size, 1000); - bufferEqual(t, c.value.cookie, new Buffer([1, 2, 3])); - - - var writer = new BerWriter(); - c.toBer(writer); - var reader = new BerReader(writer.buffer); - var psc = getControl(reader); - t.ok(psc); - console.log('psc', psc.value); - t.equal(psc.type, '1.2.840.113556.1.4.319'); - t.ok(psc.criticality); - t.equal(psc.value.size, 1000); - bufferEqual(t, psc.value.cookie, new Buffer([1, 2, 3])); - - t.end(); -}); - -test('tober', function (t) { - var psc = new PagedResultsControl({ - type: '1.2.840.113556.1.4.319', - criticality: true, - value: { - size: 20, - cookie: new Buffer(0) - } - }); - - var ber = new BerWriter(); - psc.toBer(ber); - - var c = getControl(new BerReader(ber.buffer)); - t.ok(c); - t.equal(c.type, '1.2.840.113556.1.4.319'); - t.ok(c.criticality); - t.equal(c.value.size, 20); - bufferEqual(t, c.value.cookie, new Buffer(0)); - - t.end(); -}); diff --git a/test/controls/persistent_search_control.test.js b/test/controls/persistent_search_control.test.js index 11962e0..414de54 100644 --- a/test/controls/persistent_search_control.test.js +++ b/test/controls/persistent_search_control.test.js @@ -1,32 +1,28 @@ // Copyright 2011 Mark Cavage, Inc. All rights reserved. -var test = require('tape').test; +var test = require('tap').test -var asn1 = require('asn1'); +var asn1 = require('asn1') -var BerReader = asn1.BerReader; -var BerWriter = asn1.BerWriter; -var getControl; -var PersistentSearchControl; +var BerReader = asn1.BerReader +var BerWriter = asn1.BerWriter +var getControl +var PersistentSearchControl - - -///--- Tests +/// --- Tests test('load library', function (t) { - PersistentSearchControl = require('../../lib').PersistentSearchControl; - t.ok(PersistentSearchControl); - getControl = require('../../lib').getControl; - t.ok(getControl); - t.end(); -}); - + PersistentSearchControl = require('../../lib').PersistentSearchControl + t.ok(PersistentSearchControl) + getControl = require('../../lib').getControl + t.ok(getControl) + t.end() +}) test('new no args', function (t) { - t.ok(new PersistentSearchControl()); - t.end(); -}); - + t.ok(new PersistentSearchControl()) + t.end() +}) test('new with args', function (t) { var c = new PersistentSearchControl({ @@ -37,47 +33,46 @@ test('new with args', function (t) { changesOnly: false, returnECs: false } - }); - t.ok(c); - t.equal(c.type, '2.16.840.1.113730.3.4.3'); - t.ok(c.criticality); + }) + t.ok(c) + t.equal(c.type, '2.16.840.1.113730.3.4.3') + t.ok(c.criticality) - t.equal(c.value.changeTypes, 15); - t.equal(c.value.changesOnly, false); - t.equal(c.value.returnECs, false); + t.equal(c.value.changeTypes, 15) + t.equal(c.value.changesOnly, false) + t.equal(c.value.returnECs, false) + var writer = new BerWriter() + c.toBer(writer) + var reader = new BerReader(writer.buffer) + var psc = getControl(reader) + t.ok(psc) + t.equal(psc.type, '2.16.840.1.113730.3.4.3') + t.ok(psc.criticality) + t.equal(psc.value.changeTypes, 15) + t.equal(psc.value.changesOnly, false) + t.equal(psc.value.returnECs, false) - var writer = new BerWriter(); - c.toBer(writer); - var reader = new BerReader(writer.buffer); - var psc = getControl(reader); - t.ok(psc); - t.equal(psc.type, '2.16.840.1.113730.3.4.3'); - t.ok(psc.criticality); - t.equal(psc.value.changeTypes, 15); - t.equal(psc.value.changesOnly, false); - t.equal(psc.value.returnECs, false); - - t.end(); -}); + t.end() +}) test('getControl with args', function (t) { - var buf = new Buffer([ + var buf = Buffer.from([ 0x30, 0x26, 0x04, 0x17, 0x32, 0x2e, 0x31, 0x36, 0x2e, 0x38, 0x34, 0x30, 0x2e, 0x31, 0x2e, 0x31, 0x31, 0x33, 0x37, 0x33, 0x30, 0x2e, 0x33, 0x2e, 0x34, 0x2e, 0x33, 0x04, 0x0b, 0x30, 0x09, 0x02, 0x01, 0x0f, 0x01, 0x01, - 0xff, 0x01, 0x01, 0xff]); + 0xff, 0x01, 0x01, 0xff]) - var ber = new BerReader(buf); - var psc = getControl(ber); - t.ok(psc); - t.equal(psc.type, '2.16.840.1.113730.3.4.3'); - t.equal(psc.criticality, false); - t.equal(psc.value.changeTypes, 15); - t.equal(psc.value.changesOnly, true); - t.equal(psc.value.returnECs, true); - t.end(); -}); + var ber = new BerReader(buf) + var psc = getControl(ber) + t.ok(psc) + t.equal(psc.type, '2.16.840.1.113730.3.4.3') + t.equal(psc.criticality, false) + t.equal(psc.value.changeTypes, 15) + t.equal(psc.value.changesOnly, true) + t.equal(psc.value.returnECs, true) + t.end() +}) test('tober', function (t) { var psc = new PersistentSearchControl({ @@ -88,17 +83,17 @@ test('tober', function (t) { changesOnly: false, returnECs: false } - }); + }) - var ber = new BerWriter(); - psc.toBer(ber); + var ber = new BerWriter() + psc.toBer(ber) - var c = getControl(new BerReader(ber.buffer)); - t.ok(c); - t.equal(c.type, '2.16.840.1.113730.3.4.3'); - t.ok(c.criticality); - t.equal(c.value.changeTypes, 15); - t.equal(c.value.changesOnly, false); - t.equal(c.value.returnECs, false); - t.end(); -}); + var c = getControl(new BerReader(ber.buffer)) + t.ok(c) + t.equal(c.type, '2.16.840.1.113730.3.4.3') + t.ok(c.criticality) + t.equal(c.value.changeTypes, 15) + t.equal(c.value.changesOnly, false) + t.equal(c.value.returnECs, false) + t.end() +}) diff --git a/test/controls/server_side_sorting_control_request.test.js b/test/controls/server_side_sorting_control_request.test.js index 1830c0c..a2cd85c 100644 --- a/test/controls/server_side_sorting_control_request.test.js +++ b/test/controls/server_side_sorting_control_request.test.js @@ -1,70 +1,56 @@ +'use strict' -var test = require('tape').test; - -var asn1 = require('asn1'); - -var BerReader = asn1.BerReader; -var BerWriter = asn1.BerWriter; -var getControl; -var SSSRControl; - -///--- Tests - - -test('load library', function (t) { - SSSRControl = require('../../lib').ServerSideSortingRequestControl; - t.ok(SSSRControl); - getControl = require('../../lib').getControl; - t.ok(getControl); - t.end(); -}); +const { test } = require('tap') +const { BerReader, BerWriter } = require('asn1') +const { getControl, ServerSideSortingRequestControl: SSSRControl } = require('../../lib') test('new no args', function (t) { - t.ok(new SSSRControl()); - t.end(); -}); + t.ok(new SSSRControl()) + t.end() +}) test('new with args', function (t) { - var c = new SSSRControl({ + const c = new SSSRControl({ criticality: true, value: { attributeType: 'sn' } - }); - t.ok(c); - t.equal(c.type, '1.2.840.113556.1.4.473'); - t.ok(c.criticality); - t.equal(c.value.length, 1); - t.equal(c.value[0].attributeType, 'sn'); + }) + t.ok(c) + t.equal(c.type, '1.2.840.113556.1.4.473') + t.ok(c.criticality) + t.equal(c.value.length, 1) + t.equal(c.value[0].attributeType, 'sn') - t.end(); -}); + t.end() +}) test('toBer - object', function (t) { - var sssc = new SSSRControl({ + const sssc = new SSSRControl({ criticality: true, value: { attributeType: 'sn', orderingRule: 'caseIgnoreOrderingMatch', reverseOrder: true - }}); + } + }) - var ber = new BerWriter(); - sssc.toBer(ber); + const ber = new BerWriter() + sssc.toBer(ber) - var c = getControl(new BerReader(ber.buffer)); - t.ok(c); - t.equal(c.type, '1.2.840.113556.1.4.473'); - t.ok(c.criticality); - t.equal(c.value[0].attributeType, 'sn'); - t.equal(c.value[0].orderingRule, 'caseIgnoreOrderingMatch'); - t.equal(c.value[0].reverseOrder, true); + const c = getControl(new BerReader(ber.buffer)) + t.ok(c) + t.equal(c.type, '1.2.840.113556.1.4.473') + t.ok(c.criticality) + t.equal(c.value[0].attributeType, 'sn') + t.equal(c.value[0].orderingRule, 'caseIgnoreOrderingMatch') + t.equal(c.value[0].reverseOrder, true) - t.end(); -}); + t.end() +}) test('toBer - array', function (t) { - var sssc = new SSSRControl({ + const sssc = new SSSRControl({ criticality: true, value: [ { @@ -77,33 +63,33 @@ test('toBer - array', function (t) { orderingRule: 'caseIgnoreOrderingMatch' } ] - }); + }) - var ber = new BerWriter(); - sssc.toBer(ber); + const ber = new BerWriter() + sssc.toBer(ber) - var c = getControl(new BerReader(ber.buffer)); - t.ok(c); - t.equal(c.type, '1.2.840.113556.1.4.473'); - t.ok(c.criticality); - t.equal(c.value.length, 2); - t.equal(c.value[0].attributeType, 'sn'); - t.equal(c.value[0].orderingRule, 'caseIgnoreOrderingMatch'); - t.equal(c.value[0].reverseOrder, true); - t.equal(c.value[1].attributeType, 'givenName'); - t.equal(c.value[1].orderingRule, 'caseIgnoreOrderingMatch'); + const c = getControl(new BerReader(ber.buffer)) + t.ok(c) + t.equal(c.type, '1.2.840.113556.1.4.473') + t.ok(c.criticality) + t.equal(c.value.length, 2) + t.equal(c.value[0].attributeType, 'sn') + t.equal(c.value[0].orderingRule, 'caseIgnoreOrderingMatch') + t.equal(c.value[0].reverseOrder, true) + t.equal(c.value[1].attributeType, 'givenName') + t.equal(c.value[1].orderingRule, 'caseIgnoreOrderingMatch') - t.end(); -}); + t.end() +}) test('toBer - empty', function (t) { - var sssc = new SSSRControl(); - var ber = new BerWriter(); - sssc.toBer(ber); + const sssc = new SSSRControl() + const ber = new BerWriter() + sssc.toBer(ber) - var c = getControl(new BerReader(ber.buffer)); - t.ok(c); - t.equal(c.type, '1.2.840.113556.1.4.473'); - t.equal(c.value.length, 0); - t.end(); -}); + const c = getControl(new BerReader(ber.buffer)) + t.ok(c) + t.equal(c.type, '1.2.840.113556.1.4.473') + t.equal(c.value.length, 0) + t.end() +}) diff --git a/test/controls/server_side_sorting_control_response.test.js b/test/controls/server_side_sorting_control_response.test.js index 1c38f77..7b094f7 100644 --- a/test/controls/server_side_sorting_control_response.test.js +++ b/test/controls/server_side_sorting_control_response.test.js @@ -1,117 +1,105 @@ -var test = require('tape').test; +'use strict' -var asn1 = require('asn1'); - -var BerReader = asn1.BerReader; -var BerWriter = asn1.BerWriter; -var ldap; -var getControl; -var SSSResponseControl; -var OID = '1.2.840.113556.1.4.474'; - -///--- Tests - - -test('load library', function (t) { - ldap = require('../../lib'); - SSSResponseControl = ldap.ServerSideSortingResponseControl; - t.ok(SSSResponseControl); - getControl = ldap.getControl; - t.ok(getControl); - t.end(); -}); +const { test } = require('tap') +const { BerReader, BerWriter } = require('asn1') +const ldap = require('../../lib') +const { getControl, ServerSideSortingResponseControl: SSSResponseControl } = ldap +const OID = '1.2.840.113556.1.4.474' test('new no args', function (t) { - var c = new SSSResponseControl(); - t.ok(c); - t.equal(c.type, OID); - t.equal(c.criticality, false); - t.end(); -}); + const c = new SSSResponseControl() + t.ok(c) + t.equal(c.type, OID) + t.equal(c.criticality, false) + t.end() +}) test('new with args', function (t) { - var c = new SSSResponseControl({ + const c = new SSSResponseControl({ criticality: true, value: { result: ldap.LDAP_SUCCESS, failedAttribute: 'cn' } - }); - t.ok(c); - t.equal(c.type, OID); - t.equal(c.criticality, false); - t.equal(c.value.result, ldap.LDAP_SUCCESS); - t.equal(c.value.failedAttribute, 'cn'); + }) + t.ok(c) + t.equal(c.type, OID) + t.equal(c.criticality, false) + t.equal(c.value.result, ldap.LDAP_SUCCESS) + t.equal(c.value.failedAttribute, 'cn') - t.end(); -}); + t.end() +}) test('toBer - success', function (t) { - var sssc = new SSSResponseControl({ + const sssc = new SSSResponseControl({ value: { result: ldap.LDAP_SUCCESS, failedAttribute: 'foobar' - }}); + } + }) - var ber = new BerWriter(); - sssc.toBer(ber); + const ber = new BerWriter() + sssc.toBer(ber) - var c = getControl(new BerReader(ber.buffer)); - t.ok(c); - t.equal(c.type, '1.2.840.113556.1.4.474'); - t.equal(c.criticality, false); - t.equal(c.value.result, ldap.LDAP_SUCCESS); - t.notOk(c.value.failedAttribute); - t.end(); -}); + const c = getControl(new BerReader(ber.buffer)) + t.ok(c) + t.equal(c.type, '1.2.840.113556.1.4.474') + t.equal(c.criticality, false) + t.equal(c.value.result, ldap.LDAP_SUCCESS) + t.notOk(c.value.failedAttribute) + t.end() +}) test('toBer - simple failure', function (t) { - var sssc = new SSSResponseControl({ + const sssc = new SSSResponseControl({ value: { result: ldap.LDAP_NO_SUCH_ATTRIBUTE - }}); + } + }) - var ber = new BerWriter(); - sssc.toBer(ber); + const ber = new BerWriter() + sssc.toBer(ber) - var c = getControl(new BerReader(ber.buffer)); - t.ok(c); - t.equal(c.type, OID); - t.equal(c.criticality, false); - t.equal(c.value.result, ldap.LDAP_NO_SUCH_ATTRIBUTE); - t.notOk(c.value.failedAttribute); - t.end(); -}); + const c = getControl(new BerReader(ber.buffer)) + t.ok(c) + t.equal(c.type, OID) + t.equal(c.criticality, false) + t.equal(c.value.result, ldap.LDAP_NO_SUCH_ATTRIBUTE) + t.notOk(c.value.failedAttribute) + t.end() +}) test('toBer - detailed failure', function (t) { - var sssc = new SSSResponseControl({ + const sssc = new SSSResponseControl({ value: { result: ldap.LDAP_NO_SUCH_ATTRIBUTE, failedAttribute: 'foobar' - }}); + } + }) - var ber = new BerWriter(); - sssc.toBer(ber); + const ber = new BerWriter() + sssc.toBer(ber) - var c = getControl(new BerReader(ber.buffer)); - t.ok(c); - t.equal(c.type, OID); - t.equal(c.criticality, false); - t.equal(c.value.result, ldap.LDAP_NO_SUCH_ATTRIBUTE); - t.equal(c.value.failedAttribute, 'foobar'); - t.end(); -}); + const c = getControl(new BerReader(ber.buffer)) + t.ok(c) + t.equal(c.type, OID) + t.equal(c.criticality, false) + t.equal(c.value.result, ldap.LDAP_NO_SUCH_ATTRIBUTE) + t.equal(c.value.failedAttribute, 'foobar') + t.end() +}) test('toBer - empty', function (t) { - var sssc = new SSSResponseControl(); - var ber = new BerWriter(); - sssc.toBer(ber); + const sssc = new SSSResponseControl() + const ber = new BerWriter() + sssc.toBer(ber) - var c = getControl(new BerReader(ber.buffer)); - t.ok(c); - t.equal(c.type, OID); - t.equal(c.criticality, false); - t.notOk(c.value.result); - t.notOk(c.value.failedAttribute); - t.end(); -}); + const c = getControl(new BerReader(ber.buffer)) + t.ok(c) + t.equal(c.type, OID) + t.equal(c.criticality, false) + t.notOk(c.value.result) + t.notOk(c.value.failedAttribute) + t.end() +}) diff --git a/test/controls/virtual_list_view_request_control.test.js b/test/controls/virtual_list_view_request_control.test.js new file mode 100644 index 0000000..bcb83c6 --- /dev/null +++ b/test/controls/virtual_list_view_request_control.test.js @@ -0,0 +1,94 @@ +'use strict' + +const { test } = require('tap') +const { BerReader, BerWriter } = require('asn1') +const { getControl, VirtualListViewRequestControl: VLVRControl } = require('../../lib') + +test('VLV request - new no args', function (t) { + t.ok(new VLVRControl()) + t.end() +}) + +test('VLV request - new with args', function (t) { + const c = new VLVRControl({ + criticality: true, + value: { + beforeCount: 0, + afterCount: 3, + targetOffset: 1, + contentCount: 0 + } + }) + t.ok(c) + t.equal(c.type, '2.16.840.1.113730.3.4.9') + t.ok(c.criticality) + t.equal(c.value.beforeCount, 0) + t.equal(c.value.afterCount, 3) + t.equal(c.value.targetOffset, 1) + t.equal(c.value.contentCount, 0) + + t.end() +}) + +test('VLV request - toBer - with offset', function (t) { + const vlvc = new VLVRControl({ + criticality: true, + value: { + beforeCount: 0, + afterCount: 3, + targetOffset: 1, + contentCount: 0 + } + }) + + const ber = new BerWriter() + vlvc.toBer(ber) + + const c = getControl(new BerReader(ber.buffer)) + t.ok(c) + t.equal(c.type, '2.16.840.1.113730.3.4.9') + t.ok(c.criticality) + t.equal(c.value.beforeCount, 0) + t.equal(c.value.afterCount, 3) + t.equal(c.value.targetOffset, 1) + t.equal(c.value.contentCount, 0) + + t.end() +}) + +test('VLV request - toBer - with assertion', function (t) { + const vlvc = new VLVRControl({ + criticality: true, + value: { + beforeCount: 0, + afterCount: 3, + greaterThanOrEqual: '*foo*' + } + }) + + const ber = new BerWriter() + vlvc.toBer(ber) + + const c = getControl(new BerReader(ber.buffer)) + t.ok(c) + t.equal(c.type, '2.16.840.1.113730.3.4.9') + t.ok(c.criticality) + t.equal(c.value.beforeCount, 0) + t.equal(c.value.afterCount, 3) + t.equal(c.value.greaterThanOrEqual, '*foo*') + + t.end() +}) + +test('VLV request - toBer - empty', function (t) { + const vlvc = new VLVRControl() + const ber = new BerWriter() + vlvc.toBer(ber) + + const c = getControl(new BerReader(ber.buffer)) + t.ok(c) + t.equal(c.type, '2.16.840.1.113730.3.4.9') + t.equal(c.criticality, false) + t.notOk(c.value.result) + t.end() +}) diff --git a/test/controls/virtual_list_view_response_control.test.js b/test/controls/virtual_list_view_response_control.test.js new file mode 100644 index 0000000..1460959 --- /dev/null +++ b/test/controls/virtual_list_view_response_control.test.js @@ -0,0 +1,68 @@ +'use strict' + +const { test } = require('tap') +const { BerReader, BerWriter } = require('asn1') +const ldap = require('../../lib') +const { getControl, VirtualListViewResponseControl: VLVResponseControl } = require('../../lib') +const OID = '2.16.840.1.113730.3.4.10' + +test('VLV response - new no args', function (t) { + const c = new VLVResponseControl() + t.ok(c) + t.equal(c.type, OID) + t.equal(c.criticality, false) + t.end() +}) + +test('VLV response - new with args', function (t) { + const c = new VLVResponseControl({ + criticality: true, + value: { + result: ldap.LDAP_SUCCESS, + targetPosition: 0, + contentCount: 10 + } + }) + t.ok(c) + t.equal(c.type, OID) + t.equal(c.criticality, false) + t.equal(c.value.result, ldap.LDAP_SUCCESS) + t.equal(c.value.targetPosition, 0) + t.equal(c.value.contentCount, 10) + t.end() +}) + +test('VLV response - toBer', function (t) { + const vlpc = new VLVResponseControl({ + value: { + targetPosition: 0, + contentCount: 10, + result: ldap.LDAP_SUCCESS + } + }) + + const ber = new BerWriter() + vlpc.toBer(ber) + + const c = getControl(new BerReader(ber.buffer)) + t.ok(c) + t.equal(c.type, OID) + t.equal(c.criticality, false) + t.equal(c.value.result, ldap.LDAP_SUCCESS) + t.equal(c.value.targetPosition, 0) + t.equal(c.value.contentCount, 10) + t.end() +}) + +test('VLV response - toBer - empty', function (t) { + const vlpc = new VLVResponseControl() + const ber = new BerWriter() + vlpc.toBer(ber) + + const c = getControl(new BerReader(ber.buffer)) + t.ok(c) + t.equal(c.type, OID) + t.equal(c.criticality, false) + t.notOk(c.value.result) + t.end() +}) diff --git a/test/corked_emitter.test.js b/test/corked_emitter.test.js new file mode 100644 index 0000000..5edecd0 --- /dev/null +++ b/test/corked_emitter.test.js @@ -0,0 +1,104 @@ +'use strict' + +const { test } = require('tap') +const CorkedEmitter = require('../lib/corked_emitter') + +function gatherEventSequence (expectedNumber) { + const gatheredEvents = [] + let callback + const finished = new Promise(function (resolve) { + callback = function (...args) { + gatheredEvents.push(...args) + if (gatheredEvents.length >= expectedNumber) { + // Prevent result mutation after our promise is resolved: + resolve(gatheredEvents.slice()) + } + } + }) + return { + finished, + callback + } +} + +test('normal emit flow', function (t) { + const emitter = new CorkedEmitter() + const expectedSequence = [ + ['searchEntry', { data: 'a' }], + ['searchEntry', { data: 'b' }], + ['end'] + ] + const gatherer = gatherEventSequence(3) + emitter.on('searchEntry', function (...args) { + gatherer.callback(['searchEntry', ...args]) + }) + emitter.on('end', function (...args) { + gatherer.callback(['end', ...args]) + }) + emitter.emit('searchEntry', { data: 'a' }) + emitter.emit('searchEntry', { data: 'b' }) + emitter.emit('end') + gatherer.finished.then(function (gatheredEvents) { + expectedSequence.forEach(function (expectedEvent, i) { + t.equal(JSON.stringify(expectedEvent), JSON.stringify(gatheredEvents[i])) + }) + t.end() + }) +}) + +test('reversed listener registration', function (t) { + const emitter = new CorkedEmitter() + const expectedSequence = [ + ['searchEntry', { data: 'a' }], + ['searchEntry', { data: 'b' }], + ['end'] + ] + const gatherer = gatherEventSequence(3) + // This time, we swap the event listener registrations. + // The order of emits should remain unchanged. + emitter.on('end', function (...args) { + gatherer.callback(['end', ...args]) + }) + emitter.on('searchEntry', function (...args) { + gatherer.callback(['searchEntry', ...args]) + }) + emitter.emit('searchEntry', { data: 'a' }) + emitter.emit('searchEntry', { data: 'b' }) + emitter.emit('end') + gatherer.finished.then(function (gatheredEvents) { + expectedSequence.forEach(function (expectedEvent, i) { + t.equal(JSON.stringify(expectedEvent), JSON.stringify(gatheredEvents[i])) + }) + t.end() + }) +}) + +test('delayed listener registration', function (t) { + const emitter = new CorkedEmitter() + const expectedSequence = [ + ['searchEntry', { data: 'a' }], + ['searchEntry', { data: 'b' }], + ['end'] + ] + const gatherer = gatherEventSequence(3) + emitter.emit('searchEntry', { data: 'a' }) + emitter.emit('searchEntry', { data: 'b' }) + emitter.emit('end') + // The listeners only appear after a brief delay - this simulates + // the situation described in https://github.com/ldapjs/node-ldapjs/issues/602 + // and in https://github.com/ifroz/node-ldapjs/commit/5239f6c68827f2c25b4589089c199d15bb882412 + setTimeout(function () { + emitter.on('end', function (...args) { + gatherer.callback(['end', ...args]) + }) + emitter.on('searchEntry', function (...args) { + gatherer.callback(['searchEntry', ...args]) + }) + }, 50) + gatherer.finished.then(function (gatheredEvents) { + expectedSequence.forEach(function (expectedEvent, i) { + t.equal(JSON.stringify(expectedEvent), JSON.stringify(gatheredEvents[i])) + }) + t.end() + }) +}) diff --git a/test/dn.test.js b/test/dn.test.js index d86ddcf..0928069 100644 --- a/test/dn.test.js +++ b/test/dn.test.js @@ -1,263 +1,234 @@ -// Copyright 2011 Mark Cavage, Inc. All rights reserved. - -var test = require('tape').test; - - -///--- Globals - -var dn; - - -///--- Tests - -test('load library', function (t) { - dn = require('../lib/index').dn; - t.ok(dn); - t.end(); -}); +'use strict' +const { test } = require('tap') +const { dn } = require('../lib') test('parse basic', function (t) { - var DN_STR = 'cn=mark, ou=people, o=joyent'; - var name = dn.parse(DN_STR); - t.ok(name); - t.ok(name.rdns); - t.ok(Array.isArray(name.rdns)); - t.equal(3, name.rdns.length); + const DN_STR = 'cn=mark, ou=people, o=joyent' + const name = dn.parse(DN_STR) + t.ok(name) + t.ok(name.rdns) + t.ok(Array.isArray(name.rdns)) + t.equal(3, name.rdns.length) name.rdns.forEach(function (rdn) { - t.equal('object', typeof (rdn)); - }); - t.equal(name.toString(), DN_STR); - t.end(); -}); - + t.equal('object', typeof (rdn)) + }) + t.equal(name.toString(), DN_STR) + t.end() +}) test('parse escaped', function (t) { - var DN_STR = 'cn=m\\,ark, ou=people, o=joyent'; - var name = dn.parse(DN_STR); - t.ok(name); - t.ok(name.rdns); - t.ok(Array.isArray(name.rdns)); - t.equal(3, name.rdns.length); + const DN_STR = 'cn=m\\,ark, ou=people, o=joyent' + const name = dn.parse(DN_STR) + t.ok(name) + t.ok(name.rdns) + t.ok(Array.isArray(name.rdns)) + t.equal(3, name.rdns.length) name.rdns.forEach(function (rdn) { - t.equal('object', typeof (rdn)); - }); - t.equal(name.toString(), DN_STR); - t.end(); -}); - + t.equal('object', typeof (rdn)) + }) + t.equal(name.toString(), DN_STR) + t.end() +}) test('parse compound', function (t) { - var DN_STR = 'cn=mark+sn=cavage, ou=people, o=joyent'; - var name = dn.parse(DN_STR); - t.ok(name); - t.ok(name.rdns); - t.ok(Array.isArray(name.rdns)); - t.equal(3, name.rdns.length); + const DN_STR = 'cn=mark+sn=cavage, ou=people, o=joyent' + const name = dn.parse(DN_STR) + t.ok(name) + t.ok(name.rdns) + t.ok(Array.isArray(name.rdns)) + t.equal(3, name.rdns.length) name.rdns.forEach(function (rdn) { - t.equal('object', typeof (rdn)); - }); - t.equal(name.toString(), DN_STR); - t.end(); -}); - + t.equal('object', typeof (rdn)) + }) + t.equal(name.toString(), DN_STR) + t.end() +}) test('parse quoted', function (t) { - var DN_STR = 'cn="mark+sn=cavage", ou=people, o=joyent'; - var ESCAPE_STR = 'cn=mark\\+sn\\=cavage, ou=people, o=joyent'; - var name = dn.parse(DN_STR); - t.ok(name); - t.ok(name.rdns); - t.ok(Array.isArray(name.rdns)); - t.equal(3, name.rdns.length); + const DN_STR = 'cn="mark+sn=cavage", ou=people, o=joyent' + const ESCAPE_STR = 'cn=mark\\+sn\\=cavage, ou=people, o=joyent' + const name = dn.parse(DN_STR) + t.ok(name) + t.ok(name.rdns) + t.ok(Array.isArray(name.rdns)) + t.equal(3, name.rdns.length) name.rdns.forEach(function (rdn) { - t.equal('object', typeof (rdn)); - }); - t.equal(name.toString(), ESCAPE_STR); - t.end(); -}); - + t.equal('object', typeof (rdn)) + }) + t.equal(name.toString(), ESCAPE_STR) + t.end() +}) test('equals', function (t) { - var dn1 = dn.parse('cn=foo,dc=bar'); - t.ok(dn1.equals('cn=foo,dc=bar')); - t.ok(!dn1.equals('cn=foo1,dc=bar')); - t.ok(dn1.equals(dn.parse('cn=foo,dc=bar'))); - t.ok(!dn1.equals(dn.parse('cn=foo2,dc=bar'))); - t.end(); -}); - + const dn1 = dn.parse('cn=foo,dc=bar') + t.ok(dn1.equals('cn=foo,dc=bar')) + t.ok(!dn1.equals('cn=foo1,dc=bar')) + t.ok(dn1.equals(dn.parse('cn=foo,dc=bar'))) + t.ok(!dn1.equals(dn.parse('cn=foo2,dc=bar'))) + t.end() +}) test('child of', function (t) { - var dn1 = dn.parse('cn=foo,dc=bar'); - t.ok(dn1.childOf('dc=bar')); - t.ok(!dn1.childOf('dc=moo')); - t.ok(!dn1.childOf('dc=foo')); - t.ok(!dn1.childOf('cn=foo,dc=bar')); - - t.ok(dn1.childOf(dn.parse('dc=bar'))); - t.end(); -}); + const dn1 = dn.parse('cn=foo,dc=bar') + t.ok(dn1.childOf('dc=bar')) + t.ok(!dn1.childOf('dc=moo')) + t.ok(!dn1.childOf('dc=foo')) + t.ok(!dn1.childOf('cn=foo,dc=bar')) + t.ok(dn1.childOf(dn.parse('dc=bar'))) + t.end() +}) test('parent of', function (t) { - var dn1 = dn.parse('cn=foo,dc=bar'); - t.ok(dn1.parentOf('cn=moo,cn=foo,dc=bar')); - t.ok(!dn1.parentOf('cn=moo,cn=bar,dc=foo')); - t.ok(!dn1.parentOf('cn=foo,dc=bar')); - - t.ok(dn1.parentOf(dn.parse('cn=moo,cn=foo,dc=bar'))); - t.end(); -}); + const dn1 = dn.parse('cn=foo,dc=bar') + t.ok(dn1.parentOf('cn=moo,cn=foo,dc=bar')) + t.ok(!dn1.parentOf('cn=moo,cn=bar,dc=foo')) + t.ok(!dn1.parentOf('cn=foo,dc=bar')) + t.ok(dn1.parentOf(dn.parse('cn=moo,cn=foo,dc=bar'))) + t.end() +}) test('DN parent', function (t) { - var _dn = dn.parse('cn=foo,ou=bar'); - var parent1 = _dn.parent(); - var parent2 = parent1.parent(); - t.ok(parent1.equals('ou=bar')); - t.ok(parent2.equals('')); - t.equal(parent2.parent(), null); - t.end(); -}); - + const _dn = dn.parse('cn=foo,ou=bar') + const parent1 = _dn.parent() + const parent2 = parent1.parent() + t.ok(parent1.equals('ou=bar')) + t.ok(parent2.equals('')) + t.equal(parent2.parent(), null) + t.end() +}) test('empty DNs', function (t) { - var _dn = dn.parse(''); - var _dn2 = dn.parse('cn=foo'); - t.ok(_dn.isEmpty()); - t.notOk(_dn2.isEmpty()); - t.notOk(_dn.equals('cn=foo')); - t.notOk(_dn2.equals('')); - t.ok(_dn.parentOf('cn=foo')); - t.notOk(_dn.childOf('cn=foo')); - t.notOk(_dn2.parentOf('')); - t.ok(_dn2.childOf('')); - t.end(); -}); - + const _dn = dn.parse('') + const _dn2 = dn.parse('cn=foo') + t.ok(_dn.isEmpty()) + t.notOk(_dn2.isEmpty()) + t.notOk(_dn.equals('cn=foo')) + t.notOk(_dn2.equals('')) + t.ok(_dn.parentOf('cn=foo')) + t.notOk(_dn.childOf('cn=foo')) + t.notOk(_dn2.parentOf('')) + t.ok(_dn2.childOf('')) + t.end() +}) test('case insensitive attribute names', function (t) { - var dn1 = dn.parse('CN=foo,dc=bar'); - t.ok(dn1.equals('cn=foo,dc=bar')); - t.ok(dn1.equals(dn.parse('cn=foo,DC=bar'))); - t.end(); -}); - + const dn1 = dn.parse('CN=foo,dc=bar') + t.ok(dn1.equals('cn=foo,dc=bar')) + t.ok(dn1.equals(dn.parse('cn=foo,DC=bar'))) + t.end() +}) test('format', function (t) { - var DN_ORDER = dn.parse('sn=bar+cn=foo,ou=test'); - var DN_QUOTE = dn.parse('cn="foo",ou=test'); - var DN_QUOTE2 = dn.parse('cn=" foo",ou=test'); - var DN_SPACE = dn.parse('cn=foo,ou=test'); - var DN_SPACE2 = dn.parse('cn=foo ,ou=test'); - var DN_CASE = dn.parse('CN=foo,Ou=test'); + const DN_ORDER = dn.parse('sn=bar+cn=foo,ou=test') + const DN_QUOTE = dn.parse('cn="foo",ou=test') + const DN_QUOTE2 = dn.parse('cn=" foo",ou=test') + const DN_SPACE = dn.parse('cn=foo,ou=test') + const DN_SPACE2 = dn.parse('cn=foo ,ou=test') + const DN_CASE = dn.parse('CN=foo,Ou=test') - t.equal(DN_ORDER.format({keepOrder: false}), 'cn=foo+sn=bar, ou=test'); - t.equal(DN_ORDER.format({keepOrder: true}), 'sn=bar+cn=foo, ou=test'); + t.equal(DN_ORDER.format({ keepOrder: false }), 'cn=foo+sn=bar, ou=test') + t.equal(DN_ORDER.format({ keepOrder: true }), 'sn=bar+cn=foo, ou=test') - t.equal(DN_QUOTE.format({keepQuote: false}), 'cn=foo, ou=test'); - t.equal(DN_QUOTE.format({keepQuote: true}), 'cn="foo", ou=test'); - t.equal(DN_QUOTE2.format({keepQuote: false}), 'cn=" foo", ou=test'); - t.equal(DN_QUOTE2.format({keepQuote: true}), 'cn=" foo", ou=test'); + t.equal(DN_QUOTE.format({ keepQuote: false }), 'cn=foo, ou=test') + t.equal(DN_QUOTE.format({ keepQuote: true }), 'cn="foo", ou=test') + t.equal(DN_QUOTE2.format({ keepQuote: false }), 'cn=" foo", ou=test') + t.equal(DN_QUOTE2.format({ keepQuote: true }), 'cn=" foo", ou=test') - t.equal(DN_SPACE.format({keepSpace: false}), 'cn=foo, ou=test'); - t.equal(DN_SPACE.format({keepSpace: true}), 'cn=foo,ou=test'); - t.equal(DN_SPACE.format({skipSpace: true}), 'cn=foo,ou=test'); - t.equal(DN_SPACE2.format({keepSpace: false}), 'cn=foo, ou=test'); - t.equal(DN_SPACE2.format({keepSpace: true}), 'cn=foo ,ou=test'); - t.equal(DN_SPACE2.format({skipSpace: true}), 'cn=foo,ou=test'); - - t.equal(DN_CASE.format({keepCase: false}), 'cn=foo, ou=test'); - t.equal(DN_CASE.format({keepCase: true}), 'CN=foo, Ou=test'); - t.equal(DN_CASE.format({upperName: true}), 'CN=foo, OU=test'); - t.end(); -}); + t.equal(DN_SPACE.format({ keepSpace: false }), 'cn=foo, ou=test') + t.equal(DN_SPACE.format({ keepSpace: true }), 'cn=foo,ou=test') + t.equal(DN_SPACE.format({ skipSpace: true }), 'cn=foo,ou=test') + t.equal(DN_SPACE2.format({ keepSpace: false }), 'cn=foo, ou=test') + t.equal(DN_SPACE2.format({ keepSpace: true }), 'cn=foo ,ou=test') + t.equal(DN_SPACE2.format({ skipSpace: true }), 'cn=foo,ou=test') + t.equal(DN_CASE.format({ keepCase: false }), 'cn=foo, ou=test') + t.equal(DN_CASE.format({ keepCase: true }), 'CN=foo, Ou=test') + t.equal(DN_CASE.format({ upperName: true }), 'CN=foo, OU=test') + t.end() +}) test('set format', function (t) { - var _dn = dn.parse('uid="user", sn=bar+cn=foo, dc=test , DC=com'); - t.equal(_dn.toString(), 'uid=user, cn=foo+sn=bar, dc=test, dc=com'); - _dn.setFormat({keepOrder: true}); - t.equal(_dn.toString(), 'uid=user, sn=bar+cn=foo, dc=test, dc=com'); - _dn.setFormat({keepQuote: true}); - t.equal(_dn.toString(), 'uid="user", cn=foo+sn=bar, dc=test, dc=com'); - _dn.setFormat({keepSpace: true}); - t.equal(_dn.toString(), 'uid=user, cn=foo+sn=bar, dc=test , dc=com'); - _dn.setFormat({keepCase: true}); - t.equal(_dn.toString(), 'uid=user, cn=foo+sn=bar, dc=test, DC=com'); - _dn.setFormat({upperName: true}); - t.equal(_dn.toString(), 'UID=user, CN=foo+SN=bar, DC=test, DC=com'); - t.end(); -}); - + const _dn = dn.parse('uid="user", sn=bar+cn=foo, dc=test , DC=com') + t.equal(_dn.toString(), 'uid=user, cn=foo+sn=bar, dc=test, dc=com') + _dn.setFormat({ keepOrder: true }) + t.equal(_dn.toString(), 'uid=user, sn=bar+cn=foo, dc=test, dc=com') + _dn.setFormat({ keepQuote: true }) + t.equal(_dn.toString(), 'uid="user", cn=foo+sn=bar, dc=test, dc=com') + _dn.setFormat({ keepSpace: true }) + t.equal(_dn.toString(), 'uid=user, cn=foo+sn=bar, dc=test , dc=com') + _dn.setFormat({ keepCase: true }) + t.equal(_dn.toString(), 'uid=user, cn=foo+sn=bar, dc=test, DC=com') + _dn.setFormat({ upperName: true }) + t.equal(_dn.toString(), 'UID=user, CN=foo+SN=bar, DC=test, DC=com') + t.end() +}) test('format persists across clone', function (t) { - var _dn = dn.parse('uid="user", sn=bar+cn=foo, dc=test , DC=com'); - var OUT = 'UID="user", CN=foo+SN=bar, DC=test, DC=com'; - _dn.setFormat({keepQuote: true, upperName: true}); - var clone = _dn.clone(); - t.equals(_dn.toString(), OUT); - t.equals(clone.toString(), OUT); - t.end(); -}); - + const _dn = dn.parse('uid="user", sn=bar+cn=foo, dc=test , DC=com') + const OUT = 'UID="user", CN=foo+SN=bar, DC=test, DC=com' + _dn.setFormat({ keepQuote: true, upperName: true }) + const clone = _dn.clone() + t.equals(_dn.toString(), OUT) + t.equals(clone.toString(), OUT) + t.end() +}) test('initialization', function (t) { - var dn1 = new dn.DN(); - t.ok(dn1); - t.equals(dn1.toString(), ''); - t.ok(dn1.isEmpty(), 'DN with no initializer defaults to null DN'); + const dn1 = new dn.DN() + t.ok(dn1) + t.equals(dn1.toString(), '') + t.ok(dn1.isEmpty(), 'DN with no initializer defaults to null DN') - var data = [ + const data = [ new dn.RDN({ foo: 'bar' }), new dn.RDN({ o: 'base' }) - ]; - var dn2 = new dn.DN(data); - t.ok(dn2); - t.equals(dn2.toString(), 'foo=bar, o=base'); - t.ok(!dn2.isEmpty()); - - t.end(); -}); + ] + const dn2 = new dn.DN(data) + t.ok(dn2) + t.equals(dn2.toString(), 'foo=bar, o=base') + t.ok(!dn2.isEmpty()) + t.end() +}) test('array functions', function (t) { - var dn1 = dn.parse('a=foo, b=bar, c=baz'); - t.ok(dn1); - t.equal(dn1.toString(), 'a=foo, b=bar, c=baz'); + const dn1 = dn.parse('a=foo, b=bar, c=baz') + t.ok(dn1) + t.equal(dn1.toString(), 'a=foo, b=bar, c=baz') - t.ok(dn1.reverse()); - t.equal(dn1.toString(), 'c=baz, b=bar, a=foo'); + t.ok(dn1.reverse()) + t.equal(dn1.toString(), 'c=baz, b=bar, a=foo') - var rdn = dn1.pop(); - t.ok(rdn); - t.equal(dn1.toString(), 'c=baz, b=bar'); + let rdn = dn1.pop() + t.ok(rdn) + t.equal(dn1.toString(), 'c=baz, b=bar') - t.ok(dn1.push(rdn)); - t.equal(dn1.toString(), 'c=baz, b=bar, a=foo'); + t.ok(dn1.push(rdn)) + t.equal(dn1.toString(), 'c=baz, b=bar, a=foo') - rdn = dn1.shift(); - t.ok(rdn); - t.equal(dn1.toString(), 'b=bar, a=foo'); + rdn = dn1.shift() + t.ok(rdn) + t.equal(dn1.toString(), 'b=bar, a=foo') - t.ok(dn1.unshift(rdn)); - t.equal(dn1.toString(), 'c=baz, b=bar, a=foo'); - - t.end(); -}); + t.ok(dn1.unshift(rdn)) + t.equal(dn1.toString(), 'c=baz, b=bar, a=foo') + t.end() +}) test('isDN duck-testing', function (t) { - var valid = dn.parse('cn=foo'); - var isDN = dn.DN.isDN; - t.notOk(isDN(null)); - t.notOk(isDN('cn=foo')); - t.ok(isDN(valid)); - var duck = { - rdns: [ {look: 'ma'}, {a: 'dn'} ], - toString: function () { return 'look=ma, a=dn'; } - }; - t.ok(isDN(duck)); - t.end(); -}); + const valid = dn.parse('cn=foo') + const isDN = dn.DN.isDN + t.notOk(isDN(null)) + t.notOk(isDN('cn=foo')) + t.ok(isDN(valid)) + const duck = { + rdns: [{ look: 'ma' }, { a: 'dn' }], + toString: function () { return 'look=ma, a=dn' } + } + t.ok(isDN(duck)) + t.end() +}) diff --git a/test/errors.test.js b/test/errors.test.js index 68b1035..6b66a8a 100644 --- a/test/errors.test.js +++ b/test/errors.test.js @@ -1,39 +1,53 @@ -// Copyright 2015 Joyent, Inc. +'use strict' -var test = require('tape').test; - -var ldap = require('../lib/index'); - - -///--- Tests +const { test } = require('tap') +const { + LDAPError, + ConnectionError, + AbandonedError, + TimeoutError, + ConstraintViolationError, + LDAP_OTHER +} = require('../lib') test('basic error', function (t) { - var msg = 'mymsg'; - var err = new ldap.LDAPError(msg, null, null); - t.ok(err); - t.equal(err.name, 'LDAPError'); - t.equal(err.code, ldap.LDAP_OTHER); - t.equal(err.dn, ''); - t.equal(err.message, msg); - t.end(); -}); + const msg = 'mymsg' + const err = new LDAPError(msg, null, null) + t.ok(err) + t.equal(err.name, 'LDAPError') + t.equal(err.code, LDAP_OTHER) + t.equal(err.dn, '') + t.equal(err.message, msg) + t.end() +}) + +test('exports ConstraintViolationError', function (t) { + const msg = 'mymsg' + const err = new ConstraintViolationError(msg, null, null) + t.ok(err) + t.equal(err.name, 'ConstraintViolationError') + t.equal(err.code, 19) + t.equal(err.dn, '') + t.equal(err.message, msg) + t.end() +}) test('"custom" errors', function (t) { - var errors = [ - { name: 'ConnectionError', func: ldap.ConnectionError }, - { name: 'AbandonedError', func: ldap.AbandonedError }, - { name: 'TimeoutError', func: ldap.TimeoutError } - ]; + const errors = [ + { name: 'ConnectionError', Func: ConnectionError }, + { name: 'AbandonedError', Func: AbandonedError }, + { name: 'TimeoutError', Func: TimeoutError } + ] errors.forEach(function (entry) { - var msg = entry.name + 'msg'; - var err = new entry.func(msg); - t.ok(err); - t.equal(err.name, entry.name); - t.equal(err.code, ldap.LDAP_OTHER); - t.equal(err.dn, ''); - t.equal(err.message, msg); - }); + const msg = entry.name + 'msg' + const err = new entry.Func(msg) + t.ok(err) + t.equal(err.name, entry.name) + t.equal(err.code, LDAP_OTHER) + t.equal(err.dn, '') + t.equal(err.message, msg) + }) - t.end(); -}); + t.end() +}) diff --git a/test/filters/and.test.js b/test/filters/and.test.js index 12fe4d2..694f612 100644 --- a/test/filters/and.test.js +++ b/test/filters/and.test.js @@ -1,79 +1,54 @@ -// Copyright 2011 Mark Cavage, Inc. All rights reserved. - -var test = require('tape').test; - -var asn1 = require('asn1'); - - -///--- Globals - -var EqualityFilter; -var AndFilter; - - - -///--- Tests - -test('load library', function (t) { - var filters = require('../../lib/index').filters; - t.ok(filters); - EqualityFilter = filters.EqualityFilter; - AndFilter = filters.AndFilter; - t.ok(EqualityFilter); - t.ok(AndFilter); - t.end(); -}); +'use strict' +const { test } = require('tap') +const { filters: { EqualityFilter, AndFilter } } = require('../../lib') test('Construct no args', function (t) { - t.ok(new AndFilter()); - t.end(); -}); - + t.ok(new AndFilter()) + t.end() +}) test('Construct args', function (t) { - var f = new AndFilter(); + const f = new AndFilter() f.addFilter(new EqualityFilter({ attribute: 'foo', value: 'bar' - })); + })) f.addFilter(new EqualityFilter({ attribute: 'zig', value: 'zag' - })); - t.ok(f); - t.equal(f.toString(), '(&(foo=bar)(zig=zag))'); - t.end(); -}); - + })) + t.ok(f) + t.equal(f.toString(), '(&(foo=bar)(zig=zag))') + t.end() +}) test('match true', function (t) { - var f = new AndFilter(); + const f = new AndFilter() f.addFilter(new EqualityFilter({ attribute: 'foo', value: 'bar' - })); + })) f.addFilter(new EqualityFilter({ attribute: 'zig', value: 'zag' - })); - t.ok(f); - t.ok(f.matches({ foo: 'bar', zig: 'zag' })); - t.end(); -}); - + })) + t.ok(f) + t.ok(f.matches({ foo: 'bar', zig: 'zag' })) + t.end() +}) test('match false', function (t) { - var f = new AndFilter(); + const f = new AndFilter() f.addFilter(new EqualityFilter({ attribute: 'foo', value: 'bar' - })); + })) f.addFilter(new EqualityFilter({ attribute: 'zig', value: 'zag' - })); - t.ok(f); - t.ok(!f.matches({ foo: 'bar', zig: 'zonk' })); - t.end(); -}); + })) + t.ok(f) + t.ok(!f.matches({ foo: 'bar', zig: 'zonk' })) + t.end() +}) diff --git a/test/filters/approx.test.js b/test/filters/approx.test.js index d376c40..e615e98 100644 --- a/test/filters/approx.test.js +++ b/test/filters/approx.test.js @@ -1,110 +1,85 @@ -// Copyright 2011 Mark Cavage, Inc. All rights reserved. - -var test = require('tape').test; - -var asn1 = require('asn1'); - - -///--- Globals - -var ApproximateFilter; -var BerReader = asn1.BerReader; -var BerWriter = asn1.BerWriter; - - - -///--- Tests - -test('load library', function (t) { - var filters = require('../../lib/index').filters; - t.ok(filters); - ApproximateFilter = filters.ApproximateFilter; - t.ok(ApproximateFilter); - t.end(); -}); +'use strict' +const { test } = require('tap') +const { BerReader, BerWriter } = require('asn1') +const { filters: { ApproximateFilter } } = require('../../lib') test('Construct no args', function (t) { - var f = new ApproximateFilter(); - t.ok(f); - t.ok(!f.attribute); - t.ok(!f.value); - t.end(); -}); - + const f = new ApproximateFilter() + t.ok(f) + t.ok(!f.attribute) + t.ok(!f.value) + t.end() +}) test('Construct args', function (t) { - var f = new ApproximateFilter({ + const f = new ApproximateFilter({ attribute: 'foo', value: 'bar' - }); - t.ok(f); - t.equal(f.attribute, 'foo'); - t.equal(f.value, 'bar'); - t.equal(f.toString(), '(foo~=bar)'); - t.end(); -}); - + }) + t.ok(f) + t.equal(f.attribute, 'foo') + t.equal(f.value, 'bar') + t.equal(f.toString(), '(foo~=bar)') + t.end() +}) test('GH-109 = escape value only in toString()', function (t) { - var f = new ApproximateFilter({ + const f = new ApproximateFilter({ attribute: 'foo', value: 'ba(r)' - }); - t.ok(f); - t.equal(f.attribute, 'foo'); - t.equal(f.value, 'ba(r)'); - t.equal(f.toString(), '(foo~=ba\\28r\\29)'); - t.end(); -}); - + }) + t.ok(f) + t.equal(f.attribute, 'foo') + t.equal(f.value, 'ba(r)') + t.equal(f.toString(), '(foo~=ba\\28r\\29)') + t.end() +}) test('parse ok', function (t) { - var writer = new BerWriter(); - writer.writeString('foo'); - writer.writeString('bar'); - - var f = new ApproximateFilter(); - t.ok(f); - t.ok(f.parse(new BerReader(writer.buffer))); - t.end(); -}); + const writer = new BerWriter() + writer.writeString('foo') + writer.writeString('bar') + const f = new ApproximateFilter() + t.ok(f) + t.ok(f.parse(new BerReader(writer.buffer))) + t.end() +}) test('parse bad', function (t) { - var writer = new BerWriter(); - writer.writeString('foo'); - writer.writeInt(20); + const writer = new BerWriter() + writer.writeString('foo') + writer.writeInt(20) - var f = new ApproximateFilter(); - t.ok(f); + const f = new ApproximateFilter() + t.ok(f) try { - f.parse(new BerReader(writer.buffer)); - t.fail('Should have thrown InvalidAsn1Error'); + f.parse(new BerReader(writer.buffer)) + t.fail('Should have thrown InvalidAsn1Error') } catch (e) { - t.equal(e.name, 'InvalidAsn1Error'); + t.equal(e.name, 'InvalidAsn1Error') } - t.end(); -}); - + t.end() +}) test('GH-109 = to ber uses plain values', function (t) { - var f = new ApproximateFilter({ + let f = new ApproximateFilter({ attribute: 'foo', value: 'ba(r)' - }); - t.ok(f); - var writer = new BerWriter(); - f.toBer(writer); + }) + t.ok(f) + const writer = new BerWriter() + f.toBer(writer) - f = new ApproximateFilter(); - t.ok(f); + f = new ApproximateFilter() + t.ok(f) - var reader = new BerReader(writer.buffer); - reader.readSequence(); - t.ok(f.parse(reader)); + const reader = new BerReader(writer.buffer) + reader.readSequence() + t.ok(f.parse(reader)) - t.equal(f.attribute, 'foo'); - t.equal(f.value, 'ba(r)'); - t.end(); -}); + t.equal(f.attribute, 'foo') + t.equal(f.value, 'ba(r)') + t.end() +}) diff --git a/test/filters/eq.test.js b/test/filters/eq.test.js index c30d5a6..402c0f4 100644 --- a/test/filters/eq.test.js +++ b/test/filters/eq.test.js @@ -1,198 +1,167 @@ -// Copyright 2011 Mark Cavage, Inc. All rights reserved. - -var test = require('tape').test; - -var asn1 = require('asn1'); - - -///--- Globals - -var EqualityFilter; -var BerReader = asn1.BerReader; -var BerWriter = asn1.BerWriter; - - - -///--- Tests - -test('load library', function (t) { - var filters = require('../../lib/index').filters; - t.ok(filters); - EqualityFilter = filters.EqualityFilter; - t.ok(EqualityFilter); - t.end(); -}); +'use strict' +const { test } = require('tap') +const { BerReader, BerWriter } = require('asn1') +const { filters: { EqualityFilter } } = require('../../lib') test('Construct no args', function (t) { - var f = new EqualityFilter(); - t.ok(f); - t.ok(!f.attribute); - t.ok(!f.value); - t.end(); -}); - + const f = new EqualityFilter() + t.ok(f) + t.ok(!f.attribute) + t.ok(!f.value) + t.end() +}) test('Construct args', function (t) { - var f = new EqualityFilter({ + const f = new EqualityFilter({ attribute: 'foo', value: 'bar' - }); - t.ok(f); - t.equal(f.attribute, 'foo'); - t.equal(f.value, 'bar'); - t.equal(f.toString(), '(foo=bar)'); - t.end(); -}); - + }) + t.ok(f) + t.equal(f.attribute, 'foo') + t.equal(f.value, 'bar') + t.equal(f.toString(), '(foo=bar)') + t.end() +}) test('GH-109 = escape value only in toString()', function (t) { - var f = new EqualityFilter({ + const f = new EqualityFilter({ attribute: 'foo', value: 'ba(r)' - }); - t.ok(f); - t.equal(f.attribute, 'foo'); - t.equal(f.value, 'ba(r)'); - t.equal(f.toString(), '(foo=ba\\28r\\29)'); - t.end(); -}); - + }) + t.ok(f) + t.equal(f.attribute, 'foo') + t.equal(f.value, 'ba(r)') + t.equal(f.toString(), '(foo=ba\\28r\\29)') + t.end() +}) test('match true', function (t) { - var f = new EqualityFilter({ + const f = new EqualityFilter({ attribute: 'foo', value: 'bar' - }); - t.ok(f); - t.ok(f.matches({ foo: 'bar' })); - t.end(); -}); - + }) + t.ok(f) + t.ok(f.matches({ foo: 'bar' })) + t.end() +}) test('match multiple', function (t) { - var f = new EqualityFilter({ + const f = new EqualityFilter({ attribute: 'foo', value: 'bar' - }); - t.ok(f); - t.ok(f.matches({ foo: ['plop', 'bar'] })); - t.end(); -}); - + }) + t.ok(f) + t.ok(f.matches({ foo: ['plop', 'bar'] })) + t.end() +}) test('match false', function (t) { - var f = new EqualityFilter({ + const f = new EqualityFilter({ attribute: 'foo', value: 'bar' - }); - t.ok(f); - t.ok(!f.matches({ foo: 'baz' })); - t.end(); -}); - + }) + t.ok(f) + t.ok(!f.matches({ foo: 'baz' })) + t.end() +}) test('parse ok', function (t) { - var writer = new BerWriter(); - writer.writeString('foo'); - writer.writeString('bar'); - - var f = new EqualityFilter(); - t.ok(f); - t.ok(f.parse(new BerReader(writer.buffer))); - t.ok(f.matches({ foo: 'bar' })); - t.equal(f.attribute, 'foo'); - t.equal(f.value, 'bar'); - t.end(); -}); + const writer = new BerWriter() + writer.writeString('foo') + writer.writeString('bar') + const f = new EqualityFilter() + t.ok(f) + t.ok(f.parse(new BerReader(writer.buffer))) + t.ok(f.matches({ foo: 'bar' })) + t.equal(f.attribute, 'foo') + t.equal(f.value, 'bar') + t.end() +}) test('escape EqualityFilter inputs', function (t) { - var f = new EqualityFilter({ + const f = new EqualityFilter({ attribute: '(|(foo', value: 'bar))(' - }); - - t.equal(f.attribute, '(|(foo'); - t.equal(f.value, 'bar))('); - t.equal(f.toString(), '(\\28|\\28foo=bar\\29\\29\\28)'); - t.end(); -}); + }) + t.equal(f.attribute, '(|(foo') + t.equal(f.value, 'bar))(') + t.equal(f.toString(), '(\\28|\\28foo=bar\\29\\29\\28)') + t.end() +}) test('parse bad', function (t) { - var writer = new BerWriter(); - writer.writeString('foo'); - writer.writeInt(20); + const writer = new BerWriter() + writer.writeString('foo') + writer.writeInt(20) - var f = new EqualityFilter(); - t.ok(f); + const f = new EqualityFilter() + t.ok(f) try { - f.parse(new BerReader(writer.buffer)); - t.fail('Should have thrown InvalidAsn1Error'); + f.parse(new BerReader(writer.buffer)) + t.fail('Should have thrown InvalidAsn1Error') } catch (e) { - t.equal(e.name, 'InvalidAsn1Error'); + t.equal(e.name, 'InvalidAsn1Error') } - t.end(); -}); - + t.end() +}) test('GH-109 = to ber uses plain values', function (t) { - var f = new EqualityFilter({ + let f = new EqualityFilter({ attribute: 'foo', value: 'ba(r)' - }); - t.ok(f); - var writer = new BerWriter(); - f.toBer(writer); + }) + t.ok(f) + const writer = new BerWriter() + f.toBer(writer) - f = new EqualityFilter(); - t.ok(f); + f = new EqualityFilter() + t.ok(f) - var reader = new BerReader(writer.buffer); - reader.readSequence(); - t.ok(f.parse(reader)); - - t.equal(f.attribute, 'foo'); - t.equal(f.value, 'ba(r)'); - t.end(); -}); + const reader = new BerReader(writer.buffer) + reader.readSequence() + t.ok(f.parse(reader)) + t.equal(f.attribute, 'foo') + t.equal(f.value, 'ba(r)') + t.end() +}) test('handle values passed via buffer', function (t) { - var b = new Buffer([32, 64, 128, 254]); - var f = new EqualityFilter({ + const b = Buffer.from([32, 64, 128, 254]) + const f = new EqualityFilter({ attribute: 'foo', value: b - }); - t.ok(f); + }) + t.ok(f) - var writer = new BerWriter(); - f.toBer(writer); - var reader = new BerReader(writer.buffer); - reader.readSequence(); + const writer = new BerWriter() + f.toBer(writer) + const reader = new BerReader(writer.buffer) + reader.readSequence() - var f2 = new EqualityFilter(); - t.ok(f2.parse(reader)); + const f2 = new EqualityFilter() + t.ok(f2.parse(reader)) - t.equal(f2.value, b.toString()); - t.equal(f2.raw.length, b.length); - for (var i = 0; i < b.length; i++) { - t.equal(f2.raw[i], b[i]); + t.equal(f2.value, b.toString()) + t.equal(f2.raw.length, b.length) + for (let i = 0; i < b.length; i++) { + t.equal(f2.raw[i], b[i]) } - t.end(); -}); - + t.end() +}) test('GH-277 objectClass should be case-insensitive', function (t) { - var f = new EqualityFilter({ + const f = new EqualityFilter({ attribute: 'objectClass', value: 'CaseInsensitiveObj' - }); - t.ok(f); - t.ok(f.matches({ objectClass: 'CaseInsensitiveObj' })); - t.ok(f.matches({ OBJECTCLASS: 'CASEINSENSITIVEOBJ' })); - t.ok(f.matches({ objectclass: 'caseinsensitiveobj' })); - t.ok(!f.matches({ objectclass: 'matchless' })); - t.end(); -}); + }) + t.ok(f) + t.ok(f.matches({ objectClass: 'CaseInsensitiveObj' })) + t.ok(f.matches({ OBJECTCLASS: 'CASEINSENSITIVEOBJ' })) + t.ok(f.matches({ objectclass: 'caseinsensitiveobj' })) + t.ok(!f.matches({ objectclass: 'matchless' })) + t.end() +}) diff --git a/test/filters/ext.test.js b/test/filters/ext.test.js index 92d97ab..438eeed 100644 --- a/test/filters/ext.test.js +++ b/test/filters/ext.test.js @@ -1,110 +1,82 @@ -// Copyright 2011 Mark Cavage, Inc. All rights reserved. - -var test = require('tape').test; - -var asn1 = require('asn1'); - - -///--- Globals - -var ExtensibleFilter; -var BerReader = asn1.BerReader; -var BerWriter = asn1.BerWriter; -var filters; - - -///--- Tests - -test('load library', function (t) { - filters = require('../../lib/index').filters; - t.ok(filters); - ExtensibleFilter = filters.ExtensibleFilter; - t.ok(ExtensibleFilter); - t.end(); -}); +'use strict' +const { test } = require('tap') +const { filters: { parseString, ExtensibleFilter } } = require('../../lib') test('Construct no args', function (t) { - var f = new ExtensibleFilter(); - t.ok(f); - t.end(); -}); - + const f = new ExtensibleFilter() + t.ok(f) + t.end() +}) test('Construct args', function (t) { - var f = new ExtensibleFilter({ + const f = new ExtensibleFilter({ matchType: 'foo', value: 'bar' - }); - t.ok(f); - t.equal(f.matchType, 'foo'); - t.equal(f.value, 'bar'); - t.equal(f.toString(), '(foo:=bar)'); - t.end(); -}); - + }) + t.ok(f) + t.equal(f.matchType, 'foo') + t.equal(f.value, 'bar') + t.equal(f.toString(), '(foo:=bar)') + t.end() +}) test('parse RFC example 1', function (t) { - var f = filters.parseString('(cn:caseExactMatch:=Fred Flintstone)'); - t.ok(f); - t.equal(f.matchType, 'cn'); - t.equal(f.matchingRule, 'caseExactMatch'); - t.equal(f.matchValue, 'Fred Flintstone'); - t.notOk(f.dnAttributes); - t.end(); -}); - + const f = parseString('(cn:caseExactMatch:=Fred Flintstone)') + t.ok(f) + t.equal(f.matchType, 'cn') + t.equal(f.matchingRule, 'caseExactMatch') + t.equal(f.matchValue, 'Fred Flintstone') + t.notOk(f.dnAttributes) + t.end() +}) test('parse RFC example 2', function (t) { - var f = filters.parseString('(cn:=Betty Rubble)'); - t.ok(f); - t.equal(f.matchType, 'cn'); - t.equal(f.matchValue, 'Betty Rubble'); - t.notOk(f.dnAttributes); - t.notOk(f.matchingRule); - t.end(); -}); - + const f = parseString('(cn:=Betty Rubble)') + t.ok(f) + t.equal(f.matchType, 'cn') + t.equal(f.matchValue, 'Betty Rubble') + t.notOk(f.dnAttributes) + t.notOk(f.matchingRule) + t.end() +}) test('parse RFC example 3', function (t) { - var f = filters.parseString('(sn:dn:2.4.6.8.10:=Barney Rubble)'); - t.ok(f); - t.equal(f.matchType, 'sn'); - t.equal(f.matchingRule, '2.4.6.8.10'); - t.equal(f.matchValue, 'Barney Rubble'); - t.ok(f.dnAttributes); - t.end(); -}); - + const f = parseString('(sn:dn:2.4.6.8.10:=Barney Rubble)') + t.ok(f) + t.equal(f.matchType, 'sn') + t.equal(f.matchingRule, '2.4.6.8.10') + t.equal(f.matchValue, 'Barney Rubble') + t.ok(f.dnAttributes) + t.end() +}) test('parse RFC example 3', function (t) { - var f = filters.parseString('(o:dn:=Ace Industry)'); - t.ok(f); - t.equal(f.matchType, 'o'); - t.notOk(f.matchingRule); - t.equal(f.matchValue, 'Ace Industry'); - t.ok(f.dnAttributes); - t.end(); -}); - + const f = parseString('(o:dn:=Ace Industry)') + t.ok(f) + t.equal(f.matchType, 'o') + t.notOk(f.matchingRule) + t.equal(f.matchValue, 'Ace Industry') + t.ok(f.dnAttributes) + t.end() +}) test('parse RFC example 4', function (t) { - var f = filters.parseString('(:1.2.3:=Wilma Flintstone)'); - t.ok(f); - t.notOk(f.matchType); - t.equal(f.matchingRule, '1.2.3'); - t.equal(f.matchValue, 'Wilma Flintstone'); - t.notOk(f.dnAttributes); - t.end(); -}); - + const f = parseString('(:1.2.3:=Wilma Flintstone)') + t.ok(f) + t.notOk(f.matchType) + t.equal(f.matchingRule, '1.2.3') + t.equal(f.matchValue, 'Wilma Flintstone') + t.notOk(f.dnAttributes) + t.end() +}) test('parse RFC example 5', function (t) { - var f = filters.parseString('(:DN:2.4.6.8.10:=Dino)'); - t.ok(f); - t.notOk(f.matchType); - t.equal(f.matchingRule, '2.4.6.8.10'); - t.equal(f.matchValue, 'Dino'); - t.ok(f.dnAttributes); - t.end(); -}); + const f = parseString('(:DN:2.4.6.8.10:=Dino)') + t.ok(f) + t.notOk(f.matchType) + t.equal(f.matchingRule, '2.4.6.8.10') + t.equal(f.matchValue, 'Dino') + t.ok(f.dnAttributes) + t.end() +}) diff --git a/test/filters/ge.test.js b/test/filters/ge.test.js index 9f400f6..ecc0dd3 100644 --- a/test/filters/ge.test.js +++ b/test/filters/ge.test.js @@ -1,143 +1,116 @@ -// Copyright 2011 Mark Cavage, Inc. All rights reserved. - -var test = require('tape').test; - -var asn1 = require('asn1'); - - -///--- Globals - -var GreaterThanEqualsFilter; -var BerReader = asn1.BerReader; -var BerWriter = asn1.BerWriter; - - - -///--- Tests - -test('load library', function (t) { - var filters = require('../../lib/index').filters; - t.ok(filters); - GreaterThanEqualsFilter = filters.GreaterThanEqualsFilter; - t.ok(GreaterThanEqualsFilter); - t.end(); -}); +'use strict' +const { test } = require('tap') +const { BerReader, BerWriter } = require('asn1') +const { filters: { GreaterThanEqualsFilter } } = require('../../lib') test('Construct no args', function (t) { - var f = new GreaterThanEqualsFilter(); - t.ok(f); - t.ok(!f.attribute); - t.ok(!f.value); - t.end(); -}); - + const f = new GreaterThanEqualsFilter() + t.ok(f) + t.ok(!f.attribute) + t.ok(!f.value) + t.end() +}) test('Construct args', function (t) { - var f = new GreaterThanEqualsFilter({ + const f = new GreaterThanEqualsFilter({ attribute: 'foo', value: 'bar' - }); - t.ok(f); - t.equal(f.attribute, 'foo'); - t.equal(f.value, 'bar'); - t.equal(f.toString(), '(foo>=bar)'); - t.end(); -}); - + }) + t.ok(f) + t.equal(f.attribute, 'foo') + t.equal(f.value, 'bar') + t.equal(f.toString(), '(foo>=bar)') + t.end() +}) test('GH-109 = escape value only in toString()', function (t) { - var f = new GreaterThanEqualsFilter({ + const f = new GreaterThanEqualsFilter({ attribute: 'foo', value: 'ba(r)' - }); - t.ok(f); - t.equal(f.attribute, 'foo'); - t.equal(f.value, 'ba(r)'); - t.equal(f.toString(), '(foo>=ba\\28r\\29)'); - t.end(); -}); - + }) + t.ok(f) + t.equal(f.attribute, 'foo') + t.equal(f.value, 'ba(r)') + t.equal(f.toString(), '(foo>=ba\\28r\\29)') + t.end() +}) test('match true', function (t) { - var f = new GreaterThanEqualsFilter({ + const f = new GreaterThanEqualsFilter({ attribute: 'foo', value: 'bar' - }); - t.ok(f); - t.ok(f.matches({ foo: 'baz' })); - t.end(); -}); - + }) + t.ok(f) + t.ok(f.matches({ foo: 'baz' })) + t.end() +}) test('match multiple', function (t) { - var f = new GreaterThanEqualsFilter({ + const f = new GreaterThanEqualsFilter({ attribute: 'foo', value: 'bar' - }); - t.ok(f); - t.ok(f.matches({ foo: ['beuha', 'baz'] })); - t.end(); -}); + }) + t.ok(f) + t.ok(f.matches({ foo: ['beuha', 'baz'] })) + t.end() +}) test('match false', function (t) { - var f = new GreaterThanEqualsFilter({ + const f = new GreaterThanEqualsFilter({ attribute: 'foo', value: 'bar' - }); - t.ok(f); - t.ok(!f.matches({ foo: 'abc' })); - t.end(); -}); - + }) + t.ok(f) + t.ok(!f.matches({ foo: 'abc' })) + t.end() +}) test('parse ok', function (t) { - var writer = new BerWriter(); - writer.writeString('foo'); - writer.writeString('bar'); - - var f = new GreaterThanEqualsFilter(); - t.ok(f); - t.ok(f.parse(new BerReader(writer.buffer))); - t.ok(f.matches({ foo: 'bar' })); - t.end(); -}); + const writer = new BerWriter() + writer.writeString('foo') + writer.writeString('bar') + const f = new GreaterThanEqualsFilter() + t.ok(f) + t.ok(f.parse(new BerReader(writer.buffer))) + t.ok(f.matches({ foo: 'bar' })) + t.end() +}) test('parse bad', function (t) { - var writer = new BerWriter(); - writer.writeString('foo'); - writer.writeInt(20); + const writer = new BerWriter() + writer.writeString('foo') + writer.writeInt(20) - var f = new GreaterThanEqualsFilter(); - t.ok(f); + const f = new GreaterThanEqualsFilter() + t.ok(f) try { - f.parse(new BerReader(writer.buffer)); - t.fail('Should have thrown InvalidAsn1Error'); + f.parse(new BerReader(writer.buffer)) + t.fail('Should have thrown InvalidAsn1Error') } catch (e) { - t.equal(e.name, 'InvalidAsn1Error'); + t.equal(e.name, 'InvalidAsn1Error') } - t.end(); -}); - + t.end() +}) test('GH-109 = to ber uses plain values', function (t) { - var f = new GreaterThanEqualsFilter({ + let f = new GreaterThanEqualsFilter({ attribute: 'foo', value: 'ba(r)' - }); - t.ok(f); - var writer = new BerWriter(); - f.toBer(writer); + }) + t.ok(f) + const writer = new BerWriter() + f.toBer(writer) - f = new GreaterThanEqualsFilter(); - t.ok(f); + f = new GreaterThanEqualsFilter() + t.ok(f) - var reader = new BerReader(writer.buffer); - reader.readSequence(); - t.ok(f.parse(reader)); + const reader = new BerReader(writer.buffer) + reader.readSequence() + t.ok(f.parse(reader)) - t.equal(f.attribute, 'foo'); - t.equal(f.value, 'ba(r)'); - t.end(); -}); + t.equal(f.attribute, 'foo') + t.equal(f.value, 'ba(r)') + t.end() +}) diff --git a/test/filters/le.test.js b/test/filters/le.test.js index c709680..4f72fd6 100644 --- a/test/filters/le.test.js +++ b/test/filters/le.test.js @@ -1,144 +1,116 @@ -// Copyright 2011 Mark Cavage, Inc. All rights reserved. - -var test = require('tape').test; - -var asn1 = require('asn1'); - - -///--- Globals - -var LessThanEqualsFilter; -var BerReader = asn1.BerReader; -var BerWriter = asn1.BerWriter; - - - -///--- Tests - -test('load library', function (t) { - var filters = require('../../lib/index').filters; - t.ok(filters); - LessThanEqualsFilter = filters.LessThanEqualsFilter; - t.ok(LessThanEqualsFilter); - t.end(); -}); +'use strict' +const { test } = require('tap') +const { BerReader, BerWriter } = require('asn1') +const { filters: { LessThanEqualsFilter } } = require('../../lib') test('Construct no args', function (t) { - var f = new LessThanEqualsFilter(); - t.ok(f); - t.ok(!f.attribute); - t.ok(!f.value); - t.end(); -}); - + const f = new LessThanEqualsFilter() + t.ok(f) + t.ok(!f.attribute) + t.ok(!f.value) + t.end() +}) test('Construct args', function (t) { - var f = new LessThanEqualsFilter({ + const f = new LessThanEqualsFilter({ attribute: 'foo', value: 'bar' - }); - t.ok(f); - t.equal(f.attribute, 'foo'); - t.equal(f.value, 'bar'); - t.equal(f.toString(), '(foo<=bar)'); - t.end(); -}); - + }) + t.ok(f) + t.equal(f.attribute, 'foo') + t.equal(f.value, 'bar') + t.equal(f.toString(), '(foo<=bar)') + t.end() +}) test('GH-109 = escape value only in toString()', function (t) { - var f = new LessThanEqualsFilter({ + const f = new LessThanEqualsFilter({ attribute: 'foo', value: 'ba(r)' - }); - t.ok(f); - t.equal(f.attribute, 'foo'); - t.equal(f.value, 'ba(r)'); - t.equal(f.toString(), '(foo<=ba\\28r\\29)'); - t.end(); -}); - + }) + t.ok(f) + t.equal(f.attribute, 'foo') + t.equal(f.value, 'ba(r)') + t.equal(f.toString(), '(foo<=ba\\28r\\29)') + t.end() +}) test('match true', function (t) { - var f = new LessThanEqualsFilter({ + const f = new LessThanEqualsFilter({ attribute: 'foo', value: 'bar' - }); - t.ok(f); - t.ok(f.matches({ foo: 'abc' })); - t.end(); -}); - + }) + t.ok(f) + t.ok(f.matches({ foo: 'abc' })) + t.end() +}) test('match multiple', function (t) { - var f = new LessThanEqualsFilter({ + const f = new LessThanEqualsFilter({ attribute: 'foo', value: 'bar' - }); - t.ok(f); - t.ok(f.matches({ foo: ['abc', 'beuha'] })); - t.end(); -}); - + }) + t.ok(f) + t.ok(f.matches({ foo: ['abc', 'beuha'] })) + t.end() +}) test('match false', function (t) { - var f = new LessThanEqualsFilter({ + const f = new LessThanEqualsFilter({ attribute: 'foo', value: 'bar' - }); - t.ok(f); - t.ok(!f.matches({ foo: 'baz' })); - t.end(); -}); - + }) + t.ok(f) + t.ok(!f.matches({ foo: 'baz' })) + t.end() +}) test('parse ok', function (t) { - var writer = new BerWriter(); - writer.writeString('foo'); - writer.writeString('bar'); - - var f = new LessThanEqualsFilter(); - t.ok(f); - t.ok(f.parse(new BerReader(writer.buffer))); - t.ok(f.matches({ foo: 'bar' })); - t.end(); -}); + const writer = new BerWriter() + writer.writeString('foo') + writer.writeString('bar') + const f = new LessThanEqualsFilter() + t.ok(f) + t.ok(f.parse(new BerReader(writer.buffer))) + t.ok(f.matches({ foo: 'bar' })) + t.end() +}) test('parse bad', function (t) { - var writer = new BerWriter(); - writer.writeString('foo'); - writer.writeInt(20); + const writer = new BerWriter() + writer.writeString('foo') + writer.writeInt(20) - var f = new LessThanEqualsFilter(); - t.ok(f); + const f = new LessThanEqualsFilter() + t.ok(f) try { - f.parse(new BerReader(writer.buffer)); - t.fail('Should have thrown InvalidAsn1Error'); + f.parse(new BerReader(writer.buffer)) + t.fail('Should have thrown InvalidAsn1Error') } catch (e) { - t.equal(e.name, 'InvalidAsn1Error'); + t.equal(e.name, 'InvalidAsn1Error') } - t.end(); -}); - + t.end() +}) test('GH-109 = to ber uses plain values', function (t) { - var f = new LessThanEqualsFilter({ + let f = new LessThanEqualsFilter({ attribute: 'foo', value: 'ba(r)' - }); - t.ok(f); - var writer = new BerWriter(); - f.toBer(writer); + }) + t.ok(f) + const writer = new BerWriter() + f.toBer(writer) - f = new LessThanEqualsFilter(); - t.ok(f); + f = new LessThanEqualsFilter() + t.ok(f) - var reader = new BerReader(writer.buffer); - reader.readSequence(); - t.ok(f.parse(reader)); + const reader = new BerReader(writer.buffer) + reader.readSequence() + t.ok(f.parse(reader)) - t.equal(f.attribute, 'foo'); - t.equal(f.value, 'ba(r)'); - t.end(); -}); + t.equal(f.attribute, 'foo') + t.equal(f.value, 'ba(r)') + t.end() +}) diff --git a/test/filters/not.test.js b/test/filters/not.test.js index 782138d..b9164c1 100644 --- a/test/filters/not.test.js +++ b/test/filters/not.test.js @@ -1,70 +1,45 @@ -// Copyright 2011 Mark Cavage, Inc. All rights reserved. - -var test = require('tape').test; - -var asn1 = require('asn1'); - - -///--- Globals - -var EqualityFilter; -var NotFilter; - - - -///--- Tests - -test('load library', function (t) { - var filters = require('../../lib/index').filters; - t.ok(filters); - EqualityFilter = filters.EqualityFilter; - NotFilter = filters.NotFilter; - t.ok(EqualityFilter); - t.ok(NotFilter); - t.end(); -}); +'use strict' +const { test } = require('tap') +const { filters: { EqualityFilter, NotFilter } } = require('../../lib') test('Construct no args', function (t) { - t.ok(new NotFilter()); - t.end(); -}); - + t.ok(new NotFilter()) + t.end() +}) test('Construct args', function (t) { - var f = new NotFilter({ + const f = new NotFilter({ filter: new EqualityFilter({ attribute: 'foo', value: 'bar' }) - }); - t.ok(f); - t.equal(f.toString(), '(!(foo=bar))'); - t.end(); -}); - + }) + t.ok(f) + t.equal(f.toString(), '(!(foo=bar))') + t.end() +}) test('match true', function (t) { - var f = new NotFilter({ + const f = new NotFilter({ filter: new EqualityFilter({ attribute: 'foo', value: 'bar' }) - }); - t.ok(f); - t.ok(f.matches({ foo: 'baz' })); - t.end(); -}); - + }) + t.ok(f) + t.ok(f.matches({ foo: 'baz' })) + t.end() +}) test('match false', function (t) { - var f = new NotFilter({ + const f = new NotFilter({ filter: new EqualityFilter({ attribute: 'foo', value: 'bar' }) - }); - t.ok(f); - t.ok(!f.matches({ foo: 'bar' })); - t.end(); -}); + }) + t.ok(f) + t.ok(!f.matches({ foo: 'bar' })) + t.end() +}) diff --git a/test/filters/or.test.js b/test/filters/or.test.js index 21f91cd..dacb694 100644 --- a/test/filters/or.test.js +++ b/test/filters/or.test.js @@ -1,79 +1,54 @@ -// Copyright 2011 Mark Cavage, Inc. All rights reserved. - -var test = require('tape').test; - -var asn1 = require('asn1'); - - -///--- Globals - -var EqualityFilter; -var OrFilter; - - - -///--- Tests - -test('load library', function (t) { - var filters = require('../../lib/index').filters; - t.ok(filters); - EqualityFilter = filters.EqualityFilter; - OrFilter = filters.OrFilter; - t.ok(EqualityFilter); - t.ok(OrFilter); - t.end(); -}); +'use strict' +const { test } = require('tap') +const { filters: { EqualityFilter, OrFilter } } = require('../../lib') test('Construct no args', function (t) { - t.ok(new OrFilter()); - t.end(); -}); - + t.ok(new OrFilter()) + t.end() +}) test('Construct args', function (t) { - var f = new OrFilter(); + const f = new OrFilter() f.addFilter(new EqualityFilter({ attribute: 'foo', value: 'bar' - })); + })) f.addFilter(new EqualityFilter({ attribute: 'zig', value: 'zag' - })); - t.ok(f); - t.equal(f.toString(), '(|(foo=bar)(zig=zag))'); - t.end(); -}); - + })) + t.ok(f) + t.equal(f.toString(), '(|(foo=bar)(zig=zag))') + t.end() +}) test('match true', function (t) { - var f = new OrFilter(); + const f = new OrFilter() f.addFilter(new EqualityFilter({ attribute: 'foo', value: 'bar' - })); + })) f.addFilter(new EqualityFilter({ attribute: 'zig', value: 'zag' - })); - t.ok(f); - t.ok(f.matches({ foo: 'bar', zig: 'zonk' })); - t.end(); -}); - + })) + t.ok(f) + t.ok(f.matches({ foo: 'bar', zig: 'zonk' })) + t.end() +}) test('match false', function (t) { - var f = new OrFilter(); + const f = new OrFilter() f.addFilter(new EqualityFilter({ attribute: 'foo', value: 'bar' - })); + })) f.addFilter(new EqualityFilter({ attribute: 'zig', value: 'zag' - })); - t.ok(f); - t.ok(!f.matches({ foo: 'baz', zig: 'zonk' })); - t.end(); -}); + })) + t.ok(f) + t.ok(!f.matches({ foo: 'baz', zig: 'zonk' })) + t.end() +}) diff --git a/test/filters/parse.test.js b/test/filters/parse.test.js index d564c07..bfe6607 100644 --- a/test/filters/parse.test.js +++ b/test/filters/parse.test.js @@ -1,156 +1,141 @@ -// Copyright 2011 Mark Cavage, Inc. All rights reserved. - -var test = require('tape').test; - -var parse = require('../../lib/index').parseFilter; - +'use strict' +const { test } = require('tap') +const { parseFilter: parse } = require('../../lib') test('GH-48 XML Strings in filter', function (t) { - var str = '(&(CentralUIEnrollments=\\*)(objectClass=User))'; - var f = parse(str); - t.ok(f); - t.ok(f.filters); - t.equal(f.filters.length, 2); + const str = '(&(CentralUIEnrollments=\\*)(objectClass=User))' + const f = parse(str) + t.ok(f) + t.ok(f.filters) + t.equal(f.filters.length, 2) f.filters.forEach(function (filter) { - t.ok(filter.attribute); - }); - t.end(); -}); - + t.ok(filter.attribute) + }) + t.end() +}) test('GH-50 = in filter', function (t) { - var str = '(uniquemember=uuid=930896af-bf8c-48d4-885c-6573a94b1853, ' + - 'ou=users, o=smartdc)'; - var f = parse(str); - t.ok(f); - t.equal(f.attribute, 'uniquemember'); + const str = '(uniquemember=uuid=930896af-bf8c-48d4-885c-6573a94b1853, ' + + 'ou=users, o=smartdc)' + const f = parse(str) + t.ok(f) + t.equal(f.attribute, 'uniquemember') t.equal(f.value, - 'uuid=930896af-bf8c-48d4-885c-6573a94b1853, ou=users, o=smartdc'); - t.end(); -}); + 'uuid=930896af-bf8c-48d4-885c-6573a94b1853, ou=users, o=smartdc') + t.end() +}) +test('convert to hex code', function (t) { + const str = 'foo=bar\\(abcd\\e\\fg\\h\\69\\a' + const f = parse(str) + t.ok(f) + t.equal(f.attribute, 'foo') + t.equal(f.value, 'bar(abcdefghia') + t.equal(f.toString(), '(foo=bar\\28abcdefghia)') + t.end() +}) test('( in filter', function (t) { - var str = '(foo=bar\\()'; - var f = parse(str); - t.ok(f); - t.equal(f.attribute, 'foo'); - t.equal(f.value, 'bar('); - t.equal(f.toString(), '(foo=bar\\28)'); - t.end(); -}); + const str = '(foo=bar\\()' + const f = parse(str) + t.ok(f) + t.equal(f.attribute, 'foo') + t.equal(f.value, 'bar(') + t.equal(f.toString(), '(foo=bar\\28)') + t.end() +}) test(') in filter', function (t) { - var str = '(foo=bar\\))'; - var f = parse(str); - t.ok(f); - t.equal(f.attribute, 'foo'); - t.equal(f.value, 'bar)'); - t.equal(f.toString(), '(foo=bar\\29)'); - t.end(); -}); - - -test('( in filter', function (t) { - var str = 'foo(bar=baz\\()'; - var f = parse(str); - t.ok(f); - t.equal(f.attribute, 'foo(bar'); - t.equal(f.value, 'baz()'); - t.equal(f.toString(), '(foo\\28bar=baz\\28\\29)'); - t.end(); -}); - - -test('( in filter', function (t) { - var str = 'foo)(&(bar=baz)('; - var f = parse(str); - t.ok(f); - t.equal(f.attribute, 'foo)(&(bar'); - t.equal(f.value, 'baz)('); - t.equal(f.toString(), '(foo\\29\\28&\\28bar=baz\\29\\28)'); - t.end(); -}); - + const str = '(foo=bar\\))' + const f = parse(str) + t.ok(f) + t.equal(f.attribute, 'foo') + t.equal(f.value, 'bar)') + t.equal(f.toString(), '(foo=bar\\29)') + t.end() +}) test('\\ in filter', function (t) { - var str = '(foo=bar\\\\)'; - var f = parse(str); - t.ok(f); - t.equal(f.attribute, 'foo'); - t.equal(f.value, 'bar\\'); - t.equal(f.toString(), '(foo=bar\\5c)'); - t.end(); -}); + const str = '(foo=bar\\\\)' + const f = parse(str) + t.ok(f) + t.equal(f.attribute, 'foo') + t.equal(f.value, 'bar\\') + t.equal(f.toString(), '(foo=bar\\5c)') + t.end() +}) +test('not escaped \\ at end of filter', function (t) { + const str = 'foo=bar\\' + const f = parse(str) + t.ok(f) + t.equal(f.attribute, 'foo') + t.equal(f.value, 'bar\\') + t.equal(f.toString(), '(foo=bar\\5c)') + t.end() +}) test('* in equality filter', function (t) { - var str = '(foo=bar\\*)'; - var f = parse(str); - t.ok(f); - t.equal(f.attribute, 'foo'); - t.equal(f.value, 'bar*'); - t.equal(f.toString(), '(foo=bar\\2a)'); - t.end(); -}); - + const str = '(foo=bar\\*)' + const f = parse(str) + t.ok(f) + t.equal(f.attribute, 'foo') + t.equal(f.value, 'bar*') + t.equal(f.toString(), '(foo=bar\\2a)') + t.end() +}) test('* substr filter (prefix)', function (t) { - var str = '(foo=bar*)'; - var f = parse(str); - t.ok(f); - t.equal(f.attribute, 'foo'); - t.equal(f.initial, 'bar'); - t.equal(f.toString(), '(foo=bar*)'); - t.end(); -}); - + const str = '(foo=bar*)' + const f = parse(str) + t.ok(f) + t.equal(f.attribute, 'foo') + t.equal(f.initial, 'bar') + t.equal(f.toString(), '(foo=bar*)') + t.end() +}) test('GH-53 NotFilter', function (t) { - var str = '(&(objectClass=person)(!(objectClass=shadowAccount)))'; - var f = parse(str); - t.ok(f); - t.equal(f.type, 'and'); - t.equal(f.filters.length, 2); - t.equal(f.filters[0].type, 'equal'); - t.equal(f.filters[1].type, 'not'); - t.equal(f.filters[1].filter.type, 'equal'); - t.equal(f.filters[1].filter.attribute, 'objectClass'); - t.equal(f.filters[1].filter.value, 'shadowAccount'); - t.end(); -}); - + const str = '(&(objectClass=person)(!(objectClass=shadowAccount)))' + const f = parse(str) + t.ok(f) + t.equal(f.type, 'and') + t.equal(f.filters.length, 2) + t.equal(f.filters[0].type, 'equal') + t.equal(f.filters[1].type, 'not') + t.equal(f.filters[1].filter.type, 'equal') + t.equal(f.filters[1].filter.attribute, 'objectClass') + t.equal(f.filters[1].filter.value, 'shadowAccount') + t.end() +}) test('presence filter', function (t) { - var f = parse('(foo=*)'); - t.ok(f); - t.equal(f.type, 'present'); - t.equal(f.attribute, 'foo'); - t.equal(f.toString(), '(foo=*)'); - t.end(); -}); - + const f = parse('(foo=*)') + t.ok(f) + t.equal(f.type, 'present') + t.equal(f.attribute, 'foo') + t.equal(f.toString(), '(foo=*)') + t.end() +}) test('bogus filter', function (t) { t.throws(function () { - parse('foo>1'); - }); - t.end(); -}); - + parse('foo>1') + }) + t.end() +}) test('bogus filter !=', function (t) { t.throws(function () { - parse('foo!=1'); - }); - t.end(); -}); - + parse('foo!=1') + }) + t.end() +}) test('mismatched parens', function (t) { t.throws(function () { - parse('(&(foo=bar)(!(state=done))'); - }); - t.end(); -}); + parse('(&(foo=bar)(!(state=done))') + }) + t.end() +}) diff --git a/test/filters/presence.test.js b/test/filters/presence.test.js index ff40483..5d78dae 100644 --- a/test/filters/presence.test.js +++ b/test/filters/presence.test.js @@ -1,108 +1,83 @@ -// Copyright 2011 Mark Cavage, Inc. All rights reserved. - -var test = require('tape').test; - -var asn1 = require('asn1'); - - -///--- Globals - -var PresenceFilter; -var BerReader = asn1.BerReader; -var BerWriter = asn1.BerWriter; - - - -///--- Tests - -test('load library', function (t) { - var filters = require('../../lib/index').filters; - t.ok(filters); - PresenceFilter = filters.PresenceFilter; - t.ok(PresenceFilter); - t.end(); -}); +'use strict' +const { test } = require('tap') +const { BerReader, BerWriter } = require('asn1') +const { filters: { PresenceFilter } } = require('../../lib') test('Construct no args', function (t) { - var f = new PresenceFilter(); - t.ok(f); - t.ok(!f.attribute); - t.end(); -}); - + const f = new PresenceFilter() + t.ok(f) + t.ok(!f.attribute) + t.end() +}) test('Construct args', function (t) { - var f = new PresenceFilter({ + const f = new PresenceFilter({ attribute: 'foo' - }); - t.ok(f); - t.equal(f.attribute, 'foo'); - t.equal(f.toString(), '(foo=*)'); - t.end(); -}); + }) + t.ok(f) + t.equal(f.attribute, 'foo') + t.equal(f.toString(), '(foo=*)') + t.end() +}) test('GH-109 = escape value only in toString()', function (t) { - var f = new PresenceFilter({ + const f = new PresenceFilter({ attribute: 'fo)o' - }); - t.ok(f); - t.equal(f.attribute, 'fo)o'); - t.equal(f.toString(), '(fo\\29o=*)'); - t.end(); -}); - + }) + t.ok(f) + t.equal(f.attribute, 'fo)o') + t.equal(f.toString(), '(fo\\29o=*)') + t.end() +}) test('match true', function (t) { - var f = new PresenceFilter({ + const f = new PresenceFilter({ attribute: 'foo' - }); - t.ok(f); - t.ok(f.matches({ foo: 'bar' })); - t.end(); -}); - + }) + t.ok(f) + t.ok(f.matches({ foo: 'bar' })) + t.end() +}) test('match false', function (t) { - var f = new PresenceFilter({ + const f = new PresenceFilter({ attribute: 'foo' - }); - t.ok(f); - t.ok(!f.matches({ bar: 'foo' })); - t.end(); -}); - + }) + t.ok(f) + t.ok(!f.matches({ bar: 'foo' })) + t.end() +}) test('parse ok', function (t) { - var writer = new BerWriter(); - writer.writeString('foo', 0x87); + const writer = new BerWriter() + writer.writeString('foo', 0x87) - var f = new PresenceFilter(); - t.ok(f); - - var reader = new BerReader(writer.buffer); - reader.readSequence(); - t.ok(f.parse(reader)); - t.ok(f.matches({ foo: 'bar' })); - t.end(); -}); + const f = new PresenceFilter() + t.ok(f) + const reader = new BerReader(writer.buffer) + reader.readSequence() + t.ok(f.parse(reader)) + t.ok(f.matches({ foo: 'bar' })) + t.end() +}) test('GH-109 = to ber uses plain values', function (t) { - var f = new PresenceFilter({ + let f = new PresenceFilter({ attribute: 'f(o)o' - }); - t.ok(f); - var writer = new BerWriter(); - f.toBer(writer); + }) + t.ok(f) + const writer = new BerWriter() + f.toBer(writer) - f = new PresenceFilter(); - t.ok(f); + f = new PresenceFilter() + t.ok(f) - var reader = new BerReader(writer.buffer); - reader.readSequence(); - t.ok(f.parse(reader)); + const reader = new BerReader(writer.buffer) + reader.readSequence() + t.ok(f.parse(reader)) - t.equal(f.attribute, 'f(o)o'); - t.end(); -}); + t.equal(f.attribute, 'f(o)o') + t.end() +}) diff --git a/test/filters/substr.test.js b/test/filters/substr.test.js index 1c0a5b8..0526a28 100644 --- a/test/filters/substr.test.js +++ b/test/filters/substr.test.js @@ -1,181 +1,152 @@ -// Copyright 2011 Mark Cavage, Inc. All rights reserved. - -var test = require('tape').test; - -var asn1 = require('asn1'); - - -///--- Globals - -var SubstringFilter; -var BerReader = asn1.BerReader; -var BerWriter = asn1.BerWriter; - - - -///--- Tests - -test('load library', function (t) { - var filters = require('../../lib/index').filters; - t.ok(filters); - SubstringFilter = filters.SubstringFilter; - t.ok(SubstringFilter); - t.end(); -}); +'use strict' +const { test } = require('tap') +const { BerReader, BerWriter } = require('asn1') +const { filters: { SubstringFilter } } = require('../../lib') test('Construct no args', function (t) { - var f = new SubstringFilter(); - t.ok(f); - t.ok(!f.attribute); - t.ok(!f.value); - t.end(); -}); - + const f = new SubstringFilter() + t.ok(f) + t.ok(!f.attribute) + t.ok(!f.value) + t.end() +}) test('Construct args', function (t) { - var f = new SubstringFilter({ + const f = new SubstringFilter({ attribute: 'foo', initial: 'bar', any: ['zig', 'zag'], - 'final': 'baz' - }); - t.ok(f); - t.equal(f.attribute, 'foo'); - t.equal(f.initial, 'bar'); - t.equal(f.any.length, 2); - t.equal(f.any[0], 'zig'); - t.equal(f.any[1], 'zag'); - t.equal(f['final'], 'baz'); - t.equal(f.toString(), '(foo=bar*zig*zag*baz)'); - t.end(); -}); - + final: 'baz' + }) + t.ok(f) + t.equal(f.attribute, 'foo') + t.equal(f.initial, 'bar') + t.equal(f.any.length, 2) + t.equal(f.any[0], 'zig') + t.equal(f.any[1], 'zag') + t.equal(f.final, 'baz') + t.equal(f.toString(), '(foo=bar*zig*zag*baz)') + t.end() +}) test('GH-109 = escape value only in toString()', function (t) { - var f = new SubstringFilter({ + const f = new SubstringFilter({ attribute: 'fo(o', initial: 'ba(r)', any: ['zi)g', 'z(ag'], - 'final': '(baz)' - }); - t.ok(f); - t.equal(f.attribute, 'fo(o'); - t.equal(f.initial, 'ba(r)'); - t.equal(f.any.length, 2); - t.equal(f.any[0], 'zi)g'); - t.equal(f.any[1], 'z(ag'); - t.equal(f['final'], '(baz)'); - t.equal(f.toString(), '(fo\\28o=ba\\28r\\29*zi\\29g*z\\28ag*\\28baz\\29)'); - t.end(); -}); - + final: '(baz)' + }) + t.ok(f) + t.equal(f.attribute, 'fo(o') + t.equal(f.initial, 'ba(r)') + t.equal(f.any.length, 2) + t.equal(f.any[0], 'zi)g') + t.equal(f.any[1], 'z(ag') + t.equal(f.final, '(baz)') + t.equal(f.toString(), '(fo\\28o=ba\\28r\\29*zi\\29g*z\\28ag*\\28baz\\29)') + t.end() +}) test('match true', function (t) { - var f = new SubstringFilter({ + const f = new SubstringFilter({ attribute: 'foo', initial: 'bar', any: ['zig', 'zag'], - 'final': 'baz' - }); - t.ok(f); - t.ok(f.matches({ foo: 'barmoozigbarzagblahbaz' })); - t.end(); -}); - + final: 'baz' + }) + t.ok(f) + t.ok(f.matches({ foo: 'barmoozigbarzagblahbaz' })) + t.end() +}) test('match false', function (t) { - var f = new SubstringFilter({ + const f = new SubstringFilter({ attribute: 'foo', initial: 'bar', foo: ['zig', 'zag'], - 'final': 'baz' - }); - t.ok(f); - t.ok(!f.matches({ foo: 'bafmoozigbarzagblahbaz' })); - t.end(); -}); - + final: 'baz' + }) + t.ok(f) + t.ok(!f.matches({ foo: 'bafmoozigbarzagblahbaz' })) + t.end() +}) test('match any', function (t) { - var f = new SubstringFilter({ + const f = new SubstringFilter({ attribute: 'foo', initial: 'bar' - }); - t.ok(f); - t.ok(f.matches({ foo: ['beuha', 'barista']})); - t.end(); -}); - + }) + t.ok(f) + t.ok(f.matches({ foo: ['beuha', 'barista'] })) + t.end() +}) test('GH-109 = escape for regex in matches', function (t) { - var f = new SubstringFilter({ + const f = new SubstringFilter({ attribute: 'fo(o', initial: 'ba(r)', any: ['zi)g', 'z(ag'], - 'final': '(baz)' - }); - t.ok(f); - t.ok(f.matches({ 'fo(o': ['ba(r)_zi)g-z(ag~(baz)']})); - t.end(); -}); - + final: '(baz)' + }) + t.ok(f) + t.ok(f.matches({ 'fo(o': ['ba(r)_zi)g-z(ag~(baz)'] })) + t.end() +}) test('parse ok', function (t) { - var writer = new BerWriter(); - writer.writeString('foo'); - writer.startSequence(); - writer.writeString('bar', 0x80); - writer.writeString('bad', 0x81); - writer.writeString('baz', 0x82); - writer.endSequence(); - var f = new SubstringFilter(); - t.ok(f); - t.ok(f.parse(new BerReader(writer.buffer))); - t.ok(f.matches({ foo: 'bargoobadgoobaz' })); - t.end(); -}); - + const writer = new BerWriter() + writer.writeString('foo') + writer.startSequence() + writer.writeString('bar', 0x80) + writer.writeString('bad', 0x81) + writer.writeString('baz', 0x82) + writer.endSequence() + const f = new SubstringFilter() + t.ok(f) + t.ok(f.parse(new BerReader(writer.buffer))) + t.ok(f.matches({ foo: 'bargoobadgoobaz' })) + t.end() +}) test('parse bad', function (t) { - var writer = new BerWriter(); - writer.writeString('foo'); - writer.writeInt(20); + const writer = new BerWriter() + writer.writeString('foo') + writer.writeInt(20) - var f = new SubstringFilter(); - t.ok(f); + const f = new SubstringFilter() + t.ok(f) try { - f.parse(new BerReader(writer.buffer)); - t.fail('Should have thrown InvalidAsn1Error'); + f.parse(new BerReader(writer.buffer)) + t.fail('Should have thrown InvalidAsn1Error') } catch (e) { } - t.end(); -}); - + t.end() +}) test('GH-109 = to ber uses plain values', function (t) { - var f = new SubstringFilter({ + let f = new SubstringFilter({ attribute: 'fo(o', initial: 'ba(r)', any: ['zi)g', 'z(ag'], - 'final': '(baz)' - }); - t.ok(f); - var writer = new BerWriter(); - f.toBer(writer); + final: '(baz)' + }) + t.ok(f) + const writer = new BerWriter() + f.toBer(writer) - f = new SubstringFilter(); - t.ok(f); + f = new SubstringFilter() + t.ok(f) - var reader = new BerReader(writer.buffer); - reader.readSequence(); - t.ok(f.parse(reader)); + const reader = new BerReader(writer.buffer) + reader.readSequence() + t.ok(f.parse(reader)) - t.equal(f.attribute, 'fo(o'); - t.equal(f.initial, 'ba(r)'); - t.equal(f.any.length, 2); - t.equal(f.any[0], 'zi)g'); - t.equal(f.any[1], 'z(ag'); - t.equal(f['final'], '(baz)'); - t.end(); -}); + t.equal(f.attribute, 'fo(o') + t.equal(f.initial, 'ba(r)') + t.equal(f.any.length, 2) + t.equal(f.any[0], 'zi)g') + t.equal(f.any[1], 'z(ag') + t.equal(f.final, '(baz)') + t.end() +}) diff --git a/test/imgs/test.jpg b/test/imgs/test.jpg new file mode 100644 index 0000000..97ce548 Binary files /dev/null and b/test/imgs/test.jpg differ diff --git a/test/laundry.test.js b/test/laundry.test.js index d39abfc..796e48c 100644 --- a/test/laundry.test.js +++ b/test/laundry.test.js @@ -1,64 +1,43 @@ -// Copyright 2011 Mark Cavage, Inc. All rights reserved. +'use strict' -var test = require('tape').test; -var uuid = require('node-uuid'); +const tap = require('tap') +const uuid = require('uuid') +const { getSock } = require('./utils') +const ldap = require('../lib') -var ldap = require('../lib/index'); - - -///--- Globals - -var SOCKET = '/tmp/.' + uuid(); -var SUFFIX = 'dc=' + uuid(); - -var client; -var server; - - - -///--- Helper - -function search(t, options, callback) { - client.search(SUFFIX, options, function (err, res) { - t.ifError(err); - t.ok(res); - var found = false; +function search (t, options, callback) { + t.context.client.search(t.context.suffix, options, function (err, res) { + t.error(err) + t.ok(res) + let found = false res.on('searchEntry', function (entry) { - t.ok(entry); - found = true; - }); + t.ok(entry) + found = true + }) res.on('end', function () { - t.ok(found); - if (callback) - return callback(); - - return t.end(); - }); - }); + t.true(found) + if (callback) return callback() + return t.end() + }) + }) } +tap.beforeEach((done, t) => { + const suffix = `dc=${uuid()}` + const server = ldap.createServer() - -///--- Tests - -test('setup', function (t) { - server = ldap.createServer(); - t.ok(server); - server.listen(SOCKET, function () { - client = ldap.createClient({ - socketPath: SOCKET - }); - t.ok(client); - t.end(); - }); + t.context.server = server + t.context.socketPath = getSock() + t.context.suffix = suffix server.bind('cn=root', function (req, res, next) { - res.end(); - return next(); - }); - server.search(SUFFIX, function (req, res, next) { + res.end() + return next() + }) + + server.search(suffix, function (req, res, next) { var entry = { - dn: 'cn=foo, ' + SUFFIX, + dn: 'cn=foo, ' + suffix, attributes: { objectclass: ['person', 'top'], cn: 'Pogo Stick', @@ -66,19 +45,38 @@ test('setup', function (t) { givenname: 'ogo', mail: uuid() + '@pogostick.org' } - }; + } - if (req.filter.matches(entry.attributes)) - res.send(entry); + if (req.filter.matches(entry.attributes)) { res.send(entry) } - res.end(); - }); -}); + res.end() + }) + server.listen(t.context.socketPath, function () { + t.context.client = ldap.createClient({ + socketPath: t.context.socketPath + }) -test('Evolution search filter (GH-3)', function (t) { + t.context.client.on('connectError', (err) => { + t.context.server.close(() => done(err)) + }) + t.context.client.on('connect', (socket) => { + t.context.socket = socket + done() + }) + }) +}) + +tap.afterEach((done, t) => { + if (!t.context.client) return done() + t.context.client.unbind(() => { + t.context.server.close(done) + }) +}) + +tap.test('Evolution search filter (GH-3)', function (t) { // This is what Evolution sends, when searching for a contact 'ogo'. Wow. - var filter = + const filter = '(|(cn=ogo*)(givenname=ogo*)(sn=ogo*)(mail=ogo*)(member=ogo*)' + '(primaryphone=ogo*)(telephonenumber=ogo*)(homephone=ogo*)(mobile=ogo*)' + '(carphone=ogo*)(facsimiletelephonenumber=ogo*)' + @@ -94,47 +92,35 @@ test('Evolution search filter (GH-3)', function (t) { '(otherpostaladdress=ogo*)(jpegphoto=ogo*)(usercertificate=ogo*)' + '(labeleduri=ogo*)(displayname=ogo*)(spousename=ogo*)(note=ogo*)' + '(anniversary=ogo*)(birthdate=ogo*)(mailer=ogo*)(fileas=ogo*)' + - '(category=ogo*)(calcaluri=ogo*)(calfburl=ogo*)(icscalendar=ogo*))'; + '(category=ogo*)(calcaluri=ogo*)(calfburl=ogo*)(icscalendar=ogo*))' - return search(t, filter); -}); + return search(t, filter) +}) - -test('GH-49 Client errors on bad attributes', function (t) { - var searchOpts = { +tap.test('GH-49 Client errors on bad attributes', function (t) { + const searchOpts = { filter: 'cn=*ogo*', scope: 'one', attributes: 'dn' - }; - return search(t, searchOpts); -}); + } + return search(t, searchOpts) +}) +tap.test('GH-55 Client emits connect multiple times', function (t) { + const c = ldap.createClient({ + socketPath: t.context.socketPath + }) -test('GH-55 Client emits connect multiple times', function (t) { - var c = ldap.createClient({ - socketPath: SOCKET - }); - - var count = 0; + let count = 0 c.on('connect', function (socket) { - t.ok(socket); - count++; - c.bind('cn=root', 'secret', function (err, res) { - t.ifError(err); + t.ok(socket) + count++ + c.bind('cn=root', 'secret', function (err) { + t.ifError(err) c.unbind(function () { - t.equal(count, 1); - t.end(); - }); - }); - }); -}); - - -test('shutdown', function (t) { - client.unbind(function () { - server.on('close', function () { - t.end(); - }); - server.close(); - }); -}); + t.equal(count, 1) + t.end() + }) + }) + }) +}) diff --git a/test/lib/client/message-tracker/ge-window.test.js b/test/lib/client/message-tracker/ge-window.test.js new file mode 100644 index 0000000..10f0ef5 --- /dev/null +++ b/test/lib/client/message-tracker/ge-window.test.js @@ -0,0 +1,47 @@ +'use strict' + +const { test } = require('tap') +const { MAX_MSGID } = require('../../../../lib/client/constants') +const geWindow = require('../../../../lib/client/message-tracker/ge-window') + +test('comp > (ref in upper window) => true', async t => { + const ref = Math.floor(MAX_MSGID / 2) + 10 + const comp = ref + 10 + const result = geWindow(ref, comp) + t.is(result, true) +}) + +test('comp < (ref in upper window) => false', async t => { + const ref = Math.floor(MAX_MSGID / 2) + 10 + const comp = ref - 5 + const result = geWindow(ref, comp) + t.is(result, false) +}) + +test('comp > (ref in lower window) => true', async t => { + const ref = Math.floor(MAX_MSGID / 2) - 10 + const comp = ref + 20 + const result = geWindow(ref, comp) + t.is(result, true) +}) + +test('comp < (ref in lower window) => false', async t => { + const ref = Math.floor(MAX_MSGID / 2) - 10 + const comp = ref - 5 + const result = geWindow(ref, comp) + t.is(result, false) +}) + +test('(max === MAX_MSGID) && (comp > ref) => true', async t => { + const ref = MAX_MSGID - Math.floor(MAX_MSGID / 2) + const comp = ref + 1 + const result = geWindow(ref, comp) + t.is(result, true) +}) + +test('(max === MAX_MSGID) && (comp < ref) => false', async t => { + const ref = MAX_MSGID - Math.floor(MAX_MSGID / 2) + const comp = ref - 1 + const result = geWindow(ref, comp) + t.is(result, false) +}) diff --git a/test/lib/client/message-tracker/id-generator.test.js b/test/lib/client/message-tracker/id-generator.test.js new file mode 100644 index 0000000..a205088 --- /dev/null +++ b/test/lib/client/message-tracker/id-generator.test.js @@ -0,0 +1,21 @@ +'use strict' + +const { test } = require('tap') +const { MAX_MSGID } = require('../../../../lib/client/constants') +const idGeneratorFactory = require('../../../../lib/client/message-tracker/id-generator') + +test('starts at 0', async t => { + const nextID = idGeneratorFactory() + const currentID = nextID() + t.is(currentID, 1) +}) + +test('handles wrapping around', async t => { + const nextID = idGeneratorFactory(MAX_MSGID - 2) + + let currentID = nextID() + t.is(currentID, MAX_MSGID - 1) + + currentID = nextID() + t.is(currentID, 1) +}) diff --git a/test/lib/client/message-tracker/index.test.js b/test/lib/client/message-tracker/index.test.js new file mode 100644 index 0000000..8bfe886 --- /dev/null +++ b/test/lib/client/message-tracker/index.test.js @@ -0,0 +1,201 @@ +'use strict' + +const tap = require('tap') +const messageTrackerFactory = require('../../../../lib/client/message-tracker/') + +tap.test('options', t => { + t.test('requires an options object', async t => { + try { + messageTrackerFactory() + } catch (error) { + t.match(error, /options object is required/) + } + + try { + messageTrackerFactory([]) + } catch (error) { + t.match(error, /options object is required/) + } + + try { + messageTrackerFactory('') + } catch (error) { + t.match(error, /options object is required/) + } + + try { + messageTrackerFactory(42) + } catch (error) { + t.match(error, /options object is required/) + } + }) + + t.test('requires id to be a string', async t => { + try { + messageTrackerFactory({ id: {} }) + } catch (error) { + t.match(error, /options\.id string is required/) + } + + try { + messageTrackerFactory({ id: [] }) + } catch (error) { + t.match(error, /options\.id string is required/) + } + + try { + messageTrackerFactory({ id: 42 }) + } catch (error) { + t.match(error, /options\.id string is required/) + } + }) + + t.test('requires parser to be an object', async t => { + try { + messageTrackerFactory({ id: 'foo', parser: 'bar' }) + } catch (error) { + t.match(error, /options\.parser object is required/) + } + + try { + messageTrackerFactory({ id: 'foo', parser: 42 }) + } catch (error) { + t.match(error, /options\.parser object is required/) + } + + try { + messageTrackerFactory({ id: 'foo', parser: [] }) + } catch (error) { + t.match(error, /options\.parser object is required/) + } + }) + + t.end() +}) + +tap.test('.pending', t => { + t.test('returns 0 for no messages', async t => { + const tracker = messageTrackerFactory({ id: 'foo', parser: {} }) + t.is(tracker.pending, 0) + }) + + t.test('returns 1 for 1 message', async t => { + const tracker = messageTrackerFactory({ id: 'foo', parser: {} }) + tracker.track({}, () => {}) + t.is(tracker.pending, 1) + }) + + t.end() +}) + +tap.test('#abandon', t => { + t.test('returns false if message does not exist', async t => { + const tracker = messageTrackerFactory({ id: 'foo', parser: {} }) + const result = tracker.abandon(1) + t.is(result, false) + }) + + t.test('returns true if message is abandoned', async t => { + const tracker = messageTrackerFactory({ id: 'foo', parser: {} }) + tracker.track({}, {}) + const result = tracker.abandon(1) + t.is(result, true) + }) + + t.end() +}) + +tap.test('#fetch', t => { + t.test('returns handler for fetched message', async t => { + const tracker = messageTrackerFactory({ id: 'foo', parser: {} }) + tracker.track({}, handler) + const fetched = tracker.fetch(1) + t.is(fetched, handler) + + function handler () {} + }) + + t.test('returns handler for fetched abandoned message', async t => { + const tracker = messageTrackerFactory({ id: 'foo', parser: {} }) + tracker.track({}, handler) + tracker.track({ abandon: 'message' }, () => {}) + tracker.abandon(1) + const fetched = tracker.fetch(1) + t.is(fetched, handler) + + function handler () {} + }) + + t.test('returns null when message does not exist', async t => { + const tracker = messageTrackerFactory({ id: 'foo', parser: {} }) + const fetched = tracker.fetch(1) + t.is(fetched, null) + }) + + t.end() +}) + +tap.test('#purge', t => { + t.test('invokes cb for each tracked message', async t => { + t.plan(4) + let count = 0 + const tracker = messageTrackerFactory({ id: 'foo', parser: {} }) + tracker.track({}, handler1) + tracker.track({}, handler2) + tracker.purge(cb) + + function cb (msgID, handler) { + if (count === 0) { + t.is(msgID, 1) + t.is(handler, handler1) + count += 1 + return + } + t.is(msgID, 2) + t.is(handler, handler2) + } + + function handler1 () {} + function handler2 () {} + }) + + t.end() +}) + +tap.test('#remove', t => { + t.test('removes from the current track', async t => { + const tracker = messageTrackerFactory({ id: 'foo', parser: {} }) + tracker.track({}, () => {}) + tracker.remove(1) + t.is(tracker.pending, 0) + }) + + // Not a great test. It exercises the desired code path, but we probably + // should expose some insight into the abandoned track. + t.test('removes from the abandoned track', async t => { + const tracker = messageTrackerFactory({ id: 'foo', parser: {} }) + tracker.track({}, () => {}) + tracker.track({ abandon: 'message' }, () => {}) + tracker.abandon(1) + tracker.remove(1) + t.is(tracker.pending, 1) + }) + + t.end() +}) + +tap.test('#track', t => { + t.test('add messageID and tracks message', async t => { + const tracker = messageTrackerFactory({ id: 'foo', parser: {} }) + const msg = {} + tracker.track(msg, handler) + + t.deepEqual(msg, { messageID: 1 }) + const cb = tracker.fetch(1) + t.is(cb, handler) + + function handler () {} + }) + + t.end() +}) diff --git a/test/lib/client/message-tracker/purge-abandoned.test.js b/test/lib/client/message-tracker/purge-abandoned.test.js new file mode 100644 index 0000000..e5d16b3 --- /dev/null +++ b/test/lib/client/message-tracker/purge-abandoned.test.js @@ -0,0 +1,64 @@ +'use strict' + +const { test } = require('tap') +const { MAX_MSGID } = require('../../../../lib/client/constants') +const purgeAbandoned = require('../../../../lib/client/message-tracker/purge-abandoned') + +test('clears queue if only one message present', async t => { + t.plan(3) + const abandoned = new Map() + abandoned.set(1, { age: 2, cb }) + + purgeAbandoned(2, abandoned) + t.is(abandoned.size, 0) + + function cb (err) { + t.is(err.name, 'AbandonedError') + t.is(err.message, 'client request abandoned') + } +}) + +test('clears queue if multiple messages present', async t => { + t.plan(5) + const abandoned = new Map() + abandoned.set(1, { age: 2, cb }) + abandoned.set(2, { age: 3, cb }) + + purgeAbandoned(4, abandoned) + t.is(abandoned.size, 0) + + function cb (err) { + t.is(err.name, 'AbandonedError') + t.is(err.message, 'client request abandoned') + } +}) + +test('message id has wrappred around', async t => { + t.plan(3) + const abandoned = new Map() + abandoned.set(MAX_MSGID - 1, { age: MAX_MSGID, cb }) + + // The "abandon" message was sent with an id of "MAX_MSGID". So the message + // that is triggering the purge was the "first" message in the new sequence + // of message identifiers. + purgeAbandoned(1, abandoned) + t.is(abandoned.size, 0) + + function cb (err) { + t.is(err.name, 'AbandonedError') + t.is(err.message, 'client request abandoned') + } +}) + +test('does not clear if window not met', async t => { + t.plan(1) + const abandoned = new Map() + abandoned.set(1, { age: 2, cb }) + + purgeAbandoned(1, abandoned) + t.is(abandoned.size, 1) + + function cb () { + t.fail('should not be invoked') + } +}) diff --git a/test/lib/client/request-queue/enqueue.test.js b/test/lib/client/request-queue/enqueue.test.js new file mode 100644 index 0000000..28fab4f --- /dev/null +++ b/test/lib/client/request-queue/enqueue.test.js @@ -0,0 +1,82 @@ +'use strict' + +const { test } = require('tap') +const enqueue = require('../../../../lib/client/request-queue/enqueue') + +test('rejects new requests if size is exceeded', async t => { + const q = { _queue: { length: 5 }, size: 5 } + const result = enqueue.call(q, 'foo', 'bar', {}, {}) + t.false(result) +}) + +test('rejects new requests if queue is frozen', async t => { + const q = { _queue: { length: 0 }, size: 5, _frozen: true } + const result = enqueue.call(q, 'foo', 'bar', {}, {}) + t.false(result) +}) + +test('adds a request and returns if no timeout', async t => { + const q = { + _queue: { + length: 0, + add (obj) { + t.deepEqual(obj, { + message: 'foo', + expect: 'bar', + emitter: 'baz', + cb: 'bif' + }) + } + }, + _frozen: false, + timeout: 0 + } + const result = enqueue.call(q, 'foo', 'bar', 'baz', 'bif') + t.true(result) +}) + +test('adds a request and returns timer not set', async t => { + const q = { + _queue: { + length: 0, + add (obj) { + t.deepEqual(obj, { + message: 'foo', + expect: 'bar', + emitter: 'baz', + cb: 'bif' + }) + } + }, + _frozen: false, + timeout: 100, + _timer: null + } + const result = enqueue.call(q, 'foo', 'bar', 'baz', 'bif') + t.true(result) +}) + +test('adds a request, returns true, and clears queue', t => { + // Must not be an async test due to an internal `setTimeout` + t.plan(4) + const q = { + _queue: { + length: 0, + add (obj) { + t.deepEqual(obj, { + message: 'foo', + expect: 'bar', + emitter: 'baz', + cb: 'bif' + }) + } + }, + _frozen: false, + timeout: 5, + _timer: 123, + freeze () { t.pass() }, + purge () { t.pass() } + } + const result = enqueue.call(q, 'foo', 'bar', 'baz', 'bif') + t.true(result) +}) diff --git a/test/lib/client/request-queue/flush.test.js b/test/lib/client/request-queue/flush.test.js new file mode 100644 index 0000000..96dc7c6 --- /dev/null +++ b/test/lib/client/request-queue/flush.test.js @@ -0,0 +1,51 @@ +'use strict' + +const { test } = require('tap') +const flush = require('../../../../lib/client/request-queue/flush') + +test('clears timer', async t => { + t.plan(2) + const q = { + _timer: 123, + _queue: { + values () { + return [] + }, + clear () { + t.pass() + } + } + } + flush.call(q) + t.is(q._timer, null) +}) + +test('invokes callback with parameters', async t => { + t.plan(6) + const req = { + message: 'foo', + expect: 'bar', + emitter: 'baz', + cb: theCB + } + const q = { + _timer: 123, + _queue: { + values () { + return [req] + }, + clear () { + t.pass() + } + } + } + flush.call(q, (message, expect, emitter, cb) => { + t.is(message, 'foo') + t.is(expect, 'bar') + t.is(emitter, 'baz') + t.is(cb, theCB) + }) + t.is(q._timer, null) + + function theCB () {} +}) diff --git a/test/lib/client/request-queue/purge.test.js b/test/lib/client/request-queue/purge.test.js new file mode 100644 index 0000000..198e83b --- /dev/null +++ b/test/lib/client/request-queue/purge.test.js @@ -0,0 +1,18 @@ +'use strict' + +const { test } = require('tap') +const purge = require('../../../../lib/client/request-queue/purge') + +test('flushes the queue with timeout errors', async t => { + t.plan(3) + const q = { + flush (func) { + func('a', 'b', 'c', (err) => { + t.ok(err) + t.is(err.name, 'TimeoutError') + t.is(err.message, 'request queue timeout') + }) + } + } + purge.call(q) +}) diff --git a/test/messages/add_request.test.js b/test/messages/add_request.test.js index 0361ddc..1c287ca 100644 --- a/test/messages/add_request.test.js +++ b/test/messages/add_request.test.js @@ -1,144 +1,118 @@ -// Copyright 2011 Mark Cavage, Inc. All rights reserved. +'use strict' -var test = require('tape').test; +const { test } = require('tap') +const { BerReader, BerWriter } = require('asn1') +const { AddRequest, Attribute, dn } = require('../../lib') -var asn1 = require('asn1'); +test('new no args', t => { + t.ok(new AddRequest()) + t.end() +}) - -///--- Globals - -var BerReader = asn1.BerReader; -var BerWriter = asn1.BerWriter; -var AddRequest; -var Attribute; -var dn; - -///--- Tests - -test('load library', function (t) { - AddRequest = require('../../lib/index').AddRequest; - Attribute = require('../../lib/index').Attribute; - dn = require('../../lib/index').dn; - t.ok(AddRequest); - t.ok(Attribute); - t.ok(dn); - t.end(); -}); - - -test('new no args', function (t) { - t.ok(new AddRequest()); - t.end(); -}); - - -test('new with args', function (t) { - var req = new AddRequest({ +test('new with args', t => { + const req = new AddRequest({ entry: dn.parse('cn=foo, o=test'), - attributes: [new Attribute({type: 'cn', vals: ['foo']}), - new Attribute({type: 'objectclass', vals: ['person']})] - }); - t.ok(req); - t.equal(req.dn.toString(), 'cn=foo, o=test'); - t.equal(req.attributes.length, 2); - t.equal(req.attributes[0].type, 'cn'); - t.equal(req.attributes[0].vals[0], 'foo'); - t.equal(req.attributes[1].type, 'objectclass'); - t.equal(req.attributes[1].vals[0], 'person'); - t.end(); -}); + attributes: [new Attribute({ type: 'cn', vals: ['foo'] }), + new Attribute({ type: 'objectclass', vals: ['person'] })] + }) + t.ok(req) + t.equal(req.dn.toString(), 'cn=foo, o=test') + t.equal(req.attributes.length, 2) + t.equal(req.attributes[0].type, 'cn') + t.equal(req.attributes[0].vals[0], 'foo') + t.equal(req.attributes[1].type, 'objectclass') + t.equal(req.attributes[1].vals[0], 'person') + t.end() +}) +test('parse', t => { + const ber = new BerWriter() + ber.writeString('cn=foo, o=test') -test('parse', function (t) { - var ber = new BerWriter(); - ber.writeString('cn=foo, o=test'); + ber.startSequence() - ber.startSequence(); + ber.startSequence() + ber.writeString('cn') + ber.startSequence(0x31) + ber.writeString('foo') + ber.endSequence() + ber.endSequence() - ber.startSequence(); - ber.writeString('cn'); - ber.startSequence(0x31); - ber.writeString('foo'); - ber.endSequence(); - ber.endSequence(); + ber.startSequence() + ber.writeString('objectclass') + ber.startSequence(0x31) + ber.writeString('person') + ber.endSequence() + ber.endSequence() - ber.startSequence(); - ber.writeString('objectclass'); - ber.startSequence(0x31); - ber.writeString('person'); - ber.endSequence(); - ber.endSequence(); + ber.endSequence() - ber.endSequence(); + const req = new AddRequest() + t.ok(req._parse(new BerReader(ber.buffer))) + t.equal(req.dn.toString(), 'cn=foo, o=test') + t.equal(req.attributes.length, 2) + t.equal(req.attributes[0].type, 'cn') + t.equal(req.attributes[0].vals[0], 'foo') + t.equal(req.attributes[1].type, 'objectclass') + t.equal(req.attributes[1].vals[0], 'person') + t.end() +}) - var req = new AddRequest(); - t.ok(req._parse(new BerReader(ber.buffer))); - t.equal(req.dn.toString(), 'cn=foo, o=test'); - t.equal(req.attributes.length, 2); - t.equal(req.attributes[0].type, 'cn'); - t.equal(req.attributes[0].vals[0], 'foo'); - t.equal(req.attributes[1].type, 'objectclass'); - t.equal(req.attributes[1].vals[0], 'person'); - t.end(); -}); - - -test('toBer', function (t) { - var req = new AddRequest({ +test('toBer', t => { + const req = new AddRequest({ messageID: 123, entry: dn.parse('cn=foo, o=test'), - attributes: [new Attribute({type: 'cn', vals: ['foo']}), - new Attribute({type: 'objectclass', vals: ['person']})] - }); + attributes: [new Attribute({ type: 'cn', vals: ['foo'] }), + new Attribute({ type: 'objectclass', vals: ['person'] })] + }) - t.ok(req); + t.ok(req) - var ber = new BerReader(req.toBer()); - t.ok(ber); - t.equal(ber.readSequence(), 0x30); - t.equal(ber.readInt(), 123); - t.equal(ber.readSequence(), 0x68); - t.equal(ber.readString(), 'cn=foo, o=test'); - t.ok(ber.readSequence()); + const ber = new BerReader(req.toBer()) + t.ok(ber) + t.equal(ber.readSequence(), 0x30) + t.equal(ber.readInt(), 123) + t.equal(ber.readSequence(), 0x68) + t.equal(ber.readString(), 'cn=foo, o=test') + t.ok(ber.readSequence()) - t.ok(ber.readSequence()); - t.equal(ber.readString(), 'cn'); - t.equal(ber.readSequence(), 0x31); - t.equal(ber.readString(), 'foo'); + t.ok(ber.readSequence()) + t.equal(ber.readString(), 'cn') + t.equal(ber.readSequence(), 0x31) + t.equal(ber.readString(), 'foo') - t.ok(ber.readSequence()); - t.equal(ber.readString(), 'objectclass'); - t.equal(ber.readSequence(), 0x31); - t.equal(ber.readString(), 'person'); + t.ok(ber.readSequence()) + t.equal(ber.readString(), 'objectclass') + t.equal(ber.readSequence(), 0x31) + t.equal(ber.readString(), 'person') - t.end(); -}); + t.end() +}) - -test('toObject', function (t) { - var req = new AddRequest({ +test('toObject', t => { + const req = new AddRequest({ entry: dn.parse('cn=foo, o=test'), - attributes: [new Attribute({type: 'cn', vals: ['foo', 'bar']}), - new Attribute({type: 'objectclass', vals: ['person']})] - }); + attributes: [new Attribute({ type: 'cn', vals: ['foo', 'bar'] }), + new Attribute({ type: 'objectclass', vals: ['person'] })] + }) - t.ok(req); + t.ok(req) - var obj = req.toObject(); - t.ok(obj); + const obj = req.toObject() + t.ok(obj) - t.ok(obj.dn); - t.equal(obj.dn, 'cn=foo, o=test'); - t.ok(obj.attributes); - t.ok(obj.attributes.cn); - t.ok(Array.isArray(obj.attributes.cn)); - t.equal(obj.attributes.cn.length, 2); - t.equal(obj.attributes.cn[0], 'foo'); - t.equal(obj.attributes.cn[1], 'bar'); - t.ok(obj.attributes.objectclass); - t.ok(Array.isArray(obj.attributes.objectclass)); - t.equal(obj.attributes.objectclass.length, 1); - t.equal(obj.attributes.objectclass[0], 'person'); + t.ok(obj.dn) + t.equal(obj.dn, 'cn=foo, o=test') + t.ok(obj.attributes) + t.ok(obj.attributes.cn) + t.ok(Array.isArray(obj.attributes.cn)) + t.equal(obj.attributes.cn.length, 2) + t.equal(obj.attributes.cn[0], 'foo') + t.equal(obj.attributes.cn[1], 'bar') + t.ok(obj.attributes.objectclass) + t.ok(Array.isArray(obj.attributes.objectclass)) + t.equal(obj.attributes.objectclass.length, 1) + t.equal(obj.attributes.objectclass[0], 'person') - t.end(); -}); + t.end() +}) diff --git a/test/messages/add_response.test.js b/test/messages/add_response.test.js index 18ba114..cb06d16 100644 --- a/test/messages/add_response.test.js +++ b/test/messages/add_response.test.js @@ -1,76 +1,56 @@ -// Copyright 2011 Mark Cavage, Inc. All rights reserved. - -var test = require('tape').test; - -var asn1 = require('asn1'); - - -///--- Globals - -var BerReader = asn1.BerReader; -var BerWriter = asn1.BerWriter; -var AddResponse; - - -///--- Tests - -test('load library', function (t) { - AddResponse = require('../../lib/index').AddResponse; - t.ok(AddResponse); - t.end(); -}); +'use strict' +const { test } = require('tap') +const { BerReader, BerWriter } = require('asn1') +const { AddResponse } = require('../../lib') test('new no args', function (t) { - t.ok(new AddResponse()); - t.end(); -}); - + t.ok(new AddResponse()) + t.end() +}) test('new with args', function (t) { - var res = new AddResponse({ + const res = new AddResponse({ messageID: 123, status: 0 - }); - t.ok(res); - t.equal(res.messageID, 123); - t.equal(res.status, 0); - t.end(); -}); - + }) + t.ok(res) + t.equal(res.messageID, 123) + t.equal(res.status, 0) + t.end() +}) test('parse', function (t) { - var ber = new BerWriter(); - ber.writeEnumeration(0); - ber.writeString('cn=root'); - ber.writeString('foo'); - - var res = new AddResponse(); - t.ok(res._parse(new BerReader(ber.buffer))); - t.equal(res.status, 0); - t.equal(res.matchedDN, 'cn=root'); - t.equal(res.errorMessage, 'foo'); - t.end(); -}); + const ber = new BerWriter() + ber.writeEnumeration(0) + ber.writeString('cn=root') + ber.writeString('foo') + const res = new AddResponse() + t.ok(res._parse(new BerReader(ber.buffer))) + t.equal(res.status, 0) + t.equal(res.matchedDN, 'cn=root') + t.equal(res.errorMessage, 'foo') + t.end() +}) test('toBer', function (t) { - var res = new AddResponse({ + const res = new AddResponse({ messageID: 123, status: 3, matchedDN: 'cn=root', errorMessage: 'foo' - }); - t.ok(res); + }) + t.ok(res) - var ber = new BerReader(res.toBer()); - t.ok(ber); - t.equal(ber.readSequence(), 0x30); - t.equal(ber.readInt(), 123); - t.equal(ber.readSequence(), 0x69); - t.equal(ber.readEnumeration(), 3); - t.equal(ber.readString(), 'cn=root'); - t.equal(ber.readString(), 'foo'); + const ber = new BerReader(res.toBer()) + t.ok(ber) + t.equal(ber.readSequence(), 0x30) + t.equal(ber.readInt(), 123) + t.equal(ber.readSequence(), 0x69) + t.equal(ber.readEnumeration(), 3) + t.equal(ber.readString(), 'cn=root') + t.equal(ber.readString(), 'foo') - t.end(); -}); + t.end() +}) diff --git a/test/messages/bind_request.test.js b/test/messages/bind_request.test.js index 8ea84ae..f0639c9 100644 --- a/test/messages/bind_request.test.js +++ b/test/messages/bind_request.test.js @@ -1,80 +1,58 @@ -// Copyright 2011 Mark Cavage, Inc. All rights reserved. - -var test = require('tape').test; - -var asn1 = require('asn1'); - - -///--- Globals - -var BerReader = asn1.BerReader; -var BerWriter = asn1.BerWriter; -var BindRequest; -var dn; - -///--- Tests - -test('load library', function (t) { - BindRequest = require('../../lib/index').BindRequest; - dn = require('../../lib/index').dn; - t.ok(BindRequest); - t.ok(dn); - t.end(); -}); +'use strict' +const { test } = require('tap') +const { BerReader, BerWriter } = require('asn1') +const { BindRequest, dn } = require('../../lib') test('new no args', function (t) { - t.ok(new BindRequest()); - t.end(); -}); - + t.ok(new BindRequest()) + t.end() +}) test('new with args', function (t) { - var req = new BindRequest({ + const req = new BindRequest({ version: 3, name: dn.parse('cn=root'), credentials: 'secret' - }); - t.ok(req); - t.equal(req.version, 3); - t.equal(req.name.toString(), 'cn=root'); - t.equal(req.credentials, 'secret'); - t.end(); -}); - + }) + t.ok(req) + t.equal(req.version, 3) + t.equal(req.name.toString(), 'cn=root') + t.equal(req.credentials, 'secret') + t.end() +}) test('parse', function (t) { - var ber = new BerWriter(); - ber.writeInt(3); - ber.writeString('cn=root'); - ber.writeString('secret', 0x80); - - var req = new BindRequest(); - t.ok(req._parse(new BerReader(ber.buffer))); - t.equal(req.version, 3); - t.equal(req.dn.toString(), 'cn=root'); - t.equal(req.credentials, 'secret'); - t.end(); -}); + const ber = new BerWriter() + ber.writeInt(3) + ber.writeString('cn=root') + ber.writeString('secret', 0x80) + const req = new BindRequest() + t.ok(req._parse(new BerReader(ber.buffer))) + t.equal(req.version, 3) + t.equal(req.dn.toString(), 'cn=root') + t.equal(req.credentials, 'secret') + t.end() +}) test('toBer', function (t) { - var req = new BindRequest({ + const req = new BindRequest({ messageID: 123, version: 3, name: dn.parse('cn=root'), credentials: 'secret' - }); - t.ok(req); + }) + t.ok(req) - var ber = new BerReader(req.toBer()); - t.ok(ber); - t.equal(ber.readSequence(), 0x30); - t.equal(ber.readInt(), 123); - t.equal(ber.readSequence(), 0x60); - t.equal(ber.readInt(), 0x03); - t.equal(ber.readString(), 'cn=root'); - t.equal(ber.readString(0x80), 'secret'); + const ber = new BerReader(req.toBer()) + t.ok(ber) + t.equal(ber.readSequence(), 0x30) + t.equal(ber.readInt(), 123) + t.equal(ber.readSequence(), 0x60) + t.equal(ber.readInt(), 0x03) + t.equal(ber.readString(), 'cn=root') + t.equal(ber.readString(0x80), 'secret') - t.end(); -}); + t.end() +}) diff --git a/test/messages/bind_response.test.js b/test/messages/bind_response.test.js index bc7ea03..e9a8edd 100644 --- a/test/messages/bind_response.test.js +++ b/test/messages/bind_response.test.js @@ -1,76 +1,56 @@ -// Copyright 2011 Mark Cavage, Inc. All rights reserved. - -var test = require('tape').test; - -var asn1 = require('asn1'); - - -///--- Globals - -var BerReader = asn1.BerReader; -var BerWriter = asn1.BerWriter; -var BindResponse; - - -///--- Tests - -test('load library', function (t) { - BindResponse = require('../../lib/index').BindResponse; - t.ok(BindResponse); - t.end(); -}); +'use strict' +const { test } = require('tap') +const { BerReader, BerWriter } = require('asn1') +const { BindResponse } = require('../../lib') test('new no args', function (t) { - t.ok(new BindResponse()); - t.end(); -}); - + t.ok(new BindResponse()) + t.end() +}) test('new with args', function (t) { - var res = new BindResponse({ + const res = new BindResponse({ messageID: 123, status: 0 - }); - t.ok(res); - t.equal(res.messageID, 123); - t.equal(res.status, 0); - t.end(); -}); - + }) + t.ok(res) + t.equal(res.messageID, 123) + t.equal(res.status, 0) + t.end() +}) test('parse', function (t) { - var ber = new BerWriter(); - ber.writeEnumeration(0); - ber.writeString('cn=root'); - ber.writeString('foo'); - - var res = new BindResponse(); - t.ok(res._parse(new BerReader(ber.buffer))); - t.equal(res.status, 0); - t.equal(res.matchedDN, 'cn=root'); - t.equal(res.errorMessage, 'foo'); - t.end(); -}); + const ber = new BerWriter() + ber.writeEnumeration(0) + ber.writeString('cn=root') + ber.writeString('foo') + const res = new BindResponse() + t.ok(res._parse(new BerReader(ber.buffer))) + t.equal(res.status, 0) + t.equal(res.matchedDN, 'cn=root') + t.equal(res.errorMessage, 'foo') + t.end() +}) test('toBer', function (t) { - var res = new BindResponse({ + const res = new BindResponse({ messageID: 123, status: 3, matchedDN: 'cn=root', errorMessage: 'foo' - }); - t.ok(res); + }) + t.ok(res) - var ber = new BerReader(res.toBer()); - t.ok(ber); - t.equal(ber.readSequence(), 0x30); - t.equal(ber.readInt(), 123); - t.equal(ber.readSequence(), 0x61); - t.equal(ber.readEnumeration(), 3); - t.equal(ber.readString(), 'cn=root'); - t.equal(ber.readString(), 'foo'); + const ber = new BerReader(res.toBer()) + t.ok(ber) + t.equal(ber.readSequence(), 0x30) + t.equal(ber.readInt(), 123) + t.equal(ber.readSequence(), 0x61) + t.equal(ber.readEnumeration(), 3) + t.equal(ber.readString(), 'cn=root') + t.equal(ber.readString(), 'foo') - t.end(); -}); + t.end() +}) diff --git a/test/messages/compare_request.test.js b/test/messages/compare_request.test.js index de892ac..06d3565 100644 --- a/test/messages/compare_request.test.js +++ b/test/messages/compare_request.test.js @@ -1,87 +1,64 @@ -// Copyright 2011 Mark Cavage, Inc. All rights reserved. - -var test = require('tape').test; - -var asn1 = require('asn1'); - - -///--- Globals - -var BerReader = asn1.BerReader; -var BerWriter = asn1.BerWriter; -var CompareRequest; -var dn; - -///--- Tests - -test('load library', function (t) { - CompareRequest = require('../../lib/index').CompareRequest; - dn = require('../../lib/index').dn; - t.ok(CompareRequest); - t.ok(dn); - t.end(); -}); +'use strict' +const { test } = require('tap') +const { BerReader, BerWriter } = require('asn1') +const { CompareRequest, dn } = require('../../lib') test('new no args', function (t) { - t.ok(new CompareRequest()); - t.end(); -}); - + t.ok(new CompareRequest()) + t.end() +}) test('new with args', function (t) { - var req = new CompareRequest({ + const req = new CompareRequest({ entry: dn.parse('cn=foo, o=test'), attribute: 'sn', value: 'testy' - }); - t.ok(req); - t.equal(req.dn.toString(), 'cn=foo, o=test'); - t.equal(req.attribute, 'sn'); - t.equal(req.value, 'testy'); - t.end(); -}); - + }) + t.ok(req) + t.equal(req.dn.toString(), 'cn=foo, o=test') + t.equal(req.attribute, 'sn') + t.equal(req.value, 'testy') + t.end() +}) test('parse', function (t) { - var ber = new BerWriter(); - ber.writeString('cn=foo, o=test'); + const ber = new BerWriter() + ber.writeString('cn=foo, o=test') - ber.startSequence(); - ber.writeString('sn'); - ber.writeString('testy'); - ber.endSequence(); - - - var req = new CompareRequest(); - t.ok(req._parse(new BerReader(ber.buffer))); - t.equal(req.dn, 'cn=foo, o=test'); - t.equal(req.attribute, 'sn'); - t.equal(req.value, 'testy'); - t.end(); -}); + ber.startSequence() + ber.writeString('sn') + ber.writeString('testy') + ber.endSequence() + const req = new CompareRequest() + t.ok(req._parse(new BerReader(ber.buffer))) + t.equal(req.dn, 'cn=foo, o=test') + t.equal(req.attribute, 'sn') + t.equal(req.value, 'testy') + t.end() +}) test('toBer', function (t) { - var req = new CompareRequest({ + const req = new CompareRequest({ messageID: 123, entry: dn.parse('cn=foo, o=test'), attribute: 'sn', value: 'testy' - }); + }) - t.ok(req); + t.ok(req) - var ber = new BerReader(req.toBer()); - t.ok(ber); - t.equal(ber.readSequence(), 0x30); - t.equal(ber.readInt(), 123); - t.equal(ber.readSequence(), 0x6e); - t.equal(ber.readString(), 'cn=foo, o=test'); - t.ok(ber.readSequence()); + const ber = new BerReader(req.toBer()) + t.ok(ber) + t.equal(ber.readSequence(), 0x30) + t.equal(ber.readInt(), 123) + t.equal(ber.readSequence(), 0x6e) + t.equal(ber.readString(), 'cn=foo, o=test') + t.ok(ber.readSequence()) - t.equal(ber.readString(), 'sn'); - t.equal(ber.readString(), 'testy'); + t.equal(ber.readString(), 'sn') + t.equal(ber.readString(), 'testy') - t.end(); -}); + t.end() +}) diff --git a/test/messages/compare_response.test.js b/test/messages/compare_response.test.js index 535536a..92ad3af 100644 --- a/test/messages/compare_response.test.js +++ b/test/messages/compare_response.test.js @@ -1,76 +1,56 @@ -// Copyright 2011 Mark Cavage, Inc. All rights reserved. - -var test = require('tape').test; - -var asn1 = require('asn1'); - - -///--- Globals - -var BerReader = asn1.BerReader; -var BerWriter = asn1.BerWriter; -var CompareResponse; - - -///--- Tests - -test('load library', function (t) { - CompareResponse = require('../../lib/index').CompareResponse; - t.ok(CompareResponse); - t.end(); -}); +'use strict' +const { test } = require('tap') +const { BerReader, BerWriter } = require('asn1') +const { CompareResponse } = require('../../lib') test('new no args', function (t) { - t.ok(new CompareResponse()); - t.end(); -}); - + t.ok(new CompareResponse()) + t.end() +}) test('new with args', function (t) { - var res = new CompareResponse({ + const res = new CompareResponse({ messageID: 123, status: 0 - }); - t.ok(res); - t.equal(res.messageID, 123); - t.equal(res.status, 0); - t.end(); -}); - + }) + t.ok(res) + t.equal(res.messageID, 123) + t.equal(res.status, 0) + t.end() +}) test('parse', function (t) { - var ber = new BerWriter(); - ber.writeEnumeration(0); - ber.writeString('cn=root'); - ber.writeString('foo'); - - var res = new CompareResponse(); - t.ok(res._parse(new BerReader(ber.buffer))); - t.equal(res.status, 0); - t.equal(res.matchedDN, 'cn=root'); - t.equal(res.errorMessage, 'foo'); - t.end(); -}); + const ber = new BerWriter() + ber.writeEnumeration(0) + ber.writeString('cn=root') + ber.writeString('foo') + const res = new CompareResponse() + t.ok(res._parse(new BerReader(ber.buffer))) + t.equal(res.status, 0) + t.equal(res.matchedDN, 'cn=root') + t.equal(res.errorMessage, 'foo') + t.end() +}) test('toBer', function (t) { - var res = new CompareResponse({ + const res = new CompareResponse({ messageID: 123, status: 3, matchedDN: 'cn=root', errorMessage: 'foo' - }); - t.ok(res); + }) + t.ok(res) - var ber = new BerReader(res.toBer()); - t.ok(ber); - t.equal(ber.readSequence(), 0x30); - t.equal(ber.readInt(), 123); - t.equal(ber.readSequence(), 0x6f); - t.equal(ber.readEnumeration(), 3); - t.equal(ber.readString(), 'cn=root'); - t.equal(ber.readString(), 'foo'); + const ber = new BerReader(res.toBer()) + t.ok(ber) + t.equal(ber.readSequence(), 0x30) + t.equal(ber.readInt(), 123) + t.equal(ber.readSequence(), 0x6f) + t.equal(ber.readEnumeration(), 3) + t.equal(ber.readString(), 'cn=root') + t.equal(ber.readString(), 'foo') - t.end(); -}); + t.end() +}) diff --git a/test/messages/del_request.test.js b/test/messages/del_request.test.js index f8502ad..9de2a6e 100644 --- a/test/messages/del_request.test.js +++ b/test/messages/del_request.test.js @@ -1,72 +1,47 @@ -// Copyright 2011 Mark Cavage, Inc. All rights reserved. - - -var asn1 = require('asn1'); -var Logger = require('bunyan'); -var test = require('tape').test; - - -///--- Globals - -var BerReader = asn1.BerReader; -var BerWriter = asn1.BerWriter; -var DeleteRequest; -var dn; - - -///--- Tests - -test('load library', function (t) { - DeleteRequest = require('../../lib/index').DeleteRequest; - dn = require('../../lib/index').dn; - t.ok(DeleteRequest); - t.end(); -}); +'use strict' +const { test } = require('tap') +const { BerReader, BerWriter } = require('asn1') +const { DeleteRequest, dn } = require('../../lib') test('new no args', function (t) { - t.ok(new DeleteRequest()); - t.end(); -}); - + t.ok(new DeleteRequest()) + t.end() +}) test('new with args', function (t) { - var req = new DeleteRequest({ + const req = new DeleteRequest({ entry: dn.parse('cn=test') - }); - t.ok(req); - t.equal(req.dn.toString(), 'cn=test'); - t.end(); -}); - + }) + t.ok(req) + t.equal(req.dn.toString(), 'cn=test') + t.end() +}) test('parse', function (t) { - var ber = new BerWriter(); - ber.writeString('cn=test', 0x4a); - - var req = new DeleteRequest({ - log: new Logger({name: 'del_request.test.js'}) - }); - var reader = new BerReader(ber.buffer); - reader.readSequence(0x4a); - t.ok(req.parse(reader, reader.length)); - t.equal(req.dn.toString(), 'cn=test'); - t.end(); -}); + const ber = new BerWriter() + ber.writeString('cn=test', 0x4a) + const req = new DeleteRequest() + const reader = new BerReader(ber.buffer) + reader.readSequence(0x4a) + t.ok(req.parse(reader, reader.length)) + t.equal(req.dn.toString(), 'cn=test') + t.end() +}) test('toBer', function (t) { - var req = new DeleteRequest({ + const req = new DeleteRequest({ messageID: 123, entry: dn.parse('cn=test') - }); - t.ok(req); + }) + t.ok(req) - var ber = new BerReader(req.toBer()); - t.ok(ber); - t.equal(ber.readSequence(), 0x30); - t.equal(ber.readInt(), 123); - t.equal(ber.readString(0x4a), 'cn=test'); + const ber = new BerReader(req.toBer()) + t.ok(ber) + t.equal(ber.readSequence(), 0x30) + t.equal(ber.readInt(), 123) + t.equal(ber.readString(0x4a), 'cn=test') - t.end(); -}); + t.end() +}) diff --git a/test/messages/del_response.test.js b/test/messages/del_response.test.js index 72f8641..b45e13f 100644 --- a/test/messages/del_response.test.js +++ b/test/messages/del_response.test.js @@ -1,76 +1,56 @@ -// Copyright 2011 Mark Cavage, Inc. All rights reserved. - -var test = require('tape').test; - -var asn1 = require('asn1'); - - -///--- Globals - -var BerReader = asn1.BerReader; -var BerWriter = asn1.BerWriter; -var DeleteResponse; - - -///--- Tests - -test('load library', function (t) { - DeleteResponse = require('../../lib/index').DeleteResponse; - t.ok(DeleteResponse); - t.end(); -}); +'use strict' +const { test } = require('tap') +const { BerReader, BerWriter } = require('asn1') +const { DeleteResponse } = require('../../lib') test('new no args', function (t) { - t.ok(new DeleteResponse()); - t.end(); -}); - + t.ok(new DeleteResponse()) + t.end() +}) test('new with args', function (t) { - var res = new DeleteResponse({ + const res = new DeleteResponse({ messageID: 123, status: 0 - }); - t.ok(res); - t.equal(res.messageID, 123); - t.equal(res.status, 0); - t.end(); -}); - + }) + t.ok(res) + t.equal(res.messageID, 123) + t.equal(res.status, 0) + t.end() +}) test('parse', function (t) { - var ber = new BerWriter(); - ber.writeEnumeration(0); - ber.writeString('cn=root'); - ber.writeString('foo'); - - var res = new DeleteResponse(); - t.ok(res._parse(new BerReader(ber.buffer))); - t.equal(res.status, 0); - t.equal(res.matchedDN, 'cn=root'); - t.equal(res.errorMessage, 'foo'); - t.end(); -}); + const ber = new BerWriter() + ber.writeEnumeration(0) + ber.writeString('cn=root') + ber.writeString('foo') + const res = new DeleteResponse() + t.ok(res._parse(new BerReader(ber.buffer))) + t.equal(res.status, 0) + t.equal(res.matchedDN, 'cn=root') + t.equal(res.errorMessage, 'foo') + t.end() +}) test('toBer', function (t) { - var res = new DeleteResponse({ + const res = new DeleteResponse({ messageID: 123, status: 3, matchedDN: 'cn=root', errorMessage: 'foo' - }); - t.ok(res); + }) + t.ok(res) - var ber = new BerReader(res.toBer()); - t.ok(ber); - t.equal(ber.readSequence(), 0x30); - t.equal(ber.readInt(), 123); - t.equal(ber.readSequence(), 0x6b); - t.equal(ber.readEnumeration(), 3); - t.equal(ber.readString(), 'cn=root'); - t.equal(ber.readString(), 'foo'); + const ber = new BerReader(res.toBer()) + t.ok(ber) + t.equal(ber.readSequence(), 0x30) + t.equal(ber.readInt(), 123) + t.equal(ber.readSequence(), 0x6b) + t.equal(ber.readEnumeration(), 3) + t.equal(ber.readString(), 'cn=root') + t.equal(ber.readString(), 'foo') - t.end(); -}); + t.end() +}) diff --git a/test/messages/ext_request.test.js b/test/messages/ext_request.test.js index 5056a71..351924e 100644 --- a/test/messages/ext_request.test.js +++ b/test/messages/ext_request.test.js @@ -1,76 +1,125 @@ -// Copyright 2011 Mark Cavage, Inc. All rights reserved. - -var test = require('tape').test; - -var asn1 = require('asn1'); - - -///--- Globals - -var BerReader = asn1.BerReader; -var BerWriter = asn1.BerWriter; -var ExtendedRequest; -var dn; - -///--- Tests - -test('load library', function (t) { - ExtendedRequest = require('../../lib/index').ExtendedRequest; - dn = require('../../lib/index').dn; - t.ok(ExtendedRequest); - t.ok(dn); - t.end(); -}); +'use strict' +const { test } = require('tap') +const { BerReader, BerWriter } = require('asn1') +const { ExtendedRequest } = require('../../lib') test('new no args', function (t) { - t.ok(new ExtendedRequest()); - t.end(); -}); - + t.ok(new ExtendedRequest()) + t.end() +}) test('new with args', function (t) { - var req = new ExtendedRequest({ + const req = new ExtendedRequest({ requestName: '1.2.3.4', requestValue: 'test' - }); - t.ok(req); - t.equal(req.requestName, '1.2.3.4'); - t.equal(req.requestValue, 'test'); - t.end(); -}); + }) + t.ok(req) + t.equal(req.requestName, '1.2.3.4') + t.equal(req.requestValue, 'test') + t.is(Buffer.compare(req.requestValueBuffer, Buffer.from('test', 'utf8')), 0) + t.equal(req.value, 'test') + t.is(Buffer.compare(req.valueBuffer, Buffer.from('test', 'utf8')), 0) + t.end() +}) +test('new with buffer args', function (t) { + const req = new ExtendedRequest({ + requestName: '1.2.3.4', + requestValue: Buffer.from('test', 'utf8') + }) + t.ok(req) + t.equal(req.requestName, '1.2.3.4') + t.equal(req.requestValue, req.requestValueBuffer) + t.is(Buffer.compare(req.requestValueBuffer, Buffer.from('test', 'utf8')), 0) + t.equal(req.value, req.valueBuffer) + t.is(Buffer.compare(req.valueBuffer, Buffer.from('test', 'utf8')), 0) + t.end() +}) + +test('new no args set args', function (t) { + const req = new ExtendedRequest() + t.ok(req) + + req.name = '1.2.3.4' + t.equal(req.requestName, '1.2.3.4') + + req.value = 'test' + t.equal(req.requestValue, 'test') + t.is(Buffer.compare(req.requestValueBuffer, Buffer.from('test', 'utf8')), 0) + t.equal(req.value, 'test') + t.is(Buffer.compare(req.valueBuffer, Buffer.from('test', 'utf8')), 0) + + t.end() +}) + +test('new no args set args buffer', function (t) { + const req = new ExtendedRequest() + t.ok(req) + + req.name = '1.2.3.4' + t.equal(req.requestName, '1.2.3.4') + + req.value = Buffer.from('test', 'utf8') + t.equal(req.requestValue, req.requestValueBuffer) + t.is(Buffer.compare(req.requestValueBuffer, Buffer.from('test', 'utf8')), 0) + t.equal(req.value, req.valueBuffer) + t.is(Buffer.compare(req.valueBuffer, Buffer.from('test', 'utf8')), 0) + + t.end() +}) test('parse', function (t) { - var ber = new BerWriter(); - ber.writeString('1.2.3.4', 0x80); - ber.writeString('test', 0x81); - - - var req = new ExtendedRequest(); - t.ok(req._parse(new BerReader(ber.buffer))); - t.equal(req.requestName, '1.2.3.4'); - t.equal(req.requestValue, 'test'); - t.end(); -}); + const ber = new BerWriter() + ber.writeString('1.2.3.4', 0x80) + ber.writeString('test', 0x81) + const req = new ExtendedRequest() + t.ok(req._parse(new BerReader(ber.buffer))) + t.equal(req.requestName, '1.2.3.4') + t.equal(req.requestValue, 'test') + t.is(Buffer.compare(req.requestValueBuffer, Buffer.from('test', 'utf8')), 0) + t.equal(req.value, 'test') + t.is(Buffer.compare(req.valueBuffer, Buffer.from('test', 'utf8')), 0) + t.end() +}) test('toBer', function (t) { - var req = new ExtendedRequest({ + const req = new ExtendedRequest({ messageID: 123, requestName: '1.2.3.4', requestValue: 'test' - }); + }) - t.ok(req); + t.ok(req) - var ber = new BerReader(req.toBer()); - t.ok(ber); - t.equal(ber.readSequence(), 0x30); - t.equal(ber.readInt(), 123); - t.equal(ber.readSequence(), 0x77); - t.equal(ber.readString(0x80), '1.2.3.4'); - t.equal(ber.readString(0x81), 'test'); + const ber = new BerReader(req.toBer()) + t.ok(ber) + t.equal(ber.readSequence(), 0x30) + t.equal(ber.readInt(), 123) + t.equal(ber.readSequence(), 0x77) + t.equal(ber.readString(0x80), '1.2.3.4') + t.equal(ber.readString(0x81), 'test') - t.end(); -}); + t.end() +}) + +test('toBer from buffer', function (t) { + const req = new ExtendedRequest({ + messageID: 123, + requestName: '1.2.3.4', + requestValue: Buffer.from('test', 'utf8') + }) + + t.ok(req) + + const ber = new BerReader(req.toBer()) + t.ok(ber) + t.equal(ber.readSequence(), 0x30) + t.equal(ber.readInt(), 123) + t.equal(ber.readSequence(), 0x77) + t.equal(ber.readString(0x80), '1.2.3.4') + t.equal(ber.readString(0x81), 'test') + + t.end() +}) diff --git a/test/messages/ext_response.test.js b/test/messages/ext_response.test.js index 369793a..00f2272 100644 --- a/test/messages/ext_response.test.js +++ b/test/messages/ext_response.test.js @@ -1,88 +1,68 @@ -// Copyright 2011 Mark Cavage, Inc. All rights reserved. - -var test = require('tape').test; - -var asn1 = require('asn1'); - - -///--- Globals - -var BerReader = asn1.BerReader; -var BerWriter = asn1.BerWriter; -var ExtendedResponse; - - -///--- Tests - -test('load library', function (t) { - ExtendedResponse = require('../../lib/index').ExtendedResponse; - t.ok(ExtendedResponse); - t.end(); -}); +'use strict' +const { test } = require('tap') +const { BerReader, BerWriter } = require('asn1') +const { ExtendedResponse } = require('../../lib') test('new no args', function (t) { - t.ok(new ExtendedResponse()); - t.end(); -}); - + t.ok(new ExtendedResponse()) + t.end() +}) test('new with args', function (t) { - var res = new ExtendedResponse({ + const res = new ExtendedResponse({ messageID: 123, status: 0, responseName: '1.2.3.4', responseValue: 'test' - }); - t.ok(res); - t.equal(res.messageID, 123); - t.equal(res.status, 0); - t.equal(res.responseName, '1.2.3.4'); - t.equal(res.responseValue, 'test'); - t.end(); -}); - + }) + t.ok(res) + t.equal(res.messageID, 123) + t.equal(res.status, 0) + t.equal(res.responseName, '1.2.3.4') + t.equal(res.responseValue, 'test') + t.end() +}) test('parse', function (t) { - var ber = new BerWriter(); - ber.writeEnumeration(0); - ber.writeString('cn=root'); - ber.writeString('foo'); - ber.writeString('1.2.3.4', 0x8a); - ber.writeString('test', 0x8b); - - var res = new ExtendedResponse(); - t.ok(res._parse(new BerReader(ber.buffer))); - t.equal(res.status, 0); - t.equal(res.matchedDN, 'cn=root'); - t.equal(res.errorMessage, 'foo'); - t.equal(res.responseName, '1.2.3.4'); - t.equal(res.responseValue, 'test'); - t.end(); -}); + const ber = new BerWriter() + ber.writeEnumeration(0) + ber.writeString('cn=root') + ber.writeString('foo') + ber.writeString('1.2.3.4', 0x8a) + ber.writeString('test', 0x8b) + const res = new ExtendedResponse() + t.ok(res._parse(new BerReader(ber.buffer))) + t.equal(res.status, 0) + t.equal(res.matchedDN, 'cn=root') + t.equal(res.errorMessage, 'foo') + t.equal(res.responseName, '1.2.3.4') + t.equal(res.responseValue, 'test') + t.end() +}) test('toBer', function (t) { - var res = new ExtendedResponse({ + const res = new ExtendedResponse({ messageID: 123, status: 3, matchedDN: 'cn=root', errorMessage: 'foo', responseName: '1.2.3.4', responseValue: 'test' - }); - t.ok(res); + }) + t.ok(res) - var ber = new BerReader(res.toBer()); - t.ok(ber); - t.equal(ber.readSequence(), 0x30); - t.equal(ber.readInt(), 123); - t.equal(ber.readSequence(), 0x78); - t.equal(ber.readEnumeration(), 3); - t.equal(ber.readString(), 'cn=root'); - t.equal(ber.readString(), 'foo'); - t.equal(ber.readString(0x8a), '1.2.3.4'); - t.equal(ber.readString(0x8b), 'test'); + const ber = new BerReader(res.toBer()) + t.ok(ber) + t.equal(ber.readSequence(), 0x30) + t.equal(ber.readInt(), 123) + t.equal(ber.readSequence(), 0x78) + t.equal(ber.readEnumeration(), 3) + t.equal(ber.readString(), 'cn=root') + t.equal(ber.readString(), 'foo') + t.equal(ber.readString(0x8a), '1.2.3.4') + t.equal(ber.readString(0x8b), 'test') - t.end(); -}); + t.end() +}) diff --git a/test/messages/moddn_request.test.js b/test/messages/moddn_request.test.js index f3d2f1b..5d6035a 100644 --- a/test/messages/moddn_request.test.js +++ b/test/messages/moddn_request.test.js @@ -1,82 +1,60 @@ -// Copyright 2011 Mark Cavage, Inc. All rights reserved. - -var test = require('tape').test; - -var asn1 = require('asn1'); - - -///--- Globals - -var BerReader = asn1.BerReader; -var BerWriter = asn1.BerWriter; -var ModifyDNRequest; -var dn; - -///--- Tests - -test('load library', function (t) { - ModifyDNRequest = require('../../lib/index').ModifyDNRequest; - dn = require('../../lib/index').dn; - t.ok(ModifyDNRequest); - t.ok(dn); - t.end(); -}); +'use strict' +const { test } = require('tap') +const { BerReader, BerWriter } = require('asn1') +const { ModifyDNRequest, dn } = require('../../lib') test('new no args', function (t) { - t.ok(new ModifyDNRequest()); - t.end(); -}); - + t.ok(new ModifyDNRequest()) + t.end() +}) test('new with args', function (t) { - var req = new ModifyDNRequest({ + const req = new ModifyDNRequest({ entry: dn.parse('cn=foo, o=test'), newRdn: dn.parse('cn=foo2'), deleteOldRdn: true - }); - t.ok(req); - t.equal(req.dn.toString(), 'cn=foo, o=test'); - t.equal(req.newRdn.toString(), 'cn=foo2'); - t.equal(req.deleteOldRdn, true); - t.end(); -}); - + }) + t.ok(req) + t.equal(req.dn.toString(), 'cn=foo, o=test') + t.equal(req.newRdn.toString(), 'cn=foo2') + t.equal(req.deleteOldRdn, true) + t.end() +}) test('parse', function (t) { - var ber = new BerWriter(); - ber.writeString('cn=foo, o=test'); - ber.writeString('cn=foo2'); - ber.writeBoolean(true); + const ber = new BerWriter() + ber.writeString('cn=foo, o=test') + ber.writeString('cn=foo2') + ber.writeBoolean(true) - var req = new ModifyDNRequest(); - t.ok(req._parse(new BerReader(ber.buffer))); - t.equal(req.dn.toString(), 'cn=foo, o=test'); - t.equal(req.newRdn.toString(), 'cn=foo2'); - t.equal(req.deleteOldRdn, true); - - t.end(); -}); + const req = new ModifyDNRequest() + t.ok(req._parse(new BerReader(ber.buffer))) + t.equal(req.dn.toString(), 'cn=foo, o=test') + t.equal(req.newRdn.toString(), 'cn=foo2') + t.equal(req.deleteOldRdn, true) + t.end() +}) test('toBer', function (t) { - var req = new ModifyDNRequest({ + const req = new ModifyDNRequest({ messageID: 123, entry: dn.parse('cn=foo, o=test'), newRdn: dn.parse('cn=foo2'), deleteOldRdn: true - }); + }) - t.ok(req); + t.ok(req) - var ber = new BerReader(req.toBer()); - t.ok(ber); - t.equal(ber.readSequence(), 0x30); - t.equal(ber.readInt(), 123); - t.equal(ber.readSequence(), 0x6c); - t.equal(ber.readString(), 'cn=foo, o=test'); - t.equal(ber.readString(), 'cn=foo2'); - t.equal(ber.readBoolean(), true); + const ber = new BerReader(req.toBer()) + t.ok(ber) + t.equal(ber.readSequence(), 0x30) + t.equal(ber.readInt(), 123) + t.equal(ber.readSequence(), 0x6c) + t.equal(ber.readString(), 'cn=foo, o=test') + t.equal(ber.readString(), 'cn=foo2') + t.equal(ber.readBoolean(), true) - t.end(); -}); + t.end() +}) diff --git a/test/messages/moddn_response.test.js b/test/messages/moddn_response.test.js index 8804490..162df1b 100644 --- a/test/messages/moddn_response.test.js +++ b/test/messages/moddn_response.test.js @@ -1,76 +1,56 @@ -// Copyright 2011 Mark Cavage, Inc. All rights reserved. - -var test = require('tape').test; - -var asn1 = require('asn1'); - - -///--- Globals - -var BerReader = asn1.BerReader; -var BerWriter = asn1.BerWriter; -var ModifyDNResponse; - - -///--- Tests - -test('load library', function (t) { - ModifyDNResponse = require('../../lib/index').ModifyDNResponse; - t.ok(ModifyDNResponse); - t.end(); -}); +'use strict' +const { test } = require('tap') +const { BerReader, BerWriter } = require('asn1') +const { ModifyDNResponse } = require('../../lib') test('new no args', function (t) { - t.ok(new ModifyDNResponse()); - t.end(); -}); - + t.ok(new ModifyDNResponse()) + t.end() +}) test('new with args', function (t) { - var res = new ModifyDNResponse({ + const res = new ModifyDNResponse({ messageID: 123, status: 0 - }); - t.ok(res); - t.equal(res.messageID, 123); - t.equal(res.status, 0); - t.end(); -}); - + }) + t.ok(res) + t.equal(res.messageID, 123) + t.equal(res.status, 0) + t.end() +}) test('parse', function (t) { - var ber = new BerWriter(); - ber.writeEnumeration(0); - ber.writeString('cn=root'); - ber.writeString('foo'); - - var res = new ModifyDNResponse(); - t.ok(res._parse(new BerReader(ber.buffer))); - t.equal(res.status, 0); - t.equal(res.matchedDN, 'cn=root'); - t.equal(res.errorMessage, 'foo'); - t.end(); -}); + const ber = new BerWriter() + ber.writeEnumeration(0) + ber.writeString('cn=root') + ber.writeString('foo') + const res = new ModifyDNResponse() + t.ok(res._parse(new BerReader(ber.buffer))) + t.equal(res.status, 0) + t.equal(res.matchedDN, 'cn=root') + t.equal(res.errorMessage, 'foo') + t.end() +}) test('toBer', function (t) { - var res = new ModifyDNResponse({ + const res = new ModifyDNResponse({ messageID: 123, status: 3, matchedDN: 'cn=root', errorMessage: 'foo' - }); - t.ok(res); + }) + t.ok(res) - var ber = new BerReader(res.toBer()); - t.ok(ber); - t.equal(ber.readSequence(), 0x30); - t.equal(ber.readInt(), 123); - t.equal(ber.readSequence(), 0x6d); - t.equal(ber.readEnumeration(), 3); - t.equal(ber.readString(), 'cn=root'); - t.equal(ber.readString(), 'foo'); + const ber = new BerReader(res.toBer()) + t.ok(ber) + t.equal(ber.readSequence(), 0x30) + t.equal(ber.readInt(), 123) + t.equal(ber.readSequence(), 0x6d) + t.equal(ber.readEnumeration(), 3) + t.equal(ber.readString(), 'cn=root') + t.equal(ber.readString(), 'foo') - t.end(); -}); + t.end() +}) diff --git a/test/messages/modify_request.test.js b/test/messages/modify_request.test.js index 81609ba..22e8d1f 100644 --- a/test/messages/modify_request.test.js +++ b/test/messages/modify_request.test.js @@ -1,114 +1,86 @@ -// Copyright 2011 Mark Cavage, Inc. All rights reserved. - -var test = require('tape').test; - -var asn1 = require('asn1'); - - -///--- Globals - -var BerReader = asn1.BerReader; -var BerWriter = asn1.BerWriter; -var ModifyRequest; -var Attribute; -var Change; -var dn; - -///--- Tests - -test('load library', function (t) { - ModifyRequest = require('../../lib/index').ModifyRequest; - Attribute = require('../../lib/index').Attribute; - Change = require('../../lib/index').Change; - dn = require('../../lib/index').dn; - t.ok(ModifyRequest); - t.ok(Attribute); - t.ok(Change); - t.ok(dn); - t.end(); -}); +'use strict' +const { test } = require('tap') +const { BerReader, BerWriter } = require('asn1') +const { ModifyRequest, Attribute, Change, dn } = require('../../lib') test('new no args', function (t) { - t.ok(new ModifyRequest()); - t.end(); -}); - + t.ok(new ModifyRequest()) + t.end() +}) test('new with args', function (t) { - var req = new ModifyRequest({ + const req = new ModifyRequest({ object: dn.parse('cn=foo, o=test'), changes: [new Change({ operation: 'Replace', - modification: new Attribute({type: 'objectclass', vals: ['person']}) + modification: new Attribute({ type: 'objectclass', vals: ['person'] }) })] - }); - t.ok(req); - t.equal(req.dn.toString(), 'cn=foo, o=test'); - t.equal(req.changes.length, 1); - t.equal(req.changes[0].operation, 'replace'); - t.equal(req.changes[0].modification.type, 'objectclass'); - t.equal(req.changes[0].modification.vals[0], 'person'); - t.end(); -}); - + }) + t.ok(req) + t.equal(req.dn.toString(), 'cn=foo, o=test') + t.equal(req.changes.length, 1) + t.equal(req.changes[0].operation, 'replace') + t.equal(req.changes[0].modification.type, 'objectclass') + t.equal(req.changes[0].modification.vals[0], 'person') + t.end() +}) test('parse', function (t) { - var ber = new BerWriter(); - ber.writeString('cn=foo, o=test'); - ber.startSequence(); + const ber = new BerWriter() + ber.writeString('cn=foo, o=test') + ber.startSequence() - ber.startSequence(); - ber.writeEnumeration(0x02); + ber.startSequence() + ber.writeEnumeration(0x02) - ber.startSequence(); - ber.writeString('objectclass'); - ber.startSequence(0x31); - ber.writeString('person'); - ber.endSequence(); - ber.endSequence(); + ber.startSequence() + ber.writeString('objectclass') + ber.startSequence(0x31) + ber.writeString('person') + ber.endSequence() + ber.endSequence() - ber.endSequence(); + ber.endSequence() - ber.endSequence(); - - var req = new ModifyRequest(); - t.ok(req._parse(new BerReader(ber.buffer))); - t.equal(req.dn.toString(), 'cn=foo, o=test'); - t.equal(req.changes.length, 1); - t.equal(req.changes[0].operation, 'replace'); - t.equal(req.changes[0].modification.type, 'objectclass'); - t.equal(req.changes[0].modification.vals[0], 'person'); - t.end(); -}); + ber.endSequence() + const req = new ModifyRequest() + t.ok(req._parse(new BerReader(ber.buffer))) + t.equal(req.dn.toString(), 'cn=foo, o=test') + t.equal(req.changes.length, 1) + t.equal(req.changes[0].operation, 'replace') + t.equal(req.changes[0].modification.type, 'objectclass') + t.equal(req.changes[0].modification.vals[0], 'person') + t.end() +}) test('toBer', function (t) { - var req = new ModifyRequest({ + const req = new ModifyRequest({ messageID: 123, object: dn.parse('cn=foo, o=test'), changes: [new Change({ operation: 'Replace', - modification: new Attribute({type: 'objectclass', vals: ['person']}) + modification: new Attribute({ type: 'objectclass', vals: ['person'] }) })] - }); + }) - t.ok(req); + t.ok(req) - var ber = new BerReader(req.toBer()); - t.ok(ber); - t.equal(ber.readSequence(), 0x30); - t.equal(ber.readInt(), 123); - t.equal(ber.readSequence(), 0x66); - t.equal(ber.readString(), 'cn=foo, o=test'); - t.ok(ber.readSequence()); - t.ok(ber.readSequence()); - t.equal(ber.readEnumeration(), 0x02); + const ber = new BerReader(req.toBer()) + t.ok(ber) + t.equal(ber.readSequence(), 0x30) + t.equal(ber.readInt(), 123) + t.equal(ber.readSequence(), 0x66) + t.equal(ber.readString(), 'cn=foo, o=test') + t.ok(ber.readSequence()) + t.ok(ber.readSequence()) + t.equal(ber.readEnumeration(), 0x02) - t.ok(ber.readSequence()); - t.equal(ber.readString(), 'objectclass'); - t.equal(ber.readSequence(), 0x31); - t.equal(ber.readString(), 'person'); + t.ok(ber.readSequence()) + t.equal(ber.readString(), 'objectclass') + t.equal(ber.readSequence(), 0x31) + t.equal(ber.readString(), 'person') - t.end(); -}); + t.end() +}) diff --git a/test/messages/modify_response.test.js b/test/messages/modify_response.test.js index c35ae5c..37e0b22 100644 --- a/test/messages/modify_response.test.js +++ b/test/messages/modify_response.test.js @@ -1,76 +1,56 @@ -// Copyright 2011 Mark Cavage, Inc. All rights reserved. - -var test = require('tape').test; - -var asn1 = require('asn1'); - - -///--- Globals - -var BerReader = asn1.BerReader; -var BerWriter = asn1.BerWriter; -var ModifyResponse; - - -///--- Tests - -test('load library', function (t) { - ModifyResponse = require('../../lib/index').ModifyResponse; - t.ok(ModifyResponse); - t.end(); -}); +'use strict' +const { test } = require('tap') +const { BerReader, BerWriter } = require('asn1') +const { ModifyResponse } = require('../../lib') test('new no args', function (t) { - t.ok(new ModifyResponse()); - t.end(); -}); - + t.ok(new ModifyResponse()) + t.end() +}) test('new with args', function (t) { - var res = new ModifyResponse({ + const res = new ModifyResponse({ messageID: 123, status: 0 - }); - t.ok(res); - t.equal(res.messageID, 123); - t.equal(res.status, 0); - t.end(); -}); - + }) + t.ok(res) + t.equal(res.messageID, 123) + t.equal(res.status, 0) + t.end() +}) test('parse', function (t) { - var ber = new BerWriter(); - ber.writeEnumeration(0); - ber.writeString('cn=root'); - ber.writeString('foo'); - - var res = new ModifyResponse(); - t.ok(res._parse(new BerReader(ber.buffer))); - t.equal(res.status, 0); - t.equal(res.matchedDN, 'cn=root'); - t.equal(res.errorMessage, 'foo'); - t.end(); -}); + const ber = new BerWriter() + ber.writeEnumeration(0) + ber.writeString('cn=root') + ber.writeString('foo') + const res = new ModifyResponse() + t.ok(res._parse(new BerReader(ber.buffer))) + t.equal(res.status, 0) + t.equal(res.matchedDN, 'cn=root') + t.equal(res.errorMessage, 'foo') + t.end() +}) test('toBer', function (t) { - var res = new ModifyResponse({ + const res = new ModifyResponse({ messageID: 123, status: 3, matchedDN: 'cn=root', errorMessage: 'foo' - }); - t.ok(res); + }) + t.ok(res) - var ber = new BerReader(res.toBer()); - t.ok(ber); - t.equal(ber.readSequence(), 0x30); - t.equal(ber.readInt(), 123); - t.equal(ber.readSequence(), 0x67); - t.equal(ber.readEnumeration(), 3); - t.equal(ber.readString(), 'cn=root'); - t.equal(ber.readString(), 'foo'); + const ber = new BerReader(res.toBer()) + t.ok(ber) + t.equal(ber.readSequence(), 0x30) + t.equal(ber.readInt(), 123) + t.equal(ber.readSequence(), 0x67) + t.equal(ber.readEnumeration(), 3) + t.equal(ber.readString(), 'cn=root') + t.equal(ber.readString(), 'foo') - t.end(); -}); + t.end() +}) diff --git a/test/messages/parser.test.js b/test/messages/parser.test.js index 4648828..35eb8cd 100644 --- a/test/messages/parser.test.js +++ b/test/messages/parser.test.js @@ -1,64 +1,49 @@ -// Copyright 2014 Joyent, Inc. All rights reserved. +'use strict' -var test = require('tape').test; -var bunyan = require('bunyan'); - -///--- Globals - -var lib; -var Parser; -var LOG = bunyan.createLogger({name: 'ldapjs-test'}); - -///--- Tests -test('load library', function (t) { - lib = require('../../lib/'); - Parser = lib.Parser; - - t.ok(Parser); - t.end(); -}); +const { test } = require('tap') +const { Parser, LDAPMessage, LDAP_REQ_EXTENSION } = require('../../lib') test('wrong protocol error', function (t) { - var p = new Parser({log: LOG}); + const p = new Parser() p.once('error', function (err) { - t.ok(err); - t.end(); - }); + t.ok(err) + t.end() + }) // Send some bogus data to incur an error - p.write(new Buffer([16, 1, 4])); -}); + p.write(Buffer.from([16, 1, 4])) +}) test('bad protocol op', function (t) { - var p = new Parser({log: LOG}); - var message = new lib.LDAPMessage({ + const p = new Parser() + const message = new LDAPMessage({ protocolOp: 254 // bogus (at least today) - }); + }) p.once('error', function (err) { - t.ok(err); - t.ok(/not supported$/.test(err.message)); - t.end(); - }); - p.write(message.toBer()); -}); + t.ok(err) + t.ok(/not supported$/.test(err.message)) + t.end() + }) + p.write(message.toBer()) +}) test('bad message structure', function (t) { - var p = new Parser({log: LOG}); + const p = new Parser() // message with bogus structure - var message = new lib.LDAPMessage({ - protocolOp: lib.LDAP_REQ_EXTENSION - }); + const message = new LDAPMessage({ + protocolOp: LDAP_REQ_EXTENSION + }) message._toBer = function (writer) { - writer.writeBuffer(new Buffer([16, 1, 4]), 80); - return writer; - }; + writer.writeBuffer(Buffer.from([16, 1, 4]), 80) + return writer + } p.once('error', function (err) { - t.ok(err); - t.end(); - }); + t.ok(err) + t.end() + }) - p.write(message.toBer()); -}); + p.write(message.toBer()) +}) diff --git a/test/messages/search_entry.test.js b/test/messages/search_entry.test.js index f22ebf7..a166ae0 100644 --- a/test/messages/search_entry.test.js +++ b/test/messages/search_entry.test.js @@ -1,116 +1,91 @@ -// Copyright 2011 Mark Cavage, Inc. All rights reserved. - -var test = require('tape').test; - -var asn1 = require('asn1'); - - -///--- Globals - -var BerReader = asn1.BerReader; -var BerWriter = asn1.BerWriter; -var SearchEntry; -var Attribute; -var dn; - -///--- Tests - -test('load library', function (t) { - SearchEntry = require('../../lib/index').SearchEntry; - Attribute = require('../../lib/index').Attribute; - dn = require('../../lib/index').dn; - t.ok(SearchEntry); - t.ok(dn); - t.ok(Attribute); - t.end(); -}); +'use strict' +const { test } = require('tap') +const { BerReader, BerWriter } = require('asn1') +const { SearchEntry, Attribute, dn } = require('../../lib') test('new no args', function (t) { - t.ok(new SearchEntry()); - t.end(); -}); - + t.ok(new SearchEntry()) + t.end() +}) test('new with args', function (t) { - var res = new SearchEntry({ + const res = new SearchEntry({ messageID: 123, objectName: dn.parse('cn=foo, o=test'), - attributes: [new Attribute({type: 'cn', vals: ['foo']}), - new Attribute({type: 'objectclass', vals: ['person']})] - }); - t.ok(res); - t.equal(res.messageID, 123); - t.equal(res.dn.toString(), 'cn=foo, o=test'); - t.equal(res.attributes.length, 2); - t.equal(res.attributes[0].type, 'cn'); - t.equal(res.attributes[0].vals[0], 'foo'); - t.equal(res.attributes[1].type, 'objectclass'); - t.equal(res.attributes[1].vals[0], 'person'); - t.end(); -}); - + attributes: [new Attribute({ type: 'cn', vals: ['foo'] }), + new Attribute({ type: 'objectclass', vals: ['person'] })] + }) + t.ok(res) + t.equal(res.messageID, 123) + t.equal(res.dn.toString(), 'cn=foo, o=test') + t.equal(res.attributes.length, 2) + t.equal(res.attributes[0].type, 'cn') + t.equal(res.attributes[0].vals[0], 'foo') + t.equal(res.attributes[1].type, 'objectclass') + t.equal(res.attributes[1].vals[0], 'person') + t.end() +}) test('parse', function (t) { - var ber = new BerWriter(); - ber.writeString('cn=foo, o=test'); + const ber = new BerWriter() + ber.writeString('cn=foo, o=test') - ber.startSequence(); + ber.startSequence() - ber.startSequence(); - ber.writeString('cn'); - ber.startSequence(0x31); - ber.writeString('foo'); - ber.endSequence(); - ber.endSequence(); + ber.startSequence() + ber.writeString('cn') + ber.startSequence(0x31) + ber.writeString('foo') + ber.endSequence() + ber.endSequence() - ber.startSequence(); - ber.writeString('objectclass'); - ber.startSequence(0x31); - ber.writeString('person'); - ber.endSequence(); - ber.endSequence(); + ber.startSequence() + ber.writeString('objectclass') + ber.startSequence(0x31) + ber.writeString('person') + ber.endSequence() + ber.endSequence() - ber.endSequence(); - - var res = new SearchEntry(); - t.ok(res._parse(new BerReader(ber.buffer))); - t.equal(res.dn, 'cn=foo, o=test'); - t.equal(res.attributes.length, 2); - t.equal(res.attributes[0].type, 'cn'); - t.equal(res.attributes[0].vals[0], 'foo'); - t.equal(res.attributes[1].type, 'objectclass'); - t.equal(res.attributes[1].vals[0], 'person'); - t.end(); -}); + ber.endSequence() + const res = new SearchEntry() + t.ok(res._parse(new BerReader(ber.buffer))) + t.equal(res.dn, 'cn=foo, o=test') + t.equal(res.attributes.length, 2) + t.equal(res.attributes[0].type, 'cn') + t.equal(res.attributes[0].vals[0], 'foo') + t.equal(res.attributes[1].type, 'objectclass') + t.equal(res.attributes[1].vals[0], 'person') + t.end() +}) test('toBer', function (t) { - var res = new SearchEntry({ + const res = new SearchEntry({ messageID: 123, objectName: dn.parse('cn=foo, o=test'), - attributes: [new Attribute({type: 'cn', vals: ['foo']}), - new Attribute({type: 'objectclass', vals: ['person']})] - }); - t.ok(res); + attributes: [new Attribute({ type: 'cn', vals: ['foo'] }), + new Attribute({ type: 'objectclass', vals: ['person'] })] + }) + t.ok(res) - var ber = new BerReader(res.toBer()); - t.ok(ber); - t.equal(ber.readSequence(), 0x30); - t.equal(ber.readInt(), 123); - t.equal(ber.readSequence(), 0x64); - t.equal(ber.readString(), 'cn=foo, o=test'); - t.ok(ber.readSequence()); + const ber = new BerReader(res.toBer()) + t.ok(ber) + t.equal(ber.readSequence(), 0x30) + t.equal(ber.readInt(), 123) + t.equal(ber.readSequence(), 0x64) + t.equal(ber.readString(), 'cn=foo, o=test') + t.ok(ber.readSequence()) - t.ok(ber.readSequence()); - t.equal(ber.readString(), 'cn'); - t.equal(ber.readSequence(), 0x31); - t.equal(ber.readString(), 'foo'); + t.ok(ber.readSequence()) + t.equal(ber.readString(), 'cn') + t.equal(ber.readSequence(), 0x31) + t.equal(ber.readString(), 'foo') - t.ok(ber.readSequence()); - t.equal(ber.readString(), 'objectclass'); - t.equal(ber.readSequence(), 0x31); - t.equal(ber.readString(), 'person'); + t.ok(ber.readSequence()) + t.equal(ber.readString(), 'objectclass') + t.equal(ber.readSequence(), 0x31) + t.equal(ber.readString(), 'person') - t.end(); -}); + t.end() +}) diff --git a/test/messages/search_request.test.js b/test/messages/search_request.test.js index fdf684d..d1e44d4 100644 --- a/test/messages/search_request.test.js +++ b/test/messages/search_request.test.js @@ -1,87 +1,62 @@ -// Copyright 2011 Mark Cavage, Inc. All rights reserved. - -var test = require('tape').test; - -var asn1 = require('asn1'); - - -///--- Globals - -var BerReader = asn1.BerReader; -var BerWriter = asn1.BerWriter; -var SearchRequest; -var EqualityFilter; -var dn; - -///--- Tests - -test('load library', function (t) { - SearchRequest = require('../../lib/index').SearchRequest; - EqualityFilter = require('../../lib/index').EqualityFilter; - dn = require('../../lib/index').dn; - t.ok(SearchRequest); - t.ok(EqualityFilter); - t.ok(dn); - t.end(); -}); +'use strict' +const { test } = require('tap') +const { BerReader, BerWriter } = require('asn1') +const { SearchRequest, EqualityFilter, dn } = require('../../lib') test('new no args', function (t) { - t.ok(new SearchRequest()); - t.end(); -}); - + t.ok(new SearchRequest()) + t.end() +}) test('new with args', function (t) { - var req = new SearchRequest({ + const req = new SearchRequest({ baseObject: dn.parse('cn=foo, o=test'), filter: new EqualityFilter({ attribute: 'email', value: 'foo@bar.com' }), attributes: ['cn', 'sn'] - }); - t.ok(req); - t.equal(req.dn.toString(), 'cn=foo, o=test'); - t.equal(req.filter.toString(), '(email=foo@bar.com)'); - t.equal(req.attributes.length, 2); - t.equal(req.attributes[0], 'cn'); - t.equal(req.attributes[1], 'sn'); - t.end(); -}); - + }) + t.ok(req) + t.equal(req.dn.toString(), 'cn=foo, o=test') + t.equal(req.filter.toString(), '(email=foo@bar.com)') + t.equal(req.attributes.length, 2) + t.equal(req.attributes[0], 'cn') + t.equal(req.attributes[1], 'sn') + t.end() +}) test('parse', function (t) { - var f = new EqualityFilter({ + const f = new EqualityFilter({ attribute: 'email', value: 'foo@bar.com' - }); + }) - var ber = new BerWriter(); - ber.writeString('cn=foo, o=test'); - ber.writeEnumeration(0); - ber.writeEnumeration(0); - ber.writeInt(1); - ber.writeInt(2); - ber.writeBoolean(false); - ber = f.toBer(ber); - - var req = new SearchRequest(); - t.ok(req._parse(new BerReader(ber.buffer))); - t.equal(req.dn.toString(), 'cn=foo, o=test'); - t.equal(req.scope, 'base'); - t.equal(req.derefAliases, 0); - t.equal(req.sizeLimit, 1); - t.equal(req.timeLimit, 2); - t.equal(req.typesOnly, false); - t.equal(req.filter.toString(), '(email=foo@bar.com)'); - t.equal(req.attributes.length, 0); - t.end(); -}); + let ber = new BerWriter() + ber.writeString('cn=foo, o=test') + ber.writeEnumeration(0) + ber.writeEnumeration(0) + ber.writeInt(1) + ber.writeInt(2) + ber.writeBoolean(false) + ber = f.toBer(ber) + const req = new SearchRequest() + t.ok(req._parse(new BerReader(ber.buffer))) + t.equal(req.dn.toString(), 'cn=foo, o=test') + t.equal(req.scope, 'base') + t.equal(req.derefAliases, 0) + t.equal(req.sizeLimit, 1) + t.equal(req.timeLimit, 2) + t.equal(req.typesOnly, false) + t.equal(req.filter.toString(), '(email=foo@bar.com)') + t.equal(req.attributes.length, 0) + t.end() +}) test('toBer', function (t) { - var req = new SearchRequest({ + const req = new SearchRequest({ messageID: 123, baseObject: dn.parse('cn=foo, o=test'), scope: 1, @@ -94,27 +69,28 @@ test('toBer', function (t) { value: 'foo@bar.com' }), attributes: ['cn', 'sn'] - }); + }) - t.ok(req); + t.ok(req) - var ber = new BerReader(req.toBer()); - t.ok(ber); - t.equal(ber.readSequence(), 0x30); - t.equal(ber.readInt(), 123); - t.equal(ber.readSequence(), 0x63); - t.equal(ber.readString(), 'cn=foo, o=test'); - t.equal(ber.readEnumeration(), 1); - t.equal(ber.readEnumeration(), 2); - t.equal(ber.readInt(), 10); - t.equal(ber.readInt(), 20); - t.ok(ber.readBoolean()); - t.equal(ber.readSequence(), 0xa3); - t.equal(ber.readString(), 'email'); - t.equal(ber.readString(), 'foo@bar.com'); - t.ok(ber.readSequence()); - t.equal(ber.readString(), 'cn'); - t.equal(ber.readString(), 'sn'); + const ber = new BerReader(req.toBer()) + t.ok(ber) + t.equal(ber.readSequence(), 0x30) + t.equal(ber.readInt(), 123) + t.equal(ber.readSequence(), 0x63) + // Make sure we've removed spaces from between RDNs: + t.equal(ber.readString(), 'cn=foo,o=test') + t.equal(ber.readEnumeration(), 1) + t.equal(ber.readEnumeration(), 2) + t.equal(ber.readInt(), 10) + t.equal(ber.readInt(), 20) + t.ok(ber.readBoolean()) + t.equal(ber.readSequence(), 0xa3) + t.equal(ber.readString(), 'email') + t.equal(ber.readString(), 'foo@bar.com') + t.ok(ber.readSequence()) + t.equal(ber.readString(), 'cn') + t.equal(ber.readString(), 'sn') - t.end(); -}); + t.end() +}) diff --git a/test/messages/search_response.test.js b/test/messages/search_response.test.js index d5fe111..dbfdb90 100644 --- a/test/messages/search_response.test.js +++ b/test/messages/search_response.test.js @@ -1,76 +1,56 @@ -// Copyright 2011 Mark Cavage, Inc. All rights reserved. - -var test = require('tape').test; - -var asn1 = require('asn1'); - - -///--- Globals - -var BerReader = asn1.BerReader; -var BerWriter = asn1.BerWriter; -var SearchResponse; - - -///--- Tests - -test('load library', function (t) { - SearchResponse = require('../../lib/index').SearchResponse; - t.ok(SearchResponse); - t.end(); -}); +'use strict' +const { test } = require('tap') +const { BerReader, BerWriter } = require('asn1') +const { SearchResponse } = require('../../lib') test('new no args', function (t) { - t.ok(new SearchResponse()); - t.end(); -}); - + t.ok(new SearchResponse()) + t.end() +}) test('new with args', function (t) { - var res = new SearchResponse({ + const res = new SearchResponse({ messageID: 123, status: 0 - }); - t.ok(res); - t.equal(res.messageID, 123); - t.equal(res.status, 0); - t.end(); -}); - + }) + t.ok(res) + t.equal(res.messageID, 123) + t.equal(res.status, 0) + t.end() +}) test('parse', function (t) { - var ber = new BerWriter(); - ber.writeEnumeration(0); - ber.writeString('cn=root'); - ber.writeString('foo'); - - var res = new SearchResponse(); - t.ok(res._parse(new BerReader(ber.buffer))); - t.equal(res.status, 0); - t.equal(res.matchedDN, 'cn=root'); - t.equal(res.errorMessage, 'foo'); - t.end(); -}); + const ber = new BerWriter() + ber.writeEnumeration(0) + ber.writeString('cn=root') + ber.writeString('foo') + const res = new SearchResponse() + t.ok(res._parse(new BerReader(ber.buffer))) + t.equal(res.status, 0) + t.equal(res.matchedDN, 'cn=root') + t.equal(res.errorMessage, 'foo') + t.end() +}) test('toBer', function (t) { - var res = new SearchResponse({ + const res = new SearchResponse({ messageID: 123, status: 3, matchedDN: 'cn=root', errorMessage: 'foo' - }); - t.ok(res); + }) + t.ok(res) - var ber = new BerReader(res.toBer()); - t.ok(ber); - t.equal(ber.readSequence(), 0x30); - t.equal(ber.readInt(), 123); - t.equal(ber.readSequence(), 0x65); - t.equal(ber.readEnumeration(), 3); - t.equal(ber.readString(), 'cn=root'); - t.equal(ber.readString(), 'foo'); + const ber = new BerReader(res.toBer()) + t.ok(ber) + t.equal(ber.readSequence(), 0x30) + t.equal(ber.readInt(), 123) + t.equal(ber.readSequence(), 0x65) + t.equal(ber.readEnumeration(), 3) + t.equal(ber.readString(), 'cn=root') + t.equal(ber.readString(), 'foo') - t.end(); -}); + t.end() +}) diff --git a/test/messages/unbind_request.test.js b/test/messages/unbind_request.test.js index 83603b2..5834a61 100644 --- a/test/messages/unbind_request.test.js +++ b/test/messages/unbind_request.test.js @@ -1,57 +1,37 @@ -// Copyright 2011 Mark Cavage, Inc. All rights reserved. - -var test = require('tape').test; - -var asn1 = require('asn1'); - - -///--- Globals - -var BerReader = asn1.BerReader; -var BerWriter = asn1.BerWriter; -var UnbindRequest; - - -///--- Tests - -test('load library', function (t) { - UnbindRequest = require('../../lib/index').UnbindRequest; - t.ok(UnbindRequest); - t.end(); -}); +'use strict' +const { test } = require('tap') +const { BerReader, BerWriter } = require('asn1') +const { UnbindRequest } = require('../../lib') test('new no args', function (t) { - t.ok(new UnbindRequest()); - t.end(); -}); - + t.ok(new UnbindRequest()) + t.end() +}) test('new with args', function (t) { - var req = new UnbindRequest({}); - t.ok(req); - t.end(); -}); - + const req = new UnbindRequest({}) + t.ok(req) + t.end() +}) test('parse', function (t) { - var ber = new BerWriter(); - - var req = new UnbindRequest(); - t.ok(req._parse(new BerReader(ber.buffer))); - t.end(); -}); + const ber = new BerWriter() + const req = new UnbindRequest() + t.ok(req._parse(new BerReader(ber.buffer))) + t.end() +}) test('toBer', function (t) { - var req = new UnbindRequest({ + const req = new UnbindRequest({ messageID: 123 - }); - t.ok(req); + }) + t.ok(req) - var ber = new BerReader(req.toBer()); - t.ok(ber); - t.equal(ber.readSequence(), 0x30); - t.equal(ber.readInt(), 123); - t.end(); -}); + const ber = new BerReader(req.toBer()) + t.ok(ber) + t.equal(ber.readSequence(), 0x30) + t.equal(ber.readInt(), 123) + t.end() +}) diff --git a/test/server.test.js b/test/server.test.js index 5fec3e3..5ec2528 100644 --- a/test/server.test.js +++ b/test/server.test.js @@ -1,313 +1,334 @@ -// Copyright 2011 Mark Cavage, Inc. All rights reserved. +'use strict' -var Logger = require('bunyan'); +const tap = require('tap') +const vasync = require('vasync') +const { getSock } = require('./utils') +const ldap = require('../lib') -var test = require('tape').test; -var uuid = require('node-uuid'); -var vasync = require('vasync'); +const SERVER_PORT = process.env.SERVER_PORT || 1389 +const SUFFIX = 'dc=test' +tap.beforeEach(function (done, t) { + // We do not need a `.afterEach` to clean up the sock files because that + // is done when the server is destroyed. + t.context.sock = getSock() + done() +}) -///--- Globals +tap.test('basic create', function (t) { + const server = ldap.createServer() + t.ok(server) + t.end() +}) -var BIND_DN = 'cn=root'; -var BIND_PW = 'secret'; +tap.test('connection count', function (t) { + const server = ldap.createServer() + t.ok(server) + server.listen(0, 'localhost', function () { + t.ok(true, 'server listening on ' + server.url) -var SUFFIX = 'dc=test'; + server.getConnections(function (err, count) { + t.error(err) + t.equal(count, 0) -var SERVER_PORT = process.env.SERVER_PORT || 1389; + const client = ldap.createClient({ url: server.url }) + client.on('connect', function () { + t.ok(true, 'client connected') + server.getConnections(function (err, count) { + t.error(err) + t.equal(count, 1) + client.unbind() + server.close(() => t.end()) + }) + }) + }) + }) +}) -var ldap; -var Attribute; -var Change; -var client; -var server; -var sock; - -function getSock() { - if (process.platform === 'win32') { - return '\\\\.\\pipe\\' + uuid(); - } else { - return '/tmp/.' + uuid(); - } -} - -///--- Tests - -test('load library', function (t) { - ldap = require('../lib/index'); - t.ok(ldap.createServer); - t.end(); -}); - -test('basic create', function (t) { - server = ldap.createServer(); - t.ok(server); - t.end(); -}); - -test('properties', function (t) { - t.equal(server.name, 'LDAPServer'); +tap.test('properties', function (t) { + const server = ldap.createServer() + t.equal(server.name, 'LDAPServer') // TODO: better test - server.maxConnections = 10; - t.equal(server.maxConnections, 10); + server.maxConnections = 10 + t.equal(server.maxConnections, 10) - t.equal(server.url, null, 'url empty before bind'); + t.equal(server.url, null, 'url empty before bind') // listen on a random port so we have a url server.listen(0, 'localhost', function () { - t.ok(server.url); + t.ok(server.url) - server.close(); - t.end(); - }); -}); + server.close(() => t.end()) + }) +}) -test('listen on unix/named socket', function (t) { - t.plan(2); - server = ldap.createServer(); - sock = getSock(); - server.listen(sock, function () { - t.ok(server.url); - t.equal(server.url.split(':')[0], 'ldapi'); - server.close(); - t.end(); - }); -}); +tap.test('listen on unix/named socket', function (t) { + const server = ldap.createServer() + server.listen(t.context.sock, function () { + t.ok(server.url) + t.equal(server.url.split(':')[0], 'ldapi') + server.close(() => t.end()) + }) +}) -test('listen on static port', function (t) { - t.plan(2); - server = ldap.createServer(); +tap.test('listen on static port', function (t) { + const server = ldap.createServer() server.listen(SERVER_PORT, '127.0.0.1', function () { - var addr = server.address(); - t.equal(addr.port, parseInt(SERVER_PORT, 10)); - t.equals(server.url, 'ldap://127.0.0.1:' + SERVER_PORT); - server.close(); - t.end(); - }); -}); + const addr = server.address() + t.equal(addr.port, parseInt(SERVER_PORT, 10)) + t.equals(server.url, `ldap://127.0.0.1:${SERVER_PORT}`) + server.close(() => t.end()) + }) +}) -test('listen on ephemeral port', function (t) { - t.plan(2); - server = ldap.createServer(); +tap.test('listen on ephemeral port', function (t) { + const server = ldap.createServer() server.listen(0, 'localhost', function () { - var addr = server.address(); - t.ok(addr.port > 0); - t.ok(addr.port < 65535); - server.close(); - t.end(); - }); -}); + const addr = server.address() + t.ok(addr.port > 0) + t.ok(addr.port < 65535) + server.close(() => t.end()) + }) +}) -test('route order', function (t) { - function generateHandler(response) { - var func = function handler(req, res, next) { +tap.test('route order', function (t) { + function generateHandler (response) { + const func = function handler (req, res, next) { res.send({ dn: response, attributes: { } - }); - res.end(); - return next(); - }; - return func; + }) + res.end() + return next() + } + return func } - server = ldap.createServer(); - sock = getSock(); - var dnShort = SUFFIX; - var dnMed = 'dc=sub, ' + SUFFIX; - var dnLong = 'dc=long, dc=sub, ' + SUFFIX; + const server = ldap.createServer() + const sock = t.context.sock + const dnShort = SUFFIX + const dnMed = 'dc=sub, ' + SUFFIX + const dnLong = 'dc=long, dc=sub, ' + SUFFIX // Mount routes out of order - server.search(dnMed, generateHandler(dnMed)); - server.search(dnShort, generateHandler(dnShort)); - server.search(dnLong, generateHandler(dnLong)); + server.search(dnMed, generateHandler(dnMed)) + server.search(dnShort, generateHandler(dnShort)) + server.search(dnLong, generateHandler(dnLong)) server.listen(sock, function () { - t.ok(true, 'server listen'); - client = ldap.createClient({ socketPath: sock }); - function runSearch(value, cb) { + t.ok(true, 'server listen') + const client = ldap.createClient({ socketPath: sock }) + client.on('connect', () => { + vasync.forEachParallel({ + func: runSearch, + inputs: [dnShort, dnMed, dnLong] + }, function (err, results) { + t.error(err) + client.unbind() + server.close(() => t.end()) + }) + }) + + function runSearch (value, cb) { client.search(value, '(objectclass=*)', function (err, res) { - t.ifError(err); - t.ok(res); + t.error(err) + t.ok(res) res.on('searchEntry', function (entry) { - t.equal(entry.dn.toString(), value); - }); + t.equal(entry.dn.toString(), value) + }) res.on('end', function () { - cb(); - }); - }); + cb() + }) + }) } + }) +}) - vasync.forEachParallel({ - 'func': runSearch, - 'inputs': [dnShort, dnMed, dnLong] - }, function (err, results) { - t.notOk(err); - client.unbind(); - server.close(); - t.end(); - }); - }); -}); - -test('route absent', function (t) { - server = ldap.createServer(); - sock = getSock(); - var DN_ROUTE = 'dc=base'; - var DN_MISSING = 'dc=absent'; +tap.test('route absent', function (t) { + const server = ldap.createServer() + const DN_ROUTE = 'dc=base' + const DN_MISSING = 'dc=absent' server.bind(DN_ROUTE, function (req, res, next) { - res.end(); - return next(); - }); + res.end() + return next() + }) - server.listen(sock, function () { - t.ok(true, 'server startup'); + server.listen(t.context.sock, function () { + t.ok(true, 'server startup') vasync.parallel({ - 'funcs': [ - function presentBind(cb) { - var clt = ldap.createClient({ socketPath: sock }); + funcs: [ + function presentBind (cb) { + const clt = ldap.createClient({ socketPath: t.context.sock }) clt.bind(DN_ROUTE, '', function (err) { - t.notOk(err); - clt.unbind(); - cb(); - }); + t.notOk(err) + clt.unbind() + cb() + }) }, - function absentBind(cb) { - var clt = ldap.createClient({ socketPath: sock }); + function absentBind (cb) { + const clt = ldap.createClient({ socketPath: t.context.sock }) clt.bind(DN_MISSING, '', function (err) { - t.ok(err); - t.equal(err.code, ldap.LDAP_NO_SUCH_OBJECT); - clt.unbind(); - cb(); - }); + t.ok(err) + t.equal(err.code, ldap.LDAP_NO_SUCH_OBJECT) + clt.unbind() + cb() + }) } ] }, function (err, result) { - t.notOk(err); - server.close(); - t.end(); - }); - }); -}); + t.notOk(err) + server.close(() => t.end()) + }) + }) +}) -test('route unbind', function (t) { - t.plan(4); - server = ldap.createServer(); - sock = getSock(); +tap.test('route unbind', function (t) { + const server = ldap.createServer() server.unbind(function (req, res, next) { - t.ok(true, 'server unbind successful'); - res.end(); - return next(); - }); + t.ok(true, 'server unbind successful') + res.end() + return next() + }) - server.listen(sock, function () { - t.ok(true, 'server startup'); - client = ldap.createClient({ socketPath: sock }); + server.listen(t.context.sock, function () { + t.ok(true, 'server startup') + const client = ldap.createClient({ socketPath: t.context.sock }) client.bind('', '', function (err) { - t.ifError(err, 'client bind error'); + t.error(err, 'client bind error') client.unbind(function (err) { - t.ifError(err, 'client unbind error'); - server.close(); - t.end(); - }); - }); - }); -}); + t.error(err, 'client unbind error') + server.close(() => t.end()) + }) + }) + }) +}) -test('strict routing', function (t) { - var testDN = 'cn=valid'; - var clt; +tap.test('strict routing', function (t) { + const testDN = 'cn=valid' + let clt + let server + const sock = t.context.sock vasync.pipeline({ funcs: [ - function setup(_, cb) { + function setup (_, cb) { server = ldap.createServer({ // strictDN: true - on by default - }); - sock = getSock(); + }) // invalid DNs would go to default handler server.search('', function (req, res, next) { - t.ok(req.dn); - t.equal(typeof (req.dn), 'object'); - t.equal(req.dn.toString(), testDN); - res.end(); - next(); - }); + t.ok(req.dn) + t.equal(typeof (req.dn), 'object') + t.equal(req.dn.toString(), testDN) + res.end() + next() + }) server.listen(sock, function () { - t.ok(true, 'server startup'); + t.ok(true, 'server startup') clt = ldap.createClient({ socketPath: sock, strictDN: false - }); - cb(); - }); + }) + cb() + }) }, - function testBad(_, cb) { - clt.search('not a dn', {scope: 'base'}, function (err, res) { - t.ifError(err); + function testBad (_, cb) { + clt.search('not a dn', { scope: 'base' }, function (err, res) { + t.error(err) res.once('error', function (err2) { - t.ok(err2); - t.equal(err2.code, ldap.LDAP_INVALID_DN_SYNTAX); - cb(); - }); + t.ok(err2) + t.equal(err2.code, ldap.LDAP_INVALID_DN_SYNTAX) + cb() + }) res.once('end', function () { - t.fail('accepted invalid dn'); - cb('bogus'); - }); - }); + t.fail('accepted invalid dn') + cb(Error('bogus')) + }) + }) }, - function testGood(_, cb) { - clt.search(testDN, {scope: 'base'}, function (err, res) { - t.ifError(err); + function testGood (_, cb) { + clt.search(testDN, { scope: 'base' }, function (err, res) { + t.error(err) res.once('error', function (err2) { - t.ifError(err2); - cb(err2); - }); + t.error(err2) + cb(err2) + }) res.once('end', function (result) { - t.ok(result, 'accepted invalid dn'); - cb(); - }); - }); + t.ok(result, 'accepted invalid dn') + cb() + }) + }) } ] - }, function (err, res) { + }, function (err) { + t.error(err) if (clt) { - clt.destroy(); + clt.destroy() } - server.close(); - t.end(); - }); -}); + server.close(() => t.end()) + }) +}) -test('non-strict routing', function (t) { - server = ldap.createServer({ +tap.test('non-strict routing', function (t) { + const server = ldap.createServer({ strictDN: false - }); - sock = getSock(); - var testDN = 'this ain\'t a DN'; + }) + const testDN = 'this ain\'t a DN' // invalid DNs go to default handler server.search('', function (req, res, next) { - t.ok(req.dn); - t.equal(typeof (req.dn), 'string'); - t.equal(req.dn, testDN); - res.end(); - next(); - }); + t.ok(req.dn) + t.equal(typeof (req.dn), 'string') + t.equal(req.dn, testDN) + res.end() + next() + }) - server.listen(sock, function () { - t.ok(true, 'server startup'); - var clt = ldap.createClient({ - socketPath: sock, + server.listen(t.context.sock, function () { + t.ok(true, 'server startup') + const clt = ldap.createClient({ + socketPath: t.context.sock, strictDN: false - }); - clt.search(testDN, {scope: 'base'}, function (err, res) { - t.ifError(err); + }) + clt.search(testDN, { scope: 'base' }, function (err, res) { + t.error(err) res.on('end', function () { - clt.destroy(); - server.close(); - t.end(); - }); - }); - }); -}); + clt.destroy() + server.close(() => t.end()) + }) + }) + }) +}) + +tap.test('close accept a callback', function (t) { + const server = ldap.createServer() + // callback is called when the server is closed + server.listen(0, function (err) { + t.error(err) + server.close(function (err) { + t.error(err) + t.end() + }) + }) +}) + +tap.test('close without error calls callback', function (t) { + const server = ldap.createServer() + // when the server is closed without error, the callback parameter is undefined + server.listen(1389, '127.0.0.1', function (err) { + t.error(err) + server.close(function (err) { + t.error(err) + t.end() + }) + }) +}) + +tap.test('close passes error to callback', function (t) { + const server = ldap.createServer() + // when the server is closed with an error, the error is the first parameter of the callback + server.close(function (err) { + t.ok(err) + t.end() + }) +}) diff --git a/test/test.js b/test/test.js deleted file mode 100644 index 5030dd0..0000000 --- a/test/test.js +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2014 Mark Cavage. All rights reserved. - -var assert = require('assert'); -var fs = require('fs'); -var path = require('path'); - - -function runTests(directory) { - fs.readdir(directory, function (err, files) { - assert.ifError(err); - - console.dir(files); - files.filter(function (f) { - return (/\.test\.js$/.test(f)); - }).map(function (f) { - return (path.join(directory, f)); - }).forEach(require); - }); -} - -///--- Run All Tests - -(function main() { - runTests(__dirname); - runTests(path.join(__dirname, 'controls')); - runTests(path.join(__dirname, 'filters')); - runTests(path.join(__dirname, 'messages')); -})(); diff --git a/test/url.test.js b/test/url.test.js index cc6ad19..3b5e91e 100644 --- a/test/url.test.js +++ b/test/url.test.js @@ -1,74 +1,58 @@ -// Copyright 2011 Mark Cavage, Inc. All rights reserved. - -var test = require('tape').test; - - - -///--- Globals - -var url; -var parseURL; - - - -///--- Tests - -test('load library', function (t) { - parseURL = require('../lib/index').parseURL; - t.ok(parseURL); - - t.end(); -}); +'use strict' +const { test } = require('tap') +const { parseURL } = require('../lib') test('parse empty', function (t) { - var u = parseURL('ldap:///'); - t.equal(u.hostname, 'localhost'); - t.equal(u.port, 389); - t.ok(!u.DN); - t.ok(!u.attributes); - t.equal(u.secure, false); - t.end(); -}); - + const u = parseURL('ldap:///') + t.equal(u.hostname, 'localhost') + t.equal(u.port, 389) + t.ok(!u.DN) + t.ok(!u.attributes) + t.equal(u.secure, false) + t.end() +}) test('parse hostname', function (t) { - var u = parseURL('ldap://example.com/'); - t.equal(u.hostname, 'example.com'); - t.equal(u.port, 389); - t.ok(!u.DN); - t.ok(!u.attributes); - t.equal(u.secure, false); - t.end(); -}); - + const u = parseURL('ldap://example.com/') + t.equal(u.hostname, 'example.com') + t.equal(u.port, 389) + t.ok(!u.DN) + t.ok(!u.attributes) + t.equal(u.secure, false) + t.end() +}) test('parse host and port', function (t) { - var u = parseURL('ldap://example.com:1389/'); - t.equal(u.hostname, 'example.com'); - t.equal(u.port, 1389); - t.ok(!u.DN); - t.ok(!u.attributes); - t.equal(u.secure, false); - t.end(); -}); - + const u = parseURL('ldap://example.com:1389/') + t.equal(u.hostname, 'example.com') + t.equal(u.port, 1389) + t.ok(!u.DN) + t.ok(!u.attributes) + t.equal(u.secure, false) + t.end() +}) test('parse full', function (t) { + const u = parseURL('ldaps://ldap.example.com:1389/dc=example%20,dc=com' + + '?cn,sn?sub?(cn=Babs%20Jensen)') - var u = parseURL('ldaps://ldap.example.com:1389/dc=example%20,dc=com' + - '?cn,sn?sub?(cn=Babs%20Jensen)'); + t.equal(u.secure, true) + t.equal(u.hostname, 'ldap.example.com') + t.equal(u.port, 1389) + t.equal(u.DN, 'dc=example ,dc=com') + t.ok(u.attributes) + t.equal(u.attributes.length, 2) + t.equal(u.attributes[0], 'cn') + t.equal(u.attributes[1], 'sn') + t.equal(u.scope, 'sub') + t.equal(u.filter.toString(), '(cn=Babs Jensen)') - t.equal(u.secure, true); - t.equal(u.hostname, 'ldap.example.com'); - t.equal(u.port, 1389); - t.equal(u.DN, 'dc=example ,dc=com'); - t.ok(u.attributes); - t.equal(u.attributes.length, 2); - t.equal(u.attributes[0], 'cn'); - t.equal(u.attributes[1], 'sn'); - t.equal(u.scope, 'sub'); - t.equal(u.filter.toString(), '(cn=Babs Jensen)'); + t.end() +}) - t.end(); -}); +test('supports href', function (t) { + const u = parseURL('ldaps://ldap.example.com:1389/dc=example%20,dc=com?cn,sn?sub?(cn=Babs%20Jensen)') + t.equal(u.href, 'ldaps://ldap.example.com:1389/dc=example%20,dc=com?cn,sn?sub?(cn=Babs%20Jensen)') + t.end() +}) diff --git a/test/utils.js b/test/utils.js new file mode 100644 index 0000000..6d5598b --- /dev/null +++ b/test/utils.js @@ -0,0 +1,17 @@ +'use strict' + +const os = require('os') +const path = require('path') +const uuid = require('uuid') + +function getSock () { + if (process.platform === 'win32') { + return '\\\\.\\pipe\\' + uuid() + } else { + return path.join(os.tmpdir(), uuid()) + } +} + +module.exports = { + getSock +} diff --git a/tools/jsl.node.conf b/tools/jsl.node.conf deleted file mode 100644 index d2cb058..0000000 --- a/tools/jsl.node.conf +++ /dev/null @@ -1,139 +0,0 @@ -# -# Configuration File for JavaScript Lint -# -# This configuration file can be used to lint a collection of scripts, or to enable -# or disable warnings for scripts that are linted via the command line. -# - -### Warnings -# Enable or disable warnings based on requirements. -# Use "+WarningName" to display or "-WarningName" to suppress. -# -+ambiguous_else_stmt # the else statement could be matched with one of multiple if statements (use curly braces to indicate intent -+ambiguous_nested_stmt # block statements containing block statements should use curly braces to resolve ambiguity -+ambiguous_newline # unexpected end of line; it is ambiguous whether these lines are part of the same statement -+anon_no_return_value # anonymous function does not always return value -+assign_to_function_call # assignment to a function call --block_without_braces # block statement without curly braces -+comma_separated_stmts # multiple statements separated by commas (use semicolons?) -+comparison_type_conv # comparisons against null, 0, true, false, or an empty string allowing implicit type conversion (use === or !==) -+default_not_at_end # the default case is not at the end of the switch statement -+dup_option_explicit # duplicate "option explicit" control comment -+duplicate_case_in_switch # duplicate case in switch statement -+duplicate_formal # duplicate formal argument {name} -+empty_statement # empty statement or extra semicolon -+identifier_hides_another # identifer {name} hides an identifier in a parent scope --inc_dec_within_stmt # increment (++) and decrement (--) operators used as part of greater statement -+incorrect_version # Expected /*jsl:content-type*/ control comment. The script was parsed with the wrong version. -+invalid_fallthru # unexpected "fallthru" control comment -+invalid_pass # unexpected "pass" control comment -+jsl_cc_not_understood # couldn't understand control comment using /*jsl:keyword*/ syntax -+leading_decimal_point # leading decimal point may indicate a number or an object member -+legacy_cc_not_understood # couldn't understand control comment using /*@keyword@*/ syntax -+meaningless_block # meaningless block; curly braces have no impact -+mismatch_ctrl_comments # mismatched control comment; "ignore" and "end" control comments must have a one-to-one correspondence -+misplaced_regex # regular expressions should be preceded by a left parenthesis, assignment, colon, or comma -+missing_break # missing break statement -+missing_break_for_last_case # missing break statement for last case in switch -+missing_default_case # missing default case in switch statement -+missing_option_explicit # the "option explicit" control comment is missing -+missing_semicolon # missing semicolon -+missing_semicolon_for_lambda # missing semicolon for lambda assignment -+multiple_plus_minus # unknown order of operations for successive plus (e.g. x+++y) or minus (e.g. x---y) signs -+nested_comment # nested comment -+no_return_value # function {name} does not always return a value -+octal_number # leading zeros make an octal number -+parseint_missing_radix # parseInt missing radix parameter -+partial_option_explicit # the "option explicit" control comment, if used, must be in the first script tag -+redeclared_var # redeclaration of {name} -+trailing_comma_in_array # extra comma is not recommended in array initializers -+trailing_decimal_point # trailing decimal point may indicate a number or an object member -+undeclared_identifier # undeclared identifier: {name} -+unreachable_code # unreachable code --unreferenced_argument # argument declared but never referenced: {name} --unreferenced_function # function is declared but never referenced: {name} -+unreferenced_variable # variable is declared but never referenced: {name} -+unsupported_version # JavaScript {version} is not supported -+use_of_label # use of label -+useless_assign # useless assignment -+useless_comparison # useless comparison; comparing identical expressions --useless_quotes # the quotation marks are unnecessary -+useless_void # use of the void type may be unnecessary (void is always undefined) -+var_hides_arg # variable {name} hides argument -+want_assign_or_call # expected an assignment or function call -+with_statement # with statement hides undeclared variables; use temporary variable instead --identifier_hides_another - -### Output format -# Customize the format of the error message. -# __FILE__ indicates current file path -# __FILENAME__ indicates current file name -# __LINE__ indicates current line -# __COL__ indicates current column -# __ERROR__ indicates error message (__ERROR_PREFIX__: __ERROR_MSG__) -# __ERROR_NAME__ indicates error name (used in configuration file) -# __ERROR_PREFIX__ indicates error prefix -# __ERROR_MSG__ indicates error message -# -# For machine-friendly output, the output format can be prefixed with -# "encode:". If specified, all items will be encoded with C-slashes. -# -# Visual Studio syntax (default): -+output-format __FILE__(__LINE__): __ERROR__ -# Alternative syntax: -#+output-format __FILE__:__LINE__: __ERROR__ - - -### Context -# Show the in-line position of the error. -# Use "+context" to display or "-context" to suppress. -# -+context - - -### Control Comments -# Both JavaScript Lint and the JScript interpreter confuse each other with the syntax for -# the /*@keyword@*/ control comments and JScript conditional comments. (The latter is -# enabled in JScript with @cc_on@). The /*jsl:keyword*/ syntax is preferred for this reason, -# although legacy control comments are enabled by default for backward compatibility. -# --legacy_control_comments - - -### Defining identifiers -# By default, "option explicit" is enabled on a per-file basis. -# To enable this for all files, use "+always_use_option_explicit" --always_use_option_explicit - -# Define certain identifiers of which the lint is not aware. -# (Use this in conjunction with the "undeclared identifier" warning.) -# -# Common uses for webpages might be: -+define __dirname -+define clearInterval -+define clearTimeout -+define console -+define exports -+define global -+define module -+define process -+define require -+define setInterval -+define setImmediate -+define setTimeout -+define Buffer -+define JSON -+define Math - -### JavaScript Version -# To change the default JavaScript version: -#+default-type text/javascript;version=1.5 -#+default-type text/javascript;e4x=1 - -### Files -# Specify which files to lint -# Use "+recurse" to enable recursion (disabled by default). -# To add a set of files, use "+process FileName", "+process Folder\Path\*.js", -# or "+process Folder\Path\*.htm". -# - diff --git a/tools/jsstyle.conf b/tools/jsstyle.conf deleted file mode 100644 index 2f11f48..0000000 --- a/tools/jsstyle.conf +++ /dev/null @@ -1,4 +0,0 @@ -indent=2 -doxygen -unparenthesized-return=0 -blank-after-start-comment=0 \ No newline at end of file diff --git a/tools/mk/Makefile.defs b/tools/mk/Makefile.defs deleted file mode 100644 index 50a13c5..0000000 --- a/tools/mk/Makefile.defs +++ /dev/null @@ -1,43 +0,0 @@ -# -*- mode: makefile -*- -# -# Copyright (c) 2012, Joyent, Inc. All rights reserved. -# -# Makefile.defs: common defines. -# -# NOTE: This makefile comes from the "eng" repo. It's designed to be dropped -# into other repos as-is without requiring any modifications. If you find -# yourself changing this file, you should instead update the original copy in -# eng.git and then update your repo to use the new version. -# -# This makefile defines some useful defines. Include it at the top of -# your Makefile. -# -# Definitions in this Makefile: -# -# TOP The absolute path to the project directory. The top dir. -# BRANCH The current git branch. -# TIMESTAMP The timestamp for the build. This can be set via -# the TIMESTAMP envvar (used by MG-based builds). -# STAMP A build stamp to use in built package names. -# - -TOP := $(shell pwd) - -# -# Mountain Gorilla-spec'd versioning. -# See "Package Versioning" in MG's README.md: -# -# -# Need GNU awk for multi-char arg to "-F". -_AWK := $(shell (which gawk >/dev/null && echo gawk) \ - || (which nawk >/dev/null && echo nawk) \ - || echo awk) -BRANCH := $(shell git symbolic-ref HEAD | $(_AWK) -F/ '{print $$3}') -ifeq ($(TIMESTAMP),) - TIMESTAMP := $(shell date -u "+%Y%m%dT%H%M%SZ") -endif -_GITDESCRIBE := g$(shell git describe --all --long --dirty | $(_AWK) -F'-g' '{print $$NF}') -STAMP := $(BRANCH)-$(TIMESTAMP)-$(_GITDESCRIBE) - -# node-gyp will print build info useful for debugging with V=1 -export V=1 diff --git a/tools/mk/Makefile.deps b/tools/mk/Makefile.deps deleted file mode 100644 index eeae27f..0000000 --- a/tools/mk/Makefile.deps +++ /dev/null @@ -1,44 +0,0 @@ -# -*- mode: makefile -*- -# -# Copyright (c) 2012, Joyent, Inc. All rights reserved. -# -# Makefile.deps: Makefile for including common tools as dependencies -# -# NOTE: This makefile comes from the "eng" repo. It's designed to be dropped -# into other repos as-is without requiring any modifications. If you find -# yourself changing this file, you should instead update the original copy in -# eng.git and then update your repo to use the new version. -# -# This file is separate from Makefile.targ so that teams can choose -# independently whether to use the common targets in Makefile.targ and the -# common tools here. -# - -# -# javascriptlint -# -JSL_EXEC ?= deps/javascriptlint/build/install/jsl -JSL ?= $(JSL_EXEC) - -$(JSL_EXEC): | deps/javascriptlint/.git - cd deps/javascriptlint && make install - -distclean:: - if [[ -f deps/javascriptlint/Makefile ]]; then \ - cd deps/javascriptlint && make clean; \ - fi - -# -# jsstyle -# -JSSTYLE_EXEC ?= deps/jsstyle/jsstyle -JSSTYLE ?= $(JSSTYLE_EXEC) - -$(JSSTYLE_EXEC): | deps/jsstyle/.git - -# -# restdown -# -RESTDOWN_EXEC ?= deps/restdown/bin/restdown -RESTDOWN ?= python $(RESTDOWN_EXEC) -$(RESTDOWN_EXEC): | deps/restdown/.git diff --git a/tools/mk/Makefile.targ b/tools/mk/Makefile.targ deleted file mode 100644 index 48d07b4..0000000 --- a/tools/mk/Makefile.targ +++ /dev/null @@ -1,291 +0,0 @@ -# -*- mode: makefile -*- -# -# Copyright (c) 2012, Joyent, Inc. All rights reserved. -# -# Makefile.targ: common targets. -# -# NOTE: This makefile comes from the "eng" repo. It's designed to be dropped -# into other repos as-is without requiring any modifications. If you find -# yourself changing this file, you should instead update the original copy in -# eng.git and then update your repo to use the new version. -# -# This Makefile defines several useful targets and rules. You can use it by -# including it from a Makefile that specifies some of the variables below. -# -# Targets defined in this Makefile: -# -# check Checks JavaScript files for lint and style -# Checks bash scripts for syntax -# Checks SMF manifests for validity against the SMF DTD -# -# clean Removes built files -# -# docs Builds restdown documentation in docs/ -# -# prepush Depends on "check" and "test" -# -# test Does nothing (you should override this) -# -# xref Generates cscope (source cross-reference index) -# -# For details on what these targets are supposed to do, see the Joyent -# Engineering Guide. -# -# To make use of these targets, you'll need to set some of these variables. Any -# variables left unset will simply not be used. -# -# BASH_FILES Bash scripts to check for syntax -# (paths relative to top-level Makefile) -# -# CLEAN_FILES Files to remove as part of the "clean" target. Note -# that files generated by targets in this Makefile are -# automatically included in CLEAN_FILES. These include -# restdown-generated HTML and JSON files. -# -# DOC_FILES Restdown (documentation source) files. These are -# assumed to be contained in "docs/", and must NOT -# contain the "docs/" prefix. -# -# JSL_CONF_NODE Specify JavaScriptLint configuration files -# JSL_CONF_WEB (paths relative to top-level Makefile) -# -# Node.js and Web configuration files are separate -# because you'll usually want different global variable -# configurations. If no file is specified, none is given -# to jsl, which causes it to use a default configuration, -# which probably isn't what you want. -# -# JSL_FILES_NODE JavaScript files to check with Node config file. -# JSL_FILES_WEB JavaScript files to check with Web config file. -# -# You can also override these variables: -# -# BASH Path to bash (default: bash) -# -# CSCOPE_DIRS Directories to search for source files for the cscope -# index. (default: ".") -# -# JSL Path to JavaScriptLint (default: "jsl") -# -# JSL_FLAGS_NODE Additional flags to pass through to JSL -# JSL_FLAGS_WEB -# JSL_FLAGS -# -# JSSTYLE Path to jsstyle (default: jsstyle) -# -# JSSTYLE_FLAGS Additional flags to pass through to jsstyle -# - -# -# Defaults for the various tools we use. -# -BASH ?= bash -BASHSTYLE ?= tools/bashstyle -CP ?= cp -CSCOPE ?= cscope -CSCOPE_DIRS ?= . -JSL ?= jsl -JSSTYLE ?= jsstyle -MKDIR ?= mkdir -p -MV ?= mv -RESTDOWN_FLAGS ?= -RMTREE ?= rm -rf -JSL_FLAGS ?= --nologo --nosummary - -ifeq ($(shell uname -s),SunOS) - TAR ?= gtar -else - TAR ?= tar -endif - - -# -# Defaults for other fixed values. -# -BUILD = build -DISTCLEAN_FILES += $(BUILD) -DOC_BUILD = $(BUILD)/docs/public - -# -# Configure JSL_FLAGS_{NODE,WEB} based on JSL_CONF_{NODE,WEB}. -# -ifneq ($(origin JSL_CONF_NODE), undefined) - JSL_FLAGS_NODE += --conf=$(JSL_CONF_NODE) -endif - -ifneq ($(origin JSL_CONF_WEB), undefined) - JSL_FLAGS_WEB += --conf=$(JSL_CONF_WEB) -endif - -# -# Targets. For descriptions on what these are supposed to do, see the -# Joyent Engineering Guide. -# - -# -# Instruct make to keep around temporary files. We have rules below that -# automatically update git submodules as needed, but they employ a deps/*/.git -# temporary file. Without this directive, make tries to remove these .git -# directories after the build has completed. -# -.SECONDARY: $($(wildcard deps/*):%=%/.git) - -# -# This rule enables other rules that use files from a git submodule to have -# those files depend on deps/module/.git and have "make" automatically check -# out the submodule as needed. -# -deps/%/.git: - git submodule update --init deps/$* - -# -# These recipes make heavy use of dynamically-created phony targets. The parent -# Makefile defines a list of input files like BASH_FILES. We then say that each -# of these files depends on a fake target called filename.bashchk, and then we -# define a pattern rule for those targets that runs bash in check-syntax-only -# mode. This mechanism has the nice properties that if you specify zero files, -# the rule becomes a noop (unlike a single rule to check all bash files, which -# would invoke bash with zero files), and you can check individual files from -# the command line with "make filename.bashchk". -# -.PHONY: check-bash -check-bash: $(BASH_FILES:%=%.bashchk) $(BASH_FILES:%=%.bashstyle) - -%.bashchk: % - $(BASH) -n $^ - -%.bashstyle: % - $(BASHSTYLE) $^ - -# -# The above approach can be slow when there are many files to check because it -# requires that "make" invoke the check tool once for each file, rather than -# passing in several files at once. For the JavaScript check targets, we define -# a variable for the target itself *only if* the list of input files is -# non-empty. This avoids invoking the tool if there are no files to check. -# -JSL_NODE_TARGET = $(if $(JSL_FILES_NODE), check-jsl-node) -.PHONY: check-jsl-node -check-jsl-node: $(JSL_EXEC) - $(JSL) $(JSL_FLAGS) $(JSL_FLAGS_NODE) $(JSL_FILES_NODE) - -JSL_WEB_TARGET = $(if $(JSL_FILES_WEB), check-jsl-web) -.PHONY: check-jsl-web -check-jsl-web: $(JSL_EXEC) - $(JSL) $(JSL_FLAGS) $(JSL_FLAGS_WEB) $(JSL_FILES_WEB) - -.PHONY: check-jsl -check-jsl: $(JSL_NODE_TARGET) $(JSL_WEB_TARGET) - -JSSTYLE_TARGET = $(if $(JSSTYLE_FILES), check-jsstyle) -.PHONY: check-jsstyle -check-jsstyle: $(JSSTYLE_EXEC) - $(JSSTYLE) $(JSSTYLE_FLAGS) $(JSSTYLE_FILES) - -.PHONY: check -check: check-jsl $(JSSTYLE_TARGET) check-bash - @echo check ok - -.PHONY: clean -clean:: - -$(RMTREE) $(CLEAN_FILES) - -.PHONY: distclean -distclean:: clean - -$(RMTREE) $(DISTCLEAN_FILES) - -CSCOPE_FILES = cscope.in.out cscope.out cscope.po.out -CLEAN_FILES += $(CSCOPE_FILES) - -.PHONY: xref -xref: cscope.files - $(CSCOPE) -bqR - -.PHONY: cscope.files -cscope.files: - find $(CSCOPE_DIRS) -name '*.c' -o -name '*.h' -o -name '*.cc' \ - -o -name '*.js' -o -name '*.s' -o -name '*.cpp' > $@ - -# -# The "docs" target is complicated because we do several things here: -# -# (1) Use restdown to build HTML and JSON files from each of DOC_FILES. -# -# (2) Copy these files into $(DOC_BUILD) (build/docs/public), which -# functions as a complete copy of the documentation that could be -# mirrored or served over HTTP. -# -# (3) Then copy any directories and media from docs/media into -# $(DOC_BUILD)/media. This allows projects to include their own media, -# including files that will override same-named files provided by -# restdown. -# -# Step (3) is the surprisingly complex part: in order to do this, we need to -# identify the subdirectories in docs/media, recreate them in -# $(DOC_BUILD)/media, then do the same with the files. -# -DOC_MEDIA_DIRS := $(shell find docs/media -type d 2>/dev/null | grep -v "^docs/media$$") -DOC_MEDIA_DIRS := $(DOC_MEDIA_DIRS:docs/media/%=%) -DOC_MEDIA_DIRS_BUILD := $(DOC_MEDIA_DIRS:%=$(DOC_BUILD)/media/%) - -DOC_MEDIA_FILES := $(shell find docs/media -type f 2>/dev/null) -DOC_MEDIA_FILES := $(DOC_MEDIA_FILES:docs/media/%=%) -DOC_MEDIA_FILES_BUILD := $(DOC_MEDIA_FILES:%=$(DOC_BUILD)/media/%) - -# -# Like the other targets, "docs" just depends on the final files we want to -# create in $(DOC_BUILD), leveraging other targets and recipes to define how -# to get there. -# -.PHONY: docs -docs: \ - $(DOC_FILES:%.md=$(DOC_BUILD)/%.html) \ - $(DOC_FILES:%.md=$(DOC_BUILD)/%.json) \ - $(DOC_MEDIA_FILES_BUILD) - -# -# We keep the intermediate files so that the next build can see whether the -# files in DOC_BUILD are up to date. -# -.PRECIOUS: \ - $(DOC_FILES:%.md=docs/%.html) \ - $(DOC_FILES:%.md=docs/%json) - -# -# We do clean those intermediate files, as well as all of DOC_BUILD. -# -CLEAN_FILES += \ - $(DOC_BUILD) \ - $(DOC_FILES:%.md=docs/%.html) \ - $(DOC_FILES:%.md=docs/%.json) - -# -# Before installing the files, we must make sure the directories exist. The | -# syntax tells make that the dependency need only exist, not be up to date. -# Otherwise, it might try to rebuild spuriously because the directory itself -# appears out of date. -# -$(DOC_MEDIA_FILES_BUILD): | $(DOC_MEDIA_DIRS_BUILD) - -$(DOC_BUILD)/%: docs/% | $(DOC_BUILD) - $(CP) $< $@ - -docs/%.json docs/%.html: docs/%.md | $(DOC_BUILD) $(RESTDOWN_EXEC) - $(RESTDOWN) $(RESTDOWN_FLAGS) -m $(DOC_BUILD) $< - -$(DOC_BUILD): - $(MKDIR) $@ - -$(DOC_MEDIA_DIRS_BUILD): - $(MKDIR) $@ - -# -# The default "test" target does nothing. This should usually be overridden by -# the parent Makefile. It's included here so we can define "prepush" without -# requiring the repo to define "test". -# -.PHONY: test -test: - -.PHONY: prepush -prepush: check test