From 5033613b79ac12423e7bfdfe2dfbd2e422add5c2 Mon Sep 17 00:00:00 2001 From: Matt Simerson Date: Fri, 23 Aug 2024 09:34:26 -0700 Subject: [PATCH] populate [files] in package.json. Delete .npmignore. - dep: eslint-plugin-haraka -> @haraka/eslint-config - lint: remove duplicate / stale rules from .eslintrc - chore: prettier automated code formatting - doc: add CONTRIBUTORS.md --- .eslintrc.yaml | 24 +-- .github/workflows/ci.yml | 39 ++-- .github/workflows/codeql.yml | 4 +- .github/workflows/publish.yml | 8 +- .gitignore | 1 - .npmignore | 61 ------ .prettierrc | 1 + .release | 2 +- CHANGELOG.md | 8 +- CONTRIBUTORS.md | 8 + README.md | 132 ++++++------- aliases.js | 153 ++++++++------- authn.js | 96 +++++----- authz.js | 74 +++++--- index.js | 69 ++++--- package.json | 21 +- pool.js | 35 ++-- rcpt_to.js | 52 ++--- test/aliases.js | 332 +++++++++++++++++++------------- test/authn.js | 345 ++++++++++++++++++++------------- test/authz.js | 236 ++++++++++++++--------- test/index.js | 348 ++++++++++++++++++++-------------- test/pool.js | 135 ++++++------- test/rcpt_to.js | 216 ++++++++++++--------- 24 files changed, 1347 insertions(+), 1053 deletions(-) delete mode 100644 .npmignore create mode 100644 .prettierrc create mode 100644 CONTRIBUTORS.md diff --git a/.eslintrc.yaml b/.eslintrc.yaml index 72dcb5e..4f249c3 100644 --- a/.eslintrc.yaml +++ b/.eslintrc.yaml @@ -2,28 +2,10 @@ env: node: true es6: true mocha: true - es2020: true + es2022: true -plugins: - - haraka - -extends: - - eslint:recommended - - plugin:haraka/recommended - -root: true +extends: '@haraka' rules: - no-var: warn - space-before-function-paren: warn - no-shadow: warn - dot-notation: warn indent: warn - -globals: - OK: true - CONT: true - DENY: true - DENYSOFT: true - DENYDISCONNECT: true - DENYSOFTDISCONNECT: true + no-unused-vars: 0 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fe8b982..21925e0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,32 +1,31 @@ name: CI -on: [ push, pull_request ] +on: [push, pull_request] env: CI: true jobs: - lint: uses: haraka/.github/.github/workflows/lint.yml@master test: - needs: [ lint, get-lts ] + needs: [lint, get-lts] runs-on: ${{ matrix.os }} strategy: matrix: - os: [ ubuntu-latest ] + os: [ubuntu-latest] node-version: ${{ fromJson(needs.get-lts.outputs.lts) }} fail-fast: false steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - name: Node ${{ matrix.node-version }} on ${{ matrix.os }} - with: - node-version: ${{ matrix.node-version }} - - run: npm install - - run: sudo ./test/fixtures/linux/setup.sh - - run: npm test + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + name: Node ${{ matrix.node-version }} on ${{ matrix.os }} + with: + node-version: ${{ matrix.node-version }} + - run: npm install + - run: sudo ./test/fixtures/linux/setup.sh + - run: npm test # test-win: # needs: [ lint, get-lts ] @@ -46,11 +45,11 @@ jobs: # - run: npm test get-lts: - runs-on: ubuntu-latest - steps: - - id: get - uses: msimerson/node-lts-versions@v1 - outputs: - active: ${{ steps.get.outputs.active }} - lts: ${{ steps.get.outputs.lts }} - min: ${{ steps.get.outputs.min }} + runs-on: ubuntu-latest + steps: + - id: get + uses: msimerson/node-lts-versions@v1 + outputs: + active: ${{ steps.get.outputs.active }} + lts: ${{ steps.get.outputs.lts }} + min: ${{ steps.get.outputs.min }} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 3627451..8314a66 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -2,10 +2,10 @@ name: CodeQL on: push: - branches: [ master ] + branches: [master] pull_request: # The branches below must be a subset of the branches above - branches: [ master ] + branches: [master] schedule: - cron: '18 7 * * 4' diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 3fe2686..389fa6d 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -26,7 +26,7 @@ jobs: - run: npm test publish-npm: - needs: [ build ] + needs: [build] runs-on: ubuntu-latest environment: npm steps: @@ -40,7 +40,7 @@ jobs: NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }} publish-gpr: - needs: [ build ] + needs: [build] runs-on: ubuntu-latest environment: ghpm permissions: @@ -53,9 +53,9 @@ jobs: - uses: actions/setup-node@v4 with: registry-url: https://npm.pkg.github.com/ - scope: "@haraka" + scope: '@haraka' - name: rename package with @haraka scope run: node .release/npm/prepend-scope.cjs @haraka - run: npm publish env: - NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 625981f..5d2d1cd 100644 --- a/.gitignore +++ b/.gitignore @@ -42,4 +42,3 @@ bower_components .npmrc .idea .DS_Store -haraka-update.sh \ No newline at end of file diff --git a/.npmignore b/.npmignore deleted file mode 100644 index a5689a8..0000000 --- a/.npmignore +++ /dev/null @@ -1,61 +0,0 @@ -# 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 - -# nyc test coverage -.nyc_output - -# 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 directories -node_modules -jspm_packages - -# Optional npm cache directory -.npm - -# Optional REPL history -.node_repl_history - -package-lock.json -bower_components -# Optional npm cache directory -.npmrc -.idea -.DS_Store -haraka-update.sh - -.github -.release -.codeclimate.yml -.editorconfig -.gitignore -.gitmodules -.lgtm.yml -appveyor.yml -codecov.yml -.travis.yml -.eslintrc.yaml -.eslintrc.json - -run_tests -tests diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..0176969 --- /dev/null +++ b/.prettierrc @@ -0,0 +1 @@ +singleQuote: true diff --git a/.release b/.release index 0890e94..afb1db8 160000 --- a/.release +++ b/.release @@ -1 +1 @@ -Subproject commit 0890e945e4e061c96c7b2ab45017525904c17728 +Subproject commit afb1db801607dda5e859f39b600f0dd0111e4651 diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d54a42..4830677 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,13 @@ ### Unreleased - ### [1.1.3] - 2024-08-23 - fix: always unbind after bind, fixes #12 +- dep: eslint-plugin-haraka -> @haraka/eslint-config +- chore: populate [files] in package.json. Delete .npmignore. +- lint: remove duplicate / stale rules from .eslintrc +- chore: prettier automated code formatting +- doc: add CONTRIBUTORS.md ### [1.1.2] - 2024-03-15 @@ -40,7 +44,7 @@ #### 1.0.2 - 2016-12-10 - test get_alias for resolve-by-dn case -- added debug log to _resolve_dn_to_alias +- added debug log to \_resolve_dn_to_alias - fixed wrong default attribute there - include all ops in config diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md new file mode 100644 index 0000000..8b24187 --- /dev/null +++ b/CONTRIBUTORS.md @@ -0,0 +1,8 @@ +# Contributors + +This handcrafted artisinal software is brought to you by: + +|
msimerson (30)| +| :---: | + +this file is maintained by [.release](https://github.com/msimerson/.release) diff --git a/README.md b/README.md index f70a9b1..55ad6ad 100644 --- a/README.md +++ b/README.md @@ -8,40 +8,38 @@ This is an universal LDAP plugin for Haraka. It supports aliases, authentication Join #haraka on freenode IRC if you need help. - ## Configuration All configuration is done in `config/ldap.ini`. The following options are configurable: - ### Main section -* `server`: *required*, default: none. -Specify LDAP server addresses. -This is an array of *url* from [ldapjs](http://ldapjs.org/client.html). -Apply multiple server[] values for some simple load-balancing. -* `timeout`: *optional*, default: No timeout. -Define time out for LDAP ops. -This is the same as *timeout* from [ldapjs](http://ldapjs.org/client.html). -* `tls_enabled`: *optional*, default: `tls_enabled=false` -Enable or disable TLS. If enabled, all LDAP connections will be secured first -by calling starttls. -* `tls_rejectUnauthorized`: *optional*, default: `tls_rejectUnauthorized`false` -Enable or disable rejection of secured connections without valid server certificate. -This is as *rejectUnauthorized* from the [node.js server API as used by ldapjs](https://nodejs.org/api/tls.html#tls_tls_createserver_options_secureconnectionlistener). -* `scope`: *optional*, default: `cope=sub` -This defines the scope of the LDAP search operation, like *base* or *sub*. -This is the same as *scope* from [ldapjs](http://ldapjs.org/client.html). -* `binddn`: *optional*, default: not set -The binddn is basically the LDAP user to be used to look up data in LDAP. It -is optional (the LDAP server might allow anonymous binds). -* `bindpw`: *optional*, default: not set -A bindpw might be necessary to bind with the given binddn. It can be supplied -here. -* `basedn`: *required*, default: not set -The basedn is a requirement for many LDAP operations. It must be defined with -this option. +- `server`: _required_, default: none. + Specify LDAP server addresses. + This is an array of _url_ from [ldapjs](http://ldapjs.org/client.html). + Apply multiple server[] values for some simple load-balancing. +- `timeout`: _optional_, default: No timeout. + Define time out for LDAP ops. + This is the same as _timeout_ from [ldapjs](http://ldapjs.org/client.html). +- `tls_enabled`: _optional_, default: `tls_enabled=false` + Enable or disable TLS. If enabled, all LDAP connections will be secured first + by calling starttls. +- `tls_rejectUnauthorized`: _optional_, default: `tls_rejectUnauthorized`false` + Enable or disable rejection of secured connections without valid server certificate. + This is as _rejectUnauthorized_ from the [node.js server API as used by ldapjs](https://nodejs.org/api/tls.html#tls_tls_createserver_options_secureconnectionlistener). +- `scope`: _optional_, default: `cope=sub` + This defines the scope of the LDAP search operation, like _base_ or _sub_. + This is the same as _scope_ from [ldapjs](http://ldapjs.org/client.html). +- `binddn`: _optional_, default: not set + The binddn is basically the LDAP user to be used to look up data in LDAP. It + is optional (the LDAP server might allow anonymous binds). +- `bindpw`: _optional_, default: not set + A bindpw might be necessary to bind with the given binddn. It can be supplied + here. +- `basedn`: _required_, default: not set + The basedn is a requirement for many LDAP operations. It must be defined with + this option. Every task is enabled by adding a task-specific section in the configuration file. They are described below. @@ -49,67 +47,62 @@ Every task is enabled by adding a task-specific section in the configuration fil By enabling \[aliases\] is it possible to query LDAP to resolve email aliases and to forward email to one or multiple configured targets. -* `basedn`: *optional*, default: as set in main section -It's possible to override the default basedn -* `scope`: *optional*, default: as set in main section -It's possible to override the default scope -* `searchfilter`: *optional*, default: (&(objectclass=*)(mail=%a)(mailForwardAddress=*)) -Search filter to lookup aliases. The param %a denotes the recipient's mail address as given on the email's envelope. As result the search filter should return the objects containing the dealiased recipient addresses within a given `attribute` (see below). -* `attribute`: *optional*, default: `mailForwardingAddress` -Attribute used to parse as recipient's email address or as recipient's DN (see below). -* `attribute_is_dn`: *optional*, default: false -Set this to `true` if the attribute does not contain an email address but a fully qualified DN. -* `subattribute`: *optional*, default: `mailLocalAddress` -If the attribute references a DN then the subattribute references the DN's email address that should be used as recipient. - +- `basedn`: _optional_, default: as set in main section + It's possible to override the default basedn +- `scope`: _optional_, default: as set in main section + It's possible to override the default scope +- `searchfilter`: _optional_, default: (&(objectclass=_)(mail=%a)(mailForwardAddress=_)) + Search filter to lookup aliases. The param %a denotes the recipient's mail address as given on the email's envelope. As result the search filter should return the objects containing the dealiased recipient addresses within a given `attribute` (see below). +- `attribute`: _optional_, default: `mailForwardingAddress` + Attribute used to parse as recipient's email address or as recipient's DN (see below). +- `attribute_is_dn`: _optional_, default: false + Set this to `true` if the attribute does not contain an email address but a fully qualified DN. +- `subattribute`: _optional_, default: `mailLocalAddress` + If the attribute references a DN then the subattribute references the DN's email address that should be used as recipient. ### \[authn\] With the \[authn\] is authentication (authn) against LDAP servers enabled, i.e. this checks if a given user credentials are valid in LDAP. It can either search for the user DN first, or it can try to bind by predefined DN templates -* `basedn`: *optional*, default: as set in main section -It's possible to override the default basedn -* `scope`: *optional*, default: as set in main section -It's possible to override the default scope -* `searchfilter`: *optional*, default: (&(objectclass=*)(uid=%u)) -Search filter to lookup the user's DN. The param `%u` denotes the uid/username as given during login. As result the search filter should return the object(s) to be used for a simple bind attempt. Authentication will fail if the search filter doesn't return exactly one matching object. -* `dn`: *optional*, default: undefined -`dn` is an array of template DN to check for the given uid. This is an alternate mode of lookup, where the plugin inserts the uid in the DN template and immediately tries to bind instead of doing a search for the DN first. A template DN looks like `uid=%u,ou=users,dc=example,dc=com`. The param `%u` denotes the uid/username as given during login. - +- `basedn`: _optional_, default: as set in main section + It's possible to override the default basedn +- `scope`: _optional_, default: as set in main section + It's possible to override the default scope +- `searchfilter`: _optional_, default: (&(objectclass=\*)(uid=%u)) + Search filter to lookup the user's DN. The param `%u` denotes the uid/username as given during login. As result the search filter should return the object(s) to be used for a simple bind attempt. Authentication will fail if the search filter doesn't return exactly one matching object. +- `dn`: _optional_, default: undefined + `dn` is an array of template DN to check for the given uid. This is an alternate mode of lookup, where the plugin inserts the uid in the DN template and immediately tries to bind instead of doing a search for the DN first. A template DN looks like `uid=%u,ou=users,dc=example,dc=com`. The param `%u` denotes the uid/username as given during login. ### \[authz\] Adding the \[authn\] section enables authorization (authz) against LDAP servers, i.e. if the given user is allowed to use the given "FROM" address. -* `basedn`: *optional*, default: as set in main section -It's possible to override the default basedn -* `scope`: *optional*, default: as set in main section -It's possible to override the default scope -* `searchfilter`: *optional*, default: (&(objectclass=*)(uid=%u)(mail=%a)) -Search filter to verify authorization. If the search result yields at least one object, authorization is given. The param `%u` denotes the uid/username as given during login. The param `%a` denotes the email address as given in "FROM". - +- `basedn`: _optional_, default: as set in main section + It's possible to override the default basedn +- `scope`: _optional_, default: as set in main section + It's possible to override the default scope +- `searchfilter`: _optional_, default: (&(objectclass=\*)(uid=%u)(mail=%a)) + Search filter to verify authorization. If the search result yields at least one object, authorization is given. The param `%u` denotes the uid/username as given during login. The param `%a` denotes the email address as given in "FROM". ### \[rcpt_to\] Enable \[rcpt_to\] to verify that a given recipient address exists in LDAP. -* `basedn`: *optional*, default: as set in main section -It's possible to override the default basedn -* `scope`: *optional*, default: as set in main section -It's possible to override the default scope -* `searchfilter`: *optional*, default: (&(objectclass=*)(mail=%a)) -Search filter to look up the given address. The plugin will call `next(OK)` only if the search returned at least one object. The param `%a` denotes the email address given as recipient. - +- `basedn`: _optional_, default: as set in main section + It's possible to override the default basedn +- `scope`: _optional_, default: as set in main section + It's possible to override the default scope +- `searchfilter`: _optional_, default: (&(objectclass=\*)(mail=%a)) + Search filter to look up the given address. The plugin will call `next(OK)` only if the search returned at least one object. The param `%a` denotes the email address given as recipient. ## Examples - ### \[authn\] Below are two examples to explain both modes of operation. - #### By search + Given the following configuration: ``` @@ -118,8 +111,8 @@ searchfilter = (&(objectclass=*)(uid=%u)) Here the plugin will search for the object(s) first. The search filter should return some object's DN like `uid=user1,ou=users,dc=example,dc=com`. Then the plugin will attempt a simple bind with the found DN and the given password. - #### By DN templates + Given the following configuration: ``` @@ -129,7 +122,6 @@ dn[] = uid=%u,ou=people,dc=example,dc=com The plugin will replace `%u` with the given username and immediately attempts to simple bind with the resulting DN(s) and the given password. - #### Difference between both approaches While the search filter approach offers more flexibility, a limited number of DN templates might be faster as they don't need to search first. @@ -146,11 +138,12 @@ uid: nonunique In this scenario, the search filter approach will always deny login for uid `nonunique`, because the search doesn't return exactly one single result. However, if using DN templates instead the user would be able to log in. - ### aliases + Following are a few examples to explain the proper usage of aliases. #### simple aliases + It is possible to use email aliases to deliver email for one address to another address. Given the following LDAP objects: ``` @@ -176,8 +169,8 @@ attribute = mailRoutingAddress Given this configuration, the haraka-plugin-ldap-aliases plugin will simply change recipients that match the given searchfilter to the value referenced by the `mailRoutingAddress` attribute: Mail send to `forwarder@example.com` will be delivered to `user@example.com`. - #### attribute_is_dn + attribute_is_dn is handy to use LDAP groups as mail groups. Let's check the following LDAP group and user: ``` @@ -205,7 +198,6 @@ subattribute = mailLocalAddress The search filter applies only to groups (`objectclass=groupOfNames`) with an email address of the alias email (`mailLocalAddress=%a`). Then the plugin checks the group's attribute `member` and assumes it contains a DN (`attribute_is_dn = true`) and looks up and returns every member DN's attribute `mailLocalAddress`. In other words, email to `postmaster@example.com` would be send to `user@example.com`. Of course a group may contain multiple members, in which case every member with a valid `mailLocalAddress` would receive the email. - [ci-img]: https://github.com/haraka/haraka-plugin-ldap/actions/workflows/ci.yml/badge.svg [ci-url]: https://github.com/haraka/haraka-plugin-ldap/actions/workflows/ci.yml [clim-img]: https://codeclimate.com/github/haraka/haraka-plugin-ldap/badges/gpa.svg diff --git a/aliases.js b/aliases.js index ed76cb3..cbf6190 100644 --- a/aliases.js +++ b/aliases.js @@ -1,7 +1,7 @@ 'use strict'; -const util = require('util'); -const Address = require('address-rfc2821').Address; +const util = require('util'); +const Address = require('address-rfc2821').Address; const constants = require('haraka-constants'); exports._get_alias = function (address, callback, connection) { @@ -9,20 +9,21 @@ exports._get_alias = function (address, callback, connection) { if (!pool) { return onError('LDAP Pool not found!'); } - function onError (err) { - connection.logerror(`Could not resolve ${address} as alias`) + function onError(err) { + connection.logerror(`Could not resolve ${address} as alias`); connection.logdebug(`${util.inspect(err)}`); callback(err, false); } const search = (err, client) => { - if (err) - return onError(err); + if (err) return onError(err); const config = this._get_search_conf_alias(address, connection); - connection.logdebug(`Checking address for alias: ${ util.inspect(config)}`); + connection.logdebug(`Checking address for alias: ${util.inspect(config)}`); try { client.search(config.basedn, config, (search_error, res) => { - if (search_error) { onError(search_error); } + if (search_error) { + onError(search_error); + } let alias = []; res.on('searchEntry', (entry) => { alias = alias.concat(entry.object[config.attributes[0]]); @@ -31,108 +32,126 @@ exports._get_alias = function (address, callback, connection) { res.on('end', () => { if (pool.config.aliases.attribute_is_dn) { this._resolve_dn_to_alias(alias, callback, connection); - } - else { + } else { callback(null, alias); } - }) - }) - } - catch (e) { + }); + }); + } catch (e) { onError(e); } }; pool.get(search); -} +}; exports._get_search_conf_alias = (address, connection) => { const pool = connection.server.notes.ldappool; - let filter = pool.config.aliases.searchfilter || '(&(objectclass=*)(mail=%a)(mailForwardAddress=*))'; + let filter = + pool.config.aliases.searchfilter || + '(&(objectclass=*)(mail=%a)(mailForwardAddress=*))'; filter = filter.replace(/%a/g, address); return { basedn: pool.config.aliases.basedn || pool.config.basedn, filter, scope: pool.config.aliases.scope || pool.config.scope, - attributes: [ pool.config.aliases.attribute || 'mailForwardingAddress' ] + attributes: [pool.config.aliases.attribute || 'mailForwardingAddress'], }; -} +}; exports._resolve_dn_to_alias = (dn, callback, connection) => { const pool = connection.server.notes.ldappool; if (!pool) { return onError('LDAP Pool not found!'); } - function onError (err) { - connection.logerror(`Could not get address for DN ${ util.inspect(dn) }: ${ util.inspect(err)}`); + function onError(err) { + connection.logerror( + `Could not get address for DN ${util.inspect(dn)}: ${util.inspect(err)}`, + ); callback(err); } const config = { scope: 'base', - attributes: [ pool.config.aliases.subattribute || 'mailLocalAddress' ] + attributes: [pool.config.aliases.subattribute || 'mailLocalAddress'], }; pool.get((err, client) => { if (err) return onError(err); - connection.logdebug(`Resolving DN ${ util.inspect(dn) } to alias: ${ util.inspect(config)}`); + connection.logdebug( + `Resolving DN ${util.inspect(dn)} to alias: ${util.inspect(config)}`, + ); - const promises = [] + const promises = []; for (const d of dn) { - promises.push(new Promise((resolve) => { - const entries = [] + promises.push( + new Promise((resolve) => { + const entries = []; - client.search(d, config, (search_error, res) => { - if (search_error) - onError(search_error, d); + client.search(d, config, (search_error, res) => { + if (search_error) onError(search_error, d); - res.on('searchEntry', (entry) => { - const arr_addr = entry.object[config.attributes[0]]; - entries.push(Array.isArray(arr_addr) ? arr_addr[0] : arr_addr) - }) + res.on('searchEntry', (entry) => { + const arr_addr = entry.object[config.attributes[0]]; + entries.push(Array.isArray(arr_addr) ? arr_addr[0] : arr_addr); + }); - res.on('error', (e) => { - connection.logwarn(`Could not retrieve DN ${util.inspect(d)}`); - connection.logdebug(`${util.inspect(e)}`); - resolve([]); - }) + res.on('error', (e) => { + connection.logwarn(`Could not retrieve DN ${util.inspect(d)}`); + connection.logdebug(`${util.inspect(e)}`); + resolve([]); + }); - res.on('end', (r) => { - resolve(entries); - }) - }) - })) + res.on('end', (r) => { + resolve(entries); + }); + }); + }), + ); } Promise.all(promises) .then((res) => { - callback(null, res.flat()) + callback(null, res.flat()); }) - .catch(e => { - connection.logerror(`AllResolvedErr: ${e}`) - }) - }) -} + .catch((e) => { + connection.logerror(`AllResolvedErr: ${e}`); + }); + }); +}; exports.aliases = function (next, connection, params) { if (!params || !params[0] || !params[0].address) { - connection.logerror(`Ignoring invalid call. Given params: ${util.inspect(params)}`); + connection.logerror( + `Ignoring invalid call. Given params: ${util.inspect(params)}`, + ); return next(); } const rcpt = params[0].address(); - this._get_alias(rcpt, (err, result) => { - if (err) { - connection.logerror(`Could not use LDAP to resolve aliases: ${err.message}`); - return next(constants.denysoft); - } - if (result.length === 0) { - connection.logdebug(`No aliases results found for rcpt: ${util.inspect(rcpt)}`); - return next(); - } - connection.logdebug(this, `Aliasing ${util.inspect(rcpt)} to ${util.inspect(result)}`); - connection.transaction.rcpt_to.pop(); - for (const element of result) { - const toAddress = new Address(`<${element}>`); - connection.transaction.rcpt_to.push(toAddress); - } - next(); - }, connection); -} + this._get_alias( + rcpt, + (err, result) => { + if (err) { + connection.logerror( + `Could not use LDAP to resolve aliases: ${err.message}`, + ); + return next(constants.denysoft); + } + if (result.length === 0) { + connection.logdebug( + `No aliases results found for rcpt: ${util.inspect(rcpt)}`, + ); + return next(); + } + connection.logdebug( + this, + `Aliasing ${util.inspect(rcpt)} to ${util.inspect(result)}`, + ); + connection.transaction.rcpt_to.pop(); + for (const element of result) { + const toAddress = new Address(`<${element}>`); + connection.transaction.rcpt_to.push(toAddress); + } + next(); + }, + connection, + ); +}; diff --git a/authn.js b/authn.js index c3d6db4..d370434 100644 --- a/authn.js +++ b/authn.js @@ -1,12 +1,14 @@ 'use strict'; -const util = require('util'); +const util = require('util'); exports._verify_user = (userdn, passwd, cb, connection) => { const pool = connection.server.notes.ldappool; - function onError (err) { - connection.logerror(`Could not verify userdn and password: ${ util.inspect(err)}`); + function onError(err) { + connection.logerror( + `Could not verify userdn and password: ${util.inspect(err)}`, + ); cb(false); } @@ -19,14 +21,16 @@ exports._verify_user = (userdn, passwd, cb, connection) => { client.unbind(); if (err2) { - connection.logdebug(`Login failed, could not bind ${ util.inspect(userdn) }: ${ util.inspect(err)}`); - return cb(false) + connection.logdebug( + `Login failed, could not bind ${util.inspect(userdn)}: ${util.inspect(err)}`, + ); + return cb(false); } cb(true); - }) - }) -} + }); + }); +}; exports._get_search_conf = (user, connection) => { const pool = connection.server.notes.ldappool; @@ -35,14 +39,14 @@ exports._get_search_conf = (user, connection) => { basedn: pool.config.authn.basedn || pool.config.basedn, filter: filter.replace(/%u/g, user), scope: pool.config.authn.scope || pool.config.scope, - attributes: ['dn'] + attributes: ['dn'], }; }; exports._get_dn_for_uid = function (uid, callback, connection) { const pool = connection.server.notes.ldappool; - function onError (err) { - connection.logerror(`Could not get DN for UID ${uid}`) + function onError(err) { + connection.logerror(`Could not get DN for UID ${uid}`); connection.logdebug(`: ${util.inspect(err)}`); callback(err); } @@ -52,11 +56,11 @@ exports._get_dn_for_uid = function (uid, callback, connection) { if (err) return onError(err); const config = this._get_search_conf(uid, connection); - connection.logdebug(`Getting DN for uid: ${ util.inspect(config)}`); + connection.logdebug(`Getting DN for uid: ${util.inspect(config)}`); try { client.search(config.basedn, config, (search_error, res) => { if (search_error) onError(search_error); - const userdn=[]; + const userdn = []; res.on('searchEntry', (entry) => { userdn.push(entry.object.dn); }); @@ -65,55 +69,59 @@ exports._get_dn_for_uid = function (uid, callback, connection) { callback(null, userdn); }); }); - } - catch (e) { + } catch (e) { onError(e); } - }) -} + }); +}; exports.check_plain_passwd = function (connection, user, passwd, cb) { - if (Array.isArray(connection.server.notes.ldappool.config.authn.dn)) { - return this.check_plain_passwd_dn(connection, user, passwd, cb) + return this.check_plain_passwd_dn(connection, user, passwd, cb); } - connection.logdebug(`Looking up user ${ util.inspect(user) } by search.`); - this._get_dn_for_uid(user, (err, userdn) => { - if (err) { - connection.logerror(`Could not use LDAP for password check: ${ util.inspect(err)}`); - cb(false); - } - else if (userdn.length !== 1) { - connection.logdebug(`None or nonunique LDAP search result for user ${ util.inspect(user) }, access denied`); - cb(false); - } - else { - this._verify_user(userdn[0], passwd, cb, connection); - } - }, connection); + connection.logdebug(`Looking up user ${util.inspect(user)} by search.`); + this._get_dn_for_uid( + user, + (err, userdn) => { + if (err) { + connection.logerror( + `Could not use LDAP for password check: ${util.inspect(err)}`, + ); + cb(false); + } else if (userdn.length !== 1) { + connection.logdebug( + `None or nonunique LDAP search result for user ${util.inspect(user)}, access denied`, + ); + cb(false); + } else { + this._verify_user(userdn[0], passwd, cb, connection); + } + }, + connection, + ); }; exports.check_plain_passwd_dn = function (connection, user, passwd, cb) { - connection.logdebug(`Looking up user ${ util.inspect(user) } by DN.`); + connection.logdebug(`Looking up user ${util.inspect(user)} by DN.`); - let iter = 0 - let cbCalled = false + let iter = 0; + let cbCalled = false; - function cbOnce (result) { - iter++ - if (cbCalled) return + function cbOnce(result) { + iter++; + if (cbCalled) return; if (result) { - cbCalled = true - return cb(result) + cbCalled = true; + return cb(result); } if (iter === connection.server.notes.ldappool.config.authn.dn.length) { - cbCalled = true - cb(result) + cbCalled = true; + cb(result); } } for (const dn of connection.server.notes.ldappool.config.authn.dn) { this._verify_user(dn.replace(/%u/g, user), passwd, cbOnce, connection); } -} +}; diff --git a/authz.js b/authz.js index 6896be2..3b88406 100644 --- a/authz.js +++ b/authz.js @@ -1,15 +1,15 @@ 'use strict'; -const util = require('util'); +const util = require('util'); const constants = require('haraka-constants'); exports._verify_address = function (uid, address, callback, connection) { const pool = connection.server.notes.ldappool; const onError = (err) => { - connection.logerror(`Could not verify address ${address} for UID ${uid}`) + connection.logerror(`Could not verify address ${address} for UID ${uid}`); connection.logdebug(`${util.inspect(err)}`); callback(err, false); - } + }; if (!pool) return onError('LDAP Pool not found!'); @@ -17,10 +17,12 @@ exports._verify_address = function (uid, address, callback, connection) { if (err) return onError(err); const config = this._get_search_conf(uid, address, connection); - connection.logdebug(`Verifying address: ${ util.inspect(config)}`); + connection.logdebug(`Verifying address: ${util.inspect(config)}`); try { client.search(config.basedn, config, (search_error, res) => { - if (search_error) { onError(search_error); } + if (search_error) { + onError(search_error); + } let entries = 0; res.on('searchEntry', (entry) => { entries++; @@ -30,44 +32,60 @@ exports._verify_address = function (uid, address, callback, connection) { callback(null, entries > 0); }); }); - } - catch (e) { + } catch (e) { return onError(e); } }); -} +}; exports._get_search_conf = (user, address, connection) => { const pool = connection.server.notes.ldappool; - let filter = pool.config.authz.searchfilter || '(&(objectclass=*)(uid=%u)(mail=%a))'; + let filter = + pool.config.authz.searchfilter || '(&(objectclass=*)(uid=%u)(mail=%a))'; filter = filter.replace(/%u/g, user).replace(/%a/g, address); return { basedn: pool.config.authz.basedn || pool.config.basedn, filter, scope: pool.config.authz.scope || pool.config.scope, - attributes: [ 'dn' ] + attributes: ['dn'], }; -} +}; exports.check_authz = function (next, connection, params) { - if (!connection.notes || !connection.notes.auth_user || !params || !params[0] || !params[0].address) { - connection.logerror(`${'Ignoring invalid call. Given params are ' + - ' connection.notes:'}${util.inspect(connection.notes) - } and params:${util.inspect(params)}`); + if ( + !connection.notes || + !connection.notes.auth_user || + !params || + !params[0] || + !params[0].address + ) { + connection.logerror( + `${ + 'Ignoring invalid call. Given params are ' + ' connection.notes:' + }${util.inspect(connection.notes)} and params:${util.inspect(params)}`, + ); return next(); } const uid = connection.notes.auth_user; const address = params[0].address(); - this._verify_address(uid, address, (err, verified) => { - if (err) { - connection.logerror(`Could not use LDAP to match address to uid: ${err.message}`); - next(constants.denysoft); - } - else if (verified) { - next(); - } - else { - next(constants.deny, `User ${ util.inspect(uid) } not allowed to send from address ${ util.inspect(address) }.`); - } - }, connection); -} + this._verify_address( + uid, + address, + (err, verified) => { + if (err) { + connection.logerror( + `Could not use LDAP to match address to uid: ${err.message}`, + ); + next(constants.denysoft); + } else if (verified) { + next(); + } else { + next( + constants.deny, + `User ${util.inspect(uid)} not allowed to send from address ${util.inspect(address)}.`, + ); + } + }, + connection, + ); +}; diff --git a/index.js b/index.js index 6a4626f..100d667 100644 --- a/index.js +++ b/index.js @@ -1,10 +1,10 @@ 'use strict'; -const util = require('util'); -const authn = require('./authn'); -const aliases = require('./aliases'); -const rcpt_to = require('./rcpt_to'); -const authz = require('./authz'); +const util = require('util'); +const authn = require('./authn'); +const aliases = require('./aliases'); +const rcpt_to = require('./rcpt_to'); +const authz = require('./authz'); const LdapPool = require('./pool').LdapPool; const AUTH_COMMAND = 'AUTH'; @@ -23,90 +23,89 @@ exports.handle_authn = function (next, connection, params) { if (!connection.notes.authenticating) return next(); switch (connection.notes.auth_method) { - case AUTH_METHOD_LOGIN: - this.auth_login(next, connection, params); - break; - case AUTH_METHOD_PLAIN: - this.auth_plain(next, connection, params); - break; - default: - next(); + case AUTH_METHOD_LOGIN: + this.auth_login(next, connection, params); + break; + case AUTH_METHOD_PLAIN: + this.auth_plain(next, connection, params); + break; + default: + next(); } -} +}; exports.hook_capabilities = (next, connection) => { // default: don't offer AUTH unless session is encrypted if (connection.using_tls) { - const methods = [ 'PLAIN', 'LOGIN' ]; - connection.capabilities.push(`AUTH ${ methods.join(' ')}`); + const methods = ['PLAIN', 'LOGIN']; + connection.capabilities.push(`AUTH ${methods.join(' ')}`); connection.notes.allowed_auth_methods = methods; } next(); -} +}; exports.check_plain_passwd = function () { authn.check_plain_passwd(...arguments); -} +}; exports.aliases = function (next, connection, params) { if (!connection.server.notes.ldappool.config.aliases) return next(); aliases.aliases(...arguments); -} +}; exports.check_rcpt = function (next, connection, params) { if (!connection.server.notes.ldappool.config.rcpt_to) return next(); rcpt_to.check_rcpt.apply(rcpt_to, arguments); -} +}; exports.check_authz = function (next, connection, params) { if (!connection.server.notes.ldappool.config.authz) return next(); authz.check_authz.apply(authz, arguments); -} +}; exports.register = function () { this.inherits('auth/auth_base'); - this.register_hook('init_master', '_init_ldappool'); - this.register_hook('init_child', '_init_ldappool'); + this.register_hook('init_master', '_init_ldappool'); + this.register_hook('init_child', '_init_ldappool'); this.register_hook('rcpt', 'aliases'); this.register_hook('rcpt', 'check_rcpt'); this.register_hook('mail', 'check_authz'); this.register_hook('unrecognized_command', 'handle_authn'); this._load_ldap_ini(); -} +}; exports._load_ldap_ini = function () { - - this.loginfo("loading ldap.ini"); + this.loginfo('loading ldap.ini'); const cfg = this.config.get('ldap.ini', () => { this._load_ldap_ini(); }); if (this._pool) { this._pool._set_config(cfg); - this.logdebug(`Current config: ${ util.inspect(this._pool.config)}`); - } - else { + this.logdebug(`Current config: ${util.inspect(this._pool.config)}`); + } else { this._tmp_pool_config = cfg; } -} +}; exports._init_ldappool = function (next, server) { - if (!server.notes.ldappool) { server.notes.ldappool = new LdapPool(); if (this._tmp_pool_config) { server.notes.ldappool._set_config(this._tmp_pool_config); this._tmp_pool_config = undefined; - this.logdebug(`Current config: ${ util.inspect(server.notes.ldappool.config)}`); + this.logdebug( + `Current config: ${util.inspect(server.notes.ldappool.config)}`, + ); } } this._pool = server.notes.ldappool; next(); -} +}; exports.shutdown = function (next) { - if (this._pool) this._pool.close(next || function () { }); -} + if (this._pool) this._pool.close(next || function () {}); +}; diff --git a/package.json b/package.json index 76ab511..b9feaa5 100644 --- a/package.json +++ b/package.json @@ -2,11 +2,24 @@ "name": "haraka-plugin-ldap", "version": "1.1.3", "description": "Haraka plugin for LDAP", + "files": [ + "aliases.js", + "authn.js", + "authz.js", + "pool.js", + "rcpt_to.js", + "CHANGELOG.md", + "config", + "test" + ], "main": "index.js", "directories": {}, "scripts": { - "lint": "npx eslint *.js test/*.js", - "lintfix": "npx eslint --fix *.js test/*.js", + "format": "npm run prettier:fix && npm run lint:fix", + "lint": "npx eslint@^8 *.js test/*.js", + "lint:fix": "npx eslint@^8 *.js test/*.js --fix", + "prettier": "npx prettier . --check", + "prettier:fix": "npx prettier . --write --log-level=warn", "test": "npx mocha --exit", "versions": "npx dependency-version-checker check" }, @@ -30,8 +43,8 @@ "devDependencies": { "btoa": "*", "eslint": "8.57.0", - "eslint-plugin-haraka": "*", - "haraka-test-fixtures": "*", + "@haraka/eslint-config": "^1.1.2", + "haraka-test-fixtures": "^1.0.0", "mocha": "10.3.0" }, "dependencies": { diff --git a/pool.js b/pool.js index 898bf6b..15ee970 100644 --- a/pool.js +++ b/pool.js @@ -3,12 +3,12 @@ const ldap = require('ldapjs'); class LdapPool { - constructor (config) { + constructor(config) { this._set_config(config); - this.pool = { 'servers': [] }; + this.pool = { servers: [] }; } - _set_config (config = {}) { + _set_config(config = {}) { if (config.main === undefined) config.main = {}; this.config = { servers: config.main.server || ['ldap://localhost:389'], @@ -22,31 +22,31 @@ class LdapPool { aliases: config.aliases, authn: config.authn, authz: config.authz, - rcpt_to: config.rcpt_to + rcpt_to: config.rcpt_to, }; return this.config; } - _get_ldapjs_config () { + _get_ldapjs_config() { const config = { url: this.config.servers.shift(), - timeout: this.config.timeout + timeout: this.config.timeout, }; this.config.servers.push(config.url); if (this.config.tls_rejectUnauthorized !== undefined) { config.tlsOptions = { - rejectUnauthorized: this.config.tls_rejectUnauthorized + rejectUnauthorized: this.config.tls_rejectUnauthorized, }; } return config; } - _create_client (next) { + _create_client(next) { const client = ldap.createClient(this._get_ldapjs_config()); client.on('connectError', (err) => { - console.error(err) - }) + console.error(err); + }); if (!this.config.tls_enabled) return next(null, client); @@ -56,9 +56,8 @@ class LdapPool { }); } - close (next) { - if (this.pool.servers.length <= 0) - return next(); + close(next) { + if (this.pool.servers.length <= 0) return next(); while (this.pool.servers.length > 0) { this.pool.servers.shift().unbind(); @@ -66,25 +65,23 @@ class LdapPool { } } - _bind_default (next) { + _bind_default(next) { const cfg = this.config; this._create_client((err, client) => { - if (err) - return next(err); + if (err) return next(err); if (cfg.binddn && cfg.bindpw) { client.bind(cfg.binddn, cfg.bindpw, (err2) => { next(err2, client); }); - } - else { + } else { next(null, client); } }); } - get (next) { + get(next) { const pool = this.pool; if (pool.servers.length >= this.config.servers.length) { diff --git a/rcpt_to.js b/rcpt_to.js index 957c23f..ec1450b 100644 --- a/rcpt_to.js +++ b/rcpt_to.js @@ -1,12 +1,12 @@ 'use strict'; -const util = require('util'); +const util = require('util'); const constants = require('haraka-constants'); exports._verify_existence = function (address, callback, connection) { const pool = connection.server.notes.ldappool; - function onError (err) { - connection.logerror(`Could not verify address ${address}`) + function onError(err) { + connection.logerror(`Could not verify address ${address}`); connection.logdebug(`${util.inspect(err)}`); callback(err, false); } @@ -16,10 +16,12 @@ exports._verify_existence = function (address, callback, connection) { if (err) return onError(err); const config = this._get_search_conf(address, connection); - connection.logdebug(`Verifying existence: ${ util.inspect(config)}`); + connection.logdebug(`Verifying existence: ${util.inspect(config)}`); try { client.search(config.basedn, config, (search_error, res) => { - if (search_error) { onError(search_error); } + if (search_error) { + onError(search_error); + } let entries = 0; res.on('searchEntry', (entry) => { entries++; @@ -29,8 +31,7 @@ exports._verify_existence = function (address, callback, connection) { callback(null, entries > 0); }); }); - } - catch (e) { + } catch (e) { onError(e); } }); @@ -38,32 +39,39 @@ exports._verify_existence = function (address, callback, connection) { exports._get_search_conf = (address, connection) => { const pool = connection.server.notes.ldappool; - let filter = pool.config.rcpt_to.searchfilter || '(&(objectclass=*)(mail=%a))'; + let filter = + pool.config.rcpt_to.searchfilter || '(&(objectclass=*)(mail=%a))'; filter = filter.replace(/%a/g, address); return { basedn: pool.config.rcpt_to.basedn || pool.config.basedn, filter, scope: pool.config.rcpt_to.scope || pool.config.scope, - attributes: [ 'dn' ] + attributes: ['dn'], }; }; exports.check_rcpt = function (next, connection, params) { if (!params || !params[0] || !params[0].address) { - connection.logerror(`Ignoring invalid call. Given connection.transaction: ${util.inspect(connection.transaction)}`); + connection.logerror( + `Ignoring invalid call. Given connection.transaction: ${util.inspect(connection.transaction)}`, + ); return next(); } const rcpt = params[0].address(); - this._verify_existence(rcpt, (err, result) => { - if (err) { - connection.logerror(`Could not use LDAP for address check: ${err.message}`); - next(constants.denysoft); - } - else if (result) { - next(constants.ok); - } - else { - next(constants.deny); - } - }, connection); + this._verify_existence( + rcpt, + (err, result) => { + if (err) { + connection.logerror( + `Could not use LDAP for address check: ${err.message}`, + ); + next(constants.denysoft); + } else if (result) { + next(constants.ok); + } else { + next(constants.deny); + } + }, + connection, + ); }; diff --git a/test/aliases.js b/test/aliases.js index e58b899..eca4476 100644 --- a/test/aliases.js +++ b/test/aliases.js @@ -1,247 +1,321 @@ 'use strict'; -const assert = require('assert') -const util = require('util'); +const assert = require('assert'); +const util = require('util'); -const fixtures = require('haraka-test-fixtures'); +const fixtures = require('haraka-test-fixtures'); const constants = require('haraka-constants'); -const ldappool = require('../pool'); +const ldappool = require('../pool'); -function _set_up (done) { +function _set_up(done) { this.user = { - uid : 'user1', - dn : 'uid=user1,ou=users,dc=example,dc=com', - password : 'ykaHsOzEZD', - mail : 'user1@example.com' + uid: 'user1', + dn: 'uid=user1,ou=users,dc=example,dc=com', + password: 'ykaHsOzEZD', + mail: 'user1@example.com', }; this.group = { - dn : 'cn=postmaster,dc=example,dc=com', - mail : 'postmaster@example.com', - member : [ + dn: 'cn=postmaster,dc=example,dc=com', + mail: 'postmaster@example.com', + member: [ 'uid=user1,ou=users,dc=example,dc=com', 'uid=user2,ou=people,dc=example,dc=com', - 'uid=nonunique,ou=users,dc=example,dc=com' - ] + 'uid=nonunique,ou=users,dc=example,dc=com', + ], }; this.plugin = require('../aliases'); this.connection = fixtures.connection.createConnection(); this.connection.transaction = {}; this.connection.server = { - notes : { - ldappool : new ldappool.LdapPool({ - main : { - server : [ 'ldap://localhost:3389' ], - binddn : this.user.dn, - bindpw : this.user.password, - basedn : 'dc=example,dc=com' - } - }) - } + notes: { + ldappool: new ldappool.LdapPool({ + main: { + server: ['ldap://localhost:3389'], + binddn: this.user.dn, + bindpw: this.user.password, + basedn: 'dc=example,dc=com', + }, + }), + }, }; this.connection.server.notes.ldappool.config.aliases = { - subattribute : 'mailLocalAddress', - attribute : 'member', - searchfilter : '(&(objectclass=groupOfNames)(mailLocalAddress=%a))' + subattribute: 'mailLocalAddress', + attribute: 'member', + searchfilter: '(&(objectclass=groupOfNames)(mailLocalAddress=%a))', }; done(); } describe('_get_alias', function () { - beforeEach(_set_up); it('ok with test group', function (done) { this.connection.server.notes.ldappool.config.aliases.attribute_is_dn = true; - this.plugin._get_alias(this.group.mail, (err, result) => { - assert.ifError(err); - assert.deepStrictEqual([ - 'nonunique1@example.com', - 'user1@example.com', - 'user2@example.com' - ], result.sort()) - done(); - }, this.connection); - }) + this.plugin._get_alias( + this.group.mail, + (err, result) => { + assert.ifError(err); + assert.deepStrictEqual( + ['nonunique1@example.com', 'user1@example.com', 'user2@example.com'], + result.sort(), + ); + done(); + }, + this.connection, + ); + }); it('ok with forwarding user', function (done) { - this.connection.server.notes.ldappool.config.aliases.searchfilter = '(&(objectclass=*)(mailLocalAddress=%a))'; - this.connection.server.notes.ldappool.config.aliases.attribute = 'mailRoutingAddress'; - this.plugin._get_alias('forwarder@example.com', function (err, result) { - assert.equal('user2@example.com', result[0]); - done(); - }, this.connection); - }) + this.connection.server.notes.ldappool.config.aliases.searchfilter = + '(&(objectclass=*)(mailLocalAddress=%a))'; + this.connection.server.notes.ldappool.config.aliases.attribute = + 'mailRoutingAddress'; + this.plugin._get_alias( + 'forwarder@example.com', + function (err, result) { + assert.equal('user2@example.com', result[0]); + done(); + }, + this.connection, + ); + }); it('ok with resolve-by-dn', function (done) { this.connection.server.notes.ldappool.config.aliases.attribute_is_dn = true; - this.plugin._get_alias('postmaster@example.com', function (err, result) { - const expected = [ 'user1@example.com', 'user2@example.com', 'nonunique1@example.com' ]; - expected.sort(); - result.sort(); - assert.equal(util.inspect(expected), util.inspect(result)); - done(); - }, this.connection); - }) + this.plugin._get_alias( + 'postmaster@example.com', + function (err, result) { + const expected = [ + 'user1@example.com', + 'user2@example.com', + 'nonunique1@example.com', + ]; + expected.sort(); + result.sort(); + assert.equal(util.inspect(expected), util.inspect(result)); + done(); + }, + this.connection, + ); + }); it('empty result with invalid mail', function (done) { - this.plugin._get_alias('invalid@email', function (err, result) { - done(); - }, this.connection); - }) -}) + this.plugin._get_alias( + 'invalid@email', + function (err, result) { + done(); + }, + this.connection, + ); + }); +}); describe('_get_search_conf_alias', function () { - beforeEach(_set_up); it('get defaults', function (done) { const pool = this.connection.server.notes.ldappool; pool.config.aliases.searchfilter = undefined; pool.config.aliases.attribute = undefined; - const opts = this.plugin._get_search_conf_alias('testMail', this.connection); + const opts = this.plugin._get_search_conf_alias( + 'testMail', + this.connection, + ); assert.equal(opts.basedn, pool.config.basedn); - assert.equal(opts.filter, '(&(objectclass=*)(mail=testMail)(mailForwardAddress=*))'); + assert.equal( + opts.filter, + '(&(objectclass=*)(mail=testMail)(mailForwardAddress=*))', + ); assert.equal(opts.scope, pool.config.scope); - assert.equal(opts.attributes.toString(), ['mailForwardingAddress'].toString()); + assert.equal( + opts.attributes.toString(), + ['mailForwardingAddress'].toString(), + ); done(); - }) + }); it('get userdef', function (done) { const pool = this.connection.server.notes.ldappool; pool.config.aliases.basedn = 'hop around as you like'; pool.config.aliases.searchfilter = '(&(objectclass=posixAccount)(mail=%a))'; pool.config.aliases.scope = 'one two three'; - const opts = this.plugin._get_search_conf_alias('testMail', this.connection); + const opts = this.plugin._get_search_conf_alias( + 'testMail', + this.connection, + ); assert.equal(opts.basedn, 'hop around as you like'); assert.equal(opts.filter, '(&(objectclass=posixAccount)(mail=testMail))'); assert.equal(opts.scope, 'one two three'); assert.equal(opts.attributes.toString(), ['member'].toString()); done(); - }) -}) + }); +}); describe('_resolve_dn_to_alias', function () { - beforeEach(_set_up); it('ok one', function (done) { const user = this.user; - this.plugin._resolve_dn_to_alias([this.user.dn], function (err, result) { - assert.equal(user.mail, result); - done(); - }, this.connection); - }) + this.plugin._resolve_dn_to_alias( + [this.user.dn], + function (err, result) { + assert.equal(user.mail, result); + done(); + }, + this.connection, + ); + }); it('ok multiple', function (done) { - this.plugin._resolve_dn_to_alias(this.group.member, function (err, result) { - result.sort(); - assert.equal('nonunique1@example.com', result[0]); - assert.equal('user1@example.com', result[1]); - assert.equal('user2@example.com', result[2]); - done(); - }, this.connection); - }) + this.plugin._resolve_dn_to_alias( + this.group.member, + function (err, result) { + result.sort(); + assert.equal('nonunique1@example.com', result[0]); + assert.equal('user1@example.com', result[1]); + assert.equal('user2@example.com', result[2]); + done(); + }, + this.connection, + ); + }); it('empty array when unknown dn', function (done) { - this.plugin._resolve_dn_to_alias(['uid=unknown,dc=wherever,dc=com'], function (err, result) { - assert.equal(0, result.length); - done(); - }, this.connection); - }) -}) + this.plugin._resolve_dn_to_alias( + ['uid=unknown,dc=wherever,dc=com'], + function (err, result) { + assert.equal(0, result.length); + done(); + }, + this.connection, + ); + }); +}); describe('aliases', function () { - beforeEach(_set_up); it('ignore if invalid call / no rcpt', function (done) { const plugin = this.plugin; const connection = this.connection; - function noParams (result) { + function noParams(result) { assert.equal(undefined, result); plugin.aliases(noRcpt, connection, []); } - function noRcpt (result) { + function noRcpt(result) { assert.equal(undefined, result); - plugin.aliases(noRcptAddress, connection, [ {} ]); + plugin.aliases(noRcptAddress, connection, [{}]); } - function noRcptAddress (result) { + function noRcptAddress(result) { assert.equal(undefined, result); done(); } plugin.aliases(noParams, connection); - }) + }); it('DENYSOFT if LDAP not usable', function (done) { const plugin = this.plugin; const user = this.user; - this.connection.server.notes.ldappool.config.aliases.searchfilter = '(&(objectclass=posixAccount)(mail=%a'; - plugin.aliases(function (result) { - assert.equal(constants.denysoft, result); - done(); - }, this.connection, [ { address : () => { - return user.mail; - }}]); - }) + this.connection.server.notes.ldappool.config.aliases.searchfilter = + '(&(objectclass=posixAccount)(mail=%a'; + plugin.aliases( + function (result) { + assert.equal(constants.denysoft, result); + done(); + }, + this.connection, + [ + { + address: () => { + return user.mail; + }, + }, + ], + ); + }); it('next if no results', function (done) { const plugin = this.plugin; - function next (result) { + function next(result) { assert.equal(undefined, result); done(); } - plugin.aliases(next, this.connection, [ { address : () => { - return 'unknown@mail'; - }}]); - }) + plugin.aliases(next, this.connection, [ + { + address: () => { + return 'unknown@mail'; + }, + }, + ]); + }); it('resolve group members', function (done) { const plugin = this.plugin; const group = this.group; const connection = this.connection; - connection.transaction = { rcpt_to : [ group.mail ] }; + connection.transaction = { rcpt_to: [group.mail] }; this.connection.server.notes.ldappool.config.aliases.attribute_is_dn = true; const expected = [ '', '', - '' + '', ]; expected.sort(); - function next (result) { + function next(result) { assert.equal(undefined, result); connection.transaction.rcpt_to.sort(); - assert.equal(expected.toString(), connection.transaction.rcpt_to.toString()); + assert.equal( + expected.toString(), + connection.transaction.rcpt_to.toString(), + ); done(); } - plugin.aliases(next, connection, [ { address : () => { - return group.mail; - }}]); - }) + plugin.aliases(next, connection, [ + { + address: () => { + return group.mail; + }, + }, + ]); + }); it('do not change non-aliased user', function (done) { const plugin = this.plugin; const user = this.user; const connection = this.connection; - connection.transaction = { rcpt_to : [ 'still the same' ] }; - function next (result) { + connection.transaction = { rcpt_to: ['still the same'] }; + function next(result) { assert.equal(undefined, result); assert.equal('still the same', connection.transaction.rcpt_to.toString()); done(); } - plugin.aliases(next, connection, [ { address : () => { - return user.mail; - }}]); - }) + plugin.aliases(next, connection, [ + { + address: () => { + return user.mail; + }, + }, + ]); + }); it('resolve forwarding user', function (done) { const plugin = this.plugin; const connection = this.connection; - connection.transaction = { rcpt_to : [ 'forwarder@example.com' ] }; - connection.server.notes.ldappool.config.aliases.searchfilter = '(&(objectclass=*)(mailLocalAddress=%a))'; - connection.server.notes.ldappool.config.aliases.attribute = 'mailRoutingAddress'; - function next (result) { + connection.transaction = { rcpt_to: ['forwarder@example.com'] }; + connection.server.notes.ldappool.config.aliases.searchfilter = + '(&(objectclass=*)(mailLocalAddress=%a))'; + connection.server.notes.ldappool.config.aliases.attribute = + 'mailRoutingAddress'; + function next(result) { assert.equal(undefined, result); - assert.equal('', connection.transaction.rcpt_to.toString()); + assert.equal( + '', + connection.transaction.rcpt_to.toString(), + ); done(); } - plugin.aliases(next, connection, [ { address : () => { - return 'forwarder@example.com'; - }}]); - }) -}) + plugin.aliases(next, connection, [ + { + address: () => { + return 'forwarder@example.com'; + }, + }, + ]); + }); +}); diff --git a/test/authn.js b/test/authn.js index b997b9d..78eb2f3 100644 --- a/test/authn.js +++ b/test/authn.js @@ -1,102 +1,120 @@ 'use strict'; -const assert = require('assert') +const assert = require('assert'); -const fixtures = require('haraka-test-fixtures'); -const ldappool = require('../pool'); +const fixtures = require('haraka-test-fixtures'); +const ldappool = require('../pool'); // test user data as defined in testdata.ldif const users = [ { - uid : 'user1', - dn : 'uid=user1,ou=users,dc=example,dc=com', - password : 'ykaHsOzEZD', - mail : 'user1@example.com' + uid: 'user1', + dn: 'uid=user1,ou=users,dc=example,dc=com', + password: 'ykaHsOzEZD', + mail: 'user1@example.com', }, { - uid : 'user2', - dn : 'uid=user2,ou=people,dc=example,dc=com', - password : 'KQD9zs,LGv', - mail : 'user2@example.com' + uid: 'user2', + dn: 'uid=user2,ou=people,dc=example,dc=com', + password: 'KQD9zs,LGv', + mail: 'user2@example.com', }, { - uid : 'nonunique', - dn : 'uid=nonunique,ou=users,dc=example,dc=com', - password : 'CZVm3,BLlx', - mail : 'nonunique1@example.com' + uid: 'nonunique', + dn: 'uid=nonunique,ou=users,dc=example,dc=com', + password: 'CZVm3,BLlx', + mail: 'nonunique1@example.com', }, { - uid : 'nonunique', - dn : 'uid=nonunique,ou=people,dc=example,dc=com', - password : 'LsBHDGorAh', - mail : 'nonunique2@example.com' - } + uid: 'nonunique', + dn: 'uid=nonunique,ou=people,dc=example,dc=com', + password: 'LsBHDGorAh', + mail: 'nonunique2@example.com', + }, ]; -function _set_up (done) { +function _set_up(done) { this.users = users; this.plugin = require('../authn'); this.connection = fixtures.connection.createConnection(); this.connection.server = { notes: { - ldappool : new ldappool.LdapPool({ - main : { - server : [ 'ldap://localhost:3389' ], - binddn : this.users[0].dn, - bindpw : this.users[0].password, - basedn : 'dc=example,dc=com' - } - }) - } + ldappool: new ldappool.LdapPool({ + main: { + server: ['ldap://localhost:3389'], + binddn: this.users[0].dn, + bindpw: this.users[0].password, + basedn: 'dc=example,dc=com', + }, + }), + }, }; this.connection.server.notes.ldappool.config.authn = {}; done(); } describe('_verify_user', function () { - - beforeEach(_set_up) + beforeEach(_set_up); it('verifies test data', function (done) { let counter = 0; for (const user of users) { - this.plugin._verify_user(user.dn, user.password, (result) => { - assert.equal(true, result); - counter++; - if (counter === users.length) done(); - }, this.connection); + this.plugin._verify_user( + user.dn, + user.password, + (result) => { + assert.equal(true, result); + counter++; + if (counter === users.length) done(); + }, + this.connection, + ); } - }) + }); it('safety check: wrong password fails', function (done) { - this.plugin._verify_user(this.users[0].dn, 'wrong', function (ok) { - assert.equal(false, ok); - done(); - }, this.connection); - }) + this.plugin._verify_user( + this.users[0].dn, + 'wrong', + function (ok) { + assert.equal(false, ok); + done(); + }, + this.connection, + ); + }); it('safety check: invalid dn fails', function (done) { - this.plugin._verify_user('wrong', 'wrong', function (ok) { - assert.equal(false, ok); - done(); - }, this.connection); - }) + this.plugin._verify_user( + 'wrong', + 'wrong', + function (ok) { + assert.equal(false, ok); + done(); + }, + this.connection, + ); + }); it('no pool', function (done) { const plugin = this.plugin; const connection = this.connection; connection.server.notes.ldappool = undefined; const user = this.users[0]; - plugin._verify_user(user.dn, user.password, function (result) { - assert.equal(false, result); - done(); - }, connection); - }) -}) + plugin._verify_user( + user.dn, + user.password, + function (result) { + assert.equal(false, result); + done(); + }, + connection, + ); + }); +}); describe('_get_search_conf', function () { - - beforeEach(_set_up) + beforeEach(_set_up); it('get defaults', function (done) { const pool = this.connection.server.notes.ldappool; @@ -106,7 +124,7 @@ describe('_get_search_conf', function () { assert.equal(opts.scope, pool.config.scope); assert.equal(opts.attributes.toString(), ['dn'].toString()); done(); - }) + }); it('get userdef', function (done) { const pool = this.connection.server.notes.ldappool; @@ -119,121 +137,178 @@ describe('_get_search_conf', function () { assert.equal(opts.scope, 'one two three'); assert.equal(opts.attributes.toString(), ['dn'].toString()); done(); - }) -}) + }); +}); describe('get_dn_for_uid', function () { - - beforeEach(_set_up) + beforeEach(_set_up); it('user 1 dn2uid', function (done) { - this.plugin._get_dn_for_uid(users[0].uid, function (err, userdn) { - assert.equal(null, err); - assert.equal(userdn.toString(), users[0].dn); - done(); - }, this.connection); - }) + this.plugin._get_dn_for_uid( + users[0].uid, + function (err, userdn) { + assert.equal(null, err); + assert.equal(userdn.toString(), users[0].dn); + done(); + }, + this.connection, + ); + }); it('user 2 dn2uid', function (done) { - this.plugin._get_dn_for_uid(users[1].uid, function (err, userdn) { - assert.equal(null, err); - assert.equal(userdn.toString(), users[1].dn); - done(); - }, this.connection); - }) + this.plugin._get_dn_for_uid( + users[1].uid, + function (err, userdn) { + assert.equal(null, err); + assert.equal(userdn.toString(), users[1].dn); + done(); + }, + this.connection, + ); + }); it('nonunique dn2uid', function (done) { - this.plugin._get_dn_for_uid('nonunique', function (err, userdn) { - assert.equal(null, err); - assert.equal(2, userdn.length); - done(); - }, this.connection); - }) + this.plugin._get_dn_for_uid( + 'nonunique', + function (err, userdn) { + assert.equal(null, err); + assert.equal(2, userdn.length); + done(); + }, + this.connection, + ); + }); it('invalid uid', function (done) { - this.plugin._get_dn_for_uid('doesntexist', function (err, userdn) { - assert.equal(null, err); - assert.equal(0, userdn.length); - done(); - }, this.connection); - }) + this.plugin._get_dn_for_uid( + 'doesntexist', + function (err, userdn) { + assert.equal(null, err); + assert.equal(0, userdn.length); + done(); + }, + this.connection, + ); + }); it('invalid search filter', function (done) { const user = this.users[0]; const pool = this.connection.server.notes.ldappool; pool.config.authn.searchfilter = '(&(objectclass=*)(uid=%u'; - this.plugin._get_dn_for_uid(user.uid, function (err, userdn) { - assert.equal('unbalanced parens', err.message); - assert.equal(undefined, userdn); - done(); - }, this.connection); - }) + this.plugin._get_dn_for_uid( + user.uid, + function (err, userdn) { + assert.equal('unbalanced parens', err.message); + assert.equal(undefined, userdn); + done(); + }, + this.connection, + ); + }); it('invalid basedn', function (done) { const user = this.users[0]; this.connection.server.notes.ldappool.config.basedn = 'invalid'; - this.plugin._get_dn_for_uid(user.uid, function (err, userdn) { - assert.equal('InvalidDistinguishedNameError', err.name); - assert.equal(undefined, userdn); - done(); - }, this.connection); - }) + this.plugin._get_dn_for_uid( + user.uid, + function (err, userdn) { + assert.equal('InvalidDistinguishedNameError', err.name); + assert.equal(undefined, userdn); + done(); + }, + this.connection, + ); + }); it('no pool', function (done) { this.connection.server.notes.ldappool = undefined; const user = this.users[0]; - this.plugin._get_dn_for_uid(user.uid, function (err, userdn) { - assert.equal('LDAP Pool not found!', err); - assert.equal(undefined, userdn); - done(); - }, this.connection); - }) -}) + this.plugin._get_dn_for_uid( + user.uid, + function (err, userdn) { + assert.equal('LDAP Pool not found!', err); + assert.equal(undefined, userdn); + done(); + }, + this.connection, + ); + }); +}); describe('check_plain_passwd', function () { + beforeEach(_set_up); - beforeEach(_set_up) - - for (const user of users.slice(0,2)) { + for (const user of users.slice(0, 2)) { it(`validates user ${user.uid}`, function (done) { - this.plugin.check_plain_passwd(this.connection, user.uid, user.password, function (result) { - assert.equal(true, result); - done() - }) - }) + this.plugin.check_plain_passwd( + this.connection, + user.uid, + user.password, + function (result) { + assert.equal(true, result); + done(); + }, + ); + }); } for (const user of users.slice(2)) { it(`rejects user ${user.uid}`, function (done) { - this.plugin.check_plain_passwd(this.connection, user.uid, user.password, function (result) { - assert.equal(false, result); - done() - }) - }) + this.plugin.check_plain_passwd( + this.connection, + user.uid, + user.password, + function (result) { + assert.equal(false, result); + done(); + }, + ); + }); } it(`rejects invalid user`, function (done) { - this.plugin.check_plain_passwd(this.connection, 'invalid', 'invalid', function (result) { - assert.equal(false, result); - done(); - }); - }) + this.plugin.check_plain_passwd( + this.connection, + 'invalid', + 'invalid', + function (result) { + assert.equal(false, result); + done(); + }, + ); + }); for (const user of users) { it(`dn validates user ${user.uid}`, function (done) { - this.connection.server.notes.ldappool.config.authn.dn = [ 'uid=%u,ou=users,dc=example,dc=com', 'uid=%u,ou=people,dc=example,dc=com' ]; - this.plugin.check_plain_passwd(this.connection, user.uid, user.password, function (result) { - assert.strictEqual(true, result); - done() - }) - }) + this.connection.server.notes.ldappool.config.authn.dn = [ + 'uid=%u,ou=users,dc=example,dc=com', + 'uid=%u,ou=people,dc=example,dc=com', + ]; + this.plugin.check_plain_passwd( + this.connection, + user.uid, + user.password, + function (result) { + assert.strictEqual(true, result); + done(); + }, + ); + }); } it(`dn rejects invalid user`, function (done) { - this.connection.server.notes.ldappool.config.authn.dn = [ 'uid=%u,ou=users,dc=example,dc=com', 'uid=%u,ou=people,dc=example,dc=com' ]; - this.plugin.check_plain_passwd(this.connection, 'invalid', 'invalid', function (result) { - assert.equal(false, result); - done(); - }) - }) -}) + this.connection.server.notes.ldappool.config.authn.dn = [ + 'uid=%u,ou=users,dc=example,dc=com', + 'uid=%u,ou=people,dc=example,dc=com', + ]; + this.plugin.check_plain_passwd( + this.connection, + 'invalid', + 'invalid', + function (result) { + assert.equal(false, result); + done(); + }, + ); + }); +}); diff --git a/test/authz.js b/test/authz.js index b26a495..ecbd296 100644 --- a/test/authz.js +++ b/test/authz.js @@ -1,160 +1,214 @@ 'use strict'; -const assert = require('assert') +const assert = require('assert'); -const fixtures = require('haraka-test-fixtures'); -const Address = require('address-rfc2821').Address; +const fixtures = require('haraka-test-fixtures'); +const Address = require('address-rfc2821').Address; const constants = require('haraka-constants'); -const ldappool = require('../pool'); +const ldappool = require('../pool'); -function _set_up (done) { +function _set_up(done) { this.user = { - uid : 'user1', - dn : 'uid=user1,ou=users,dc=example,dc=com', - password : 'ykaHsOzEZD', - mail : 'user1@example.com' + uid: 'user1', + dn: 'uid=user1,ou=users,dc=example,dc=com', + password: 'ykaHsOzEZD', + mail: 'user1@example.com', }; this.plugin = require('../authz'); this.connection = fixtures.connection.createConnection(); this.connection.server = { notes: { - ldappool : new ldappool.LdapPool({ - main : { - server : [ 'ldap://localhost:3389' ], - binddn : this.user.dn, - bindpw : this.user.password, - basedn : 'dc=example,dc=com' - } - }) - } + ldappool: new ldappool.LdapPool({ + main: { + server: ['ldap://localhost:3389'], + binddn: this.user.dn, + bindpw: this.user.password, + basedn: 'dc=example,dc=com', + }, + }), + }, }; this.connection.server.notes.ldappool.config.authz = { - searchfilter : '(&(objectclass=*)(uid=%u)(mailLocalAddress=%a))' + searchfilter: '(&(objectclass=*)(uid=%u)(mailLocalAddress=%a))', }; done(); } describe('_verify_address', function () { - - beforeEach(_set_up) + beforeEach(_set_up); it('1 entry', function (done) { const user = this.user; - this.plugin._verify_address(user.uid, user.mail, function (err, result) { - assert.equal(true, result); - done(); - }, this.connection); - }) + this.plugin._verify_address( + user.uid, + user.mail, + function (err, result) { + assert.equal(true, result); + done(); + }, + this.connection, + ); + }); it('0 entries', function (done) { - this.plugin._verify_address('alien', 'unknown', function (err, result) { - assert.ifError(err) - assert.equal(false, result) - done() - }, this.connection); - }) + this.plugin._verify_address( + 'alien', + 'unknown', + function (err, result) { + assert.ifError(err); + assert.equal(false, result); + done(); + }, + this.connection, + ); + }); it('2 entries', function (done) { const pool = this.connection.server.notes.ldappool; - pool.config.authz.searchfilter = '(&(objectclass=*)(|(uid=%u)(uid=user2)))'; - this.plugin._verify_address('user1', 'who cares', function (err, result) { - assert.ifError(err) - assert.equal(true, result); - done(); - }, this.connection); - }) + pool.config.authz.searchfilter = '(&(objectclass=*)(|(uid=%u)(uid=user2)))'; + this.plugin._verify_address( + 'user1', + 'who cares', + function (err, result) { + assert.ifError(err); + assert.equal(true, result); + done(); + }, + this.connection, + ); + }); it('invalid search filter', function (done) { const user = this.user; const pool = this.connection.server.notes.ldappool; - pool.config.authz.searchfilter = '(&(objectclass=*)(|(uid=%u'; - this.plugin._verify_address(user.uid, user.mail, function (err, result) { - assert.equal('unbalanced parens', err.message); - assert.equal(false, result); - done(); - }, this.connection); - }) + pool.config.authz.searchfilter = '(&(objectclass=*)(|(uid=%u'; + this.plugin._verify_address( + user.uid, + user.mail, + function (err, result) { + assert.equal('unbalanced parens', err.message); + assert.equal(false, result); + done(); + }, + this.connection, + ); + }); it('no pool', function (done) { this.connection.server.notes.ldappool = undefined; const user = this.user; - this.plugin._verify_address(user.uid, user.mail, function (err, userdn) { - assert.equal('LDAP Pool not found!', err); - assert.equal(false, userdn); - done(); - }, this.connection); - }) -}) + this.plugin._verify_address( + user.uid, + user.mail, + function (err, userdn) { + assert.equal('LDAP Pool not found!', err); + assert.equal(false, userdn); + done(); + }, + this.connection, + ); + }); +}); describe('_get_search_conf', function () { - - beforeEach(_set_up) + beforeEach(_set_up); it('get defaults', function (done) { const pool = this.connection.server.notes.ldappool; - const opts = this.plugin._get_search_conf('testUid', 'testMail', this.connection); + const opts = this.plugin._get_search_conf( + 'testUid', + 'testMail', + this.connection, + ); assert.equal(opts.basedn, pool.config.basedn); - assert.equal(opts.filter, '(&(objectclass=*)(uid=testUid)(mailLocalAddress=testMail))'); + assert.equal( + opts.filter, + '(&(objectclass=*)(uid=testUid)(mailLocalAddress=testMail))', + ); assert.equal(opts.scope, pool.config.scope); assert.equal(opts.attributes.toString(), ['dn'].toString()); done(); - }) + }); it('get userdef', function (done) { const pool = this.connection.server.notes.ldappool; pool.config.authz.basedn = 'hop around as you like'; - pool.config.authz.searchfilter = '(&(objectclass=posixAccount)(uid=%u)(mail=%a))'; + pool.config.authz.searchfilter = + '(&(objectclass=posixAccount)(uid=%u)(mail=%a))'; pool.config.authz.scope = 'one two three'; - const opts = this.plugin._get_search_conf('testUid', 'testMail', this.connection); + const opts = this.plugin._get_search_conf( + 'testUid', + 'testMail', + this.connection, + ); assert.equal(opts.basedn, 'hop around as you like'); - assert.equal(opts.filter, '(&(objectclass=posixAccount)(uid=testUid)(mail=testMail))'); + assert.equal( + opts.filter, + '(&(objectclass=posixAccount)(uid=testUid)(mail=testMail))', + ); assert.equal(opts.scope, 'one two three'); assert.equal(opts.attributes.toString(), ['dn'].toString()); done(); - }) -}) + }); +}); describe('check_authz', function () { - - beforeEach(_set_up) + beforeEach(_set_up); it('ok', function (done) { - this.connection.notes = { auth_user : 'user1' }; - this.plugin.check_authz(function (err) { - assert.equal(undefined, err); - done(); - }, this.connection, [new Address('')]); - }) + this.connection.notes = { auth_user: 'user1' }; + this.plugin.check_authz( + function (err) { + assert.equal(undefined, err); + done(); + }, + this.connection, + [new Address('')], + ); + }); it('deny if not authorized', function (done) { const plugin = this.plugin; - this.connection.notes = { auth_user : 'user1' }; - plugin.check_authz(function (err) { - assert.equal(constants.deny, err); - done(); - }, this.connection, [new Address('user2@example.com')]); - }) + this.connection.notes = { auth_user: 'user1' }; + plugin.check_authz( + function (err) { + assert.equal(constants.deny, err); + done(); + }, + this.connection, + [new Address('user2@example.com')], + ); + }); it('denysoft on error', function (done) { - this.connection.server.notes.ldappool.config.authz.searchfilter = '(&(objectclass=*)(|(uid=%u'; - this.connection.notes = { auth_user : 'user1' }; - this.plugin.check_authz(function (err) { - assert.equal(constants.denysoft, err); - done(); - }, this.connection, [new Address('user1@example.com')]); - }) + this.connection.server.notes.ldappool.config.authz.searchfilter = + '(&(objectclass=*)(|(uid=%u'; + this.connection.notes = { auth_user: 'user1' }; + this.plugin.check_authz( + function (err) { + assert.equal(constants.denysoft, err); + done(); + }, + this.connection, + [new Address('user1@example.com')], + ); + }); it('ignore invalid params: missing auth_user', function (done) { - this.plugin.check_authz(function (err) { - assert.ifError(err); - done(); - }, this.connection, [new Address('user1@example.com')]); - }) + this.plugin.check_authz( + function (err) { + assert.ifError(err); + done(); + }, + this.connection, + [new Address('user1@example.com')], + ); + }); it('ignore invalid params: missing address', function (done) { this.plugin.check_authz(function (err) { assert.ifError(err); done(); }, this.connection); - }) -}) + }); +}); diff --git a/test/index.js b/test/index.js index b22d785..aa84e28 100644 --- a/test/index.js +++ b/test/index.js @@ -1,112 +1,123 @@ 'use strict'; -const assert = require('assert') +const assert = require('assert'); -const fixtures = require('haraka-test-fixtures'); -const Address = require('address-rfc2821').Address; -const btoa = require('btoa'); -const constants = require('haraka-constants'); -const pool = require('../pool'); +const fixtures = require('haraka-test-fixtures'); +const Address = require('address-rfc2821').Address; +const btoa = require('btoa'); +const constants = require('haraka-constants'); +const pool = require('../pool'); -function _set_up () { +function _set_up() { this.user = { - uid : 'user1', - dn : 'uid=user1,ou=users,dc=example,dc=com', - password : 'ykaHsOzEZD', - mail : 'user1@example.com' + uid: 'user1', + dn: 'uid=user1,ou=users,dc=example,dc=com', + password: 'ykaHsOzEZD', + mail: 'user1@example.com', }; this.plugin = new fixtures.plugin('ldap'); - this.server = { notes: { } }; + this.server = { notes: {} }; this.cfg = { - main : { - binddn : this.user.dn, - bindpw : this.user.password, - basedn : 'dc=example,dc=com' - } + main: { + binddn: this.user.dn, + bindpw: this.user.password, + basedn: 'dc=example,dc=com', + }, }; this.connection = fixtures.connection.createConnection(); this.connection.server = { notes: { - ldappool : new pool.LdapPool({ - main : { - server : [ 'ldap://localhost:3389' ], - binddn : this.user.dn, - bindpw : this.user.password, - basedn : 'dc=example,dc=com' - } - }) - } + ldappool: new pool.LdapPool({ + main: { + server: ['ldap://localhost:3389'], + binddn: this.user.dn, + bindpw: this.user.password, + basedn: 'dc=example,dc=com', + }, + }), + }, }; } describe('handle_authn', function () { - - beforeEach(_set_up) + beforeEach(_set_up); it('ok with test user and PLAIN', function (done) { const connection = this.connection; connection.server.notes.ldappool.config.authn = {}; - connection.notes.allowed_auth_methods = ['PLAIN','LOGIN']; - connection.notes.authenticating=true; - connection.notes.auth_method='PLAIN'; + connection.notes.allowed_auth_methods = ['PLAIN', 'LOGIN']; + connection.notes.authenticating = true; + connection.notes.auth_method = 'PLAIN'; this.plugin.auth_plain = function (result) { assert.ok(true); done(); }; - const params = [ btoa(`discard\0${this.user.uid}\0${this.user.password}`) ]; + const params = [btoa(`discard\0${this.user.uid}\0${this.user.password}`)]; this.plugin.handle_authn(function () {}, connection, params); - }) + }); it('ok with test user and LOGIN', function (done) { const plugin = this.plugin; const connection = this.connection; connection.server.notes.ldappool.config.authn = {}; - connection.notes.allowed_auth_methods = ['PLAIN','LOGIN']; - connection.notes.authenticating=true; - connection.notes.auth_method='LOGIN'; + connection.notes.allowed_auth_methods = ['PLAIN', 'LOGIN']; + connection.notes.authenticating = true; + connection.notes.auth_method = 'LOGIN'; plugin.auth_login = function () { assert.ok(true); done(); }; - const params = [ btoa(`discard\0${this.user.uid}\0${this.user.password}`) ]; + const params = [btoa(`discard\0${this.user.uid}\0${this.user.password}`)]; plugin.handle_authn(function () {}, connection, params); - }) + }); it('ignore without connection.notes.authenticating', function (done) { const plugin = this.plugin; const connection = this.connection; connection.server.notes.ldappool.config.authn = {}; - plugin.handle_authn(function () { - assert.ok(true); - done(); - }, connection, [ '' ]); - }) + plugin.handle_authn( + function () { + assert.ok(true); + done(); + }, + connection, + [''], + ); + }); it('ignore with unknown AUTH', function (done) { const plugin = this.plugin; const connection = this.connection; connection.server.notes.ldappool.config.authn = {}; - connection.notes.allowed_auth_methods = ['PLAIN','LOGIN']; - connection.notes.authenticating=true; - connection.notes.auth_method='OPENSESAME'; - plugin.handle_authn(function () { - assert.ok(true); - done(); - }, connection, [ '' ]); - }) + connection.notes.allowed_auth_methods = ['PLAIN', 'LOGIN']; + connection.notes.authenticating = true; + connection.notes.auth_method = 'OPENSESAME'; + plugin.handle_authn( + function () { + assert.ok(true); + done(); + }, + connection, + [''], + ); + }); it('next if ldappool.config.authn is not set', function (done) { const plugin = this.plugin; const connection = this.connection; - plugin.handle_authn(function () { - assert.ok(true); - done(); - }, connection, [ '' ]); - }) -}) + plugin.handle_authn( + function () { + assert.ok(true); + done(); + }, + connection, + [''], + ); + }); +}); describe('hook_capabilities', function () { - beforeEach(_set_up) + beforeEach(_set_up); it('no tls no auth', function (done) { const cb = function (rc, msg) { @@ -116,7 +127,7 @@ describe('hook_capabilities', function () { this.connection.using_tls = false; this.connection.capabilities = []; this.plugin.hook_capabilities(cb, this.connection); - }) + }); it('tls ante portas, ready for auth login', function (done) { const cb = function (rc, msg) { @@ -129,111 +140,153 @@ describe('hook_capabilities', function () { this.connection.using_tls = true; this.connection.capabilities = []; this.plugin.hook_capabilities(cb, this.connection); - }) -}) + }); +}); describe('check_plain_passwd', function () { - - beforeEach(_set_up) + beforeEach(_set_up); it('basic functionality: valid login ok', function (done) { this.plugin._init_ldappool(() => { - this.connection.server.notes.ldappool.config.authn = { }; - this.plugin.check_plain_passwd(this.connection, this.user.uid, this.user.password, (result) => { - assert.equal(true, result); - done(); - }) + this.connection.server.notes.ldappool.config.authn = {}; + this.plugin.check_plain_passwd( + this.connection, + this.user.uid, + this.user.password, + (result) => { + assert.equal(true, result); + done(); + }, + ); }, this.server); - }) + }); it('basic functionality: invalid login fails', function (done) { this.plugin._init_ldappool(() => { - this.connection.server.notes.ldappool.config.authn = { }; - this.plugin.check_plain_passwd(this.connection, this.user.uid, 'invalid', (result2) => { - assert.equal(false, result2); - done(); - }) + this.connection.server.notes.ldappool.config.authn = {}; + this.plugin.check_plain_passwd( + this.connection, + this.user.uid, + 'invalid', + (result2) => { + assert.equal(false, result2); + done(); + }, + ); }, this.server); - }) -}) + }); +}); describe('aliases', function () { - - beforeEach(_set_up) + beforeEach(_set_up); it('basic functionality: resolve forwarding user', function (done) { const connection = this.connection; - connection.transaction = { rcpt_to : [ 'forwarder@example.com' ] }; - connection.server.notes.ldappool.config.aliases = { }; - connection.server.notes.ldappool.config.aliases.searchfilter = '(&(objectclass=*)(mailLocalAddress=%a))'; - connection.server.notes.ldappool.config.aliases.attribute = 'mailRoutingAddress'; - this.plugin.aliases(function (result) { - assert.equal(undefined, result); - assert.equal('', connection.transaction.rcpt_to.toString()); - done(); - }, connection, [ { address : () => { - return 'forwarder@example.com'; - }}]); - }) + connection.transaction = { rcpt_to: ['forwarder@example.com'] }; + connection.server.notes.ldappool.config.aliases = {}; + connection.server.notes.ldappool.config.aliases.searchfilter = + '(&(objectclass=*)(mailLocalAddress=%a))'; + connection.server.notes.ldappool.config.aliases.attribute = + 'mailRoutingAddress'; + this.plugin.aliases( + function (result) { + assert.equal(undefined, result); + assert.equal( + '', + connection.transaction.rcpt_to.toString(), + ); + done(); + }, + connection, + [ + { + address: () => { + return 'forwarder@example.com'; + }, + }, + ], + ); + }); it('next if ldappool.config.aliases is not set', function (done) { - this.plugin.aliases(function () { - assert.ok(true); - done(); - }, this.connection, [ ]); - }) -}) + this.plugin.aliases( + function () { + assert.ok(true); + done(); + }, + this.connection, + [], + ); + }); +}); describe('check_rcpt', function () { - - beforeEach(_set_up) + beforeEach(_set_up); it('basic functionality: lookup recipient', function (done) { this.connection.server.notes.ldappool.config.rcpt_to = { - searchfilter : '(&(objectclass=*)(mailLocalAddress=%a))' + searchfilter: '(&(objectclass=*)(mailLocalAddress=%a))', }; - this.plugin.check_rcpt(function (err) { - assert.equal(constants.ok, err); - done(); - }, this.connection, [{ - address : () => { return 'user1@example.com'; } - }]); - }) + this.plugin.check_rcpt( + function (err) { + assert.equal(constants.ok, err); + done(); + }, + this.connection, + [ + { + address: () => { + return 'user1@example.com'; + }, + }, + ], + ); + }); it('next if ldappool.config.rcpt_to is not set', function (done) { - this.plugin.check_rcpt(function () { - assert.ok(true); - done(); - }, this.connection, [ ]); - }) -}) + this.plugin.check_rcpt( + function () { + assert.ok(true); + done(); + }, + this.connection, + [], + ); + }); +}); describe('check_authz', function () { - - beforeEach(_set_up) + beforeEach(_set_up); it('basic functionality: matching address', function (done) { this.connection.server.notes.ldappool.config.authz = { - searchfilter : '(&(objectclass=*)(uid=%u)(mailLocalAddress=%a))' + searchfilter: '(&(objectclass=*)(uid=%u)(mailLocalAddress=%a))', }; - this.connection.notes = { auth_user : 'user1' }; - this.plugin.check_authz(function (err) { - assert.ifError(err); - done(); - }, this.connection, [new Address('')]); - }) + this.connection.notes = { auth_user: 'user1' }; + this.plugin.check_authz( + function (err) { + assert.ifError(err); + done(); + }, + this.connection, + [new Address('')], + ); + }); it('next if ldappool.config.authz is not set', function (done) { - this.plugin.check_authz(function () { - assert.ok(true); - done(); - }, this.connection, [ ]); - }) -}) + this.plugin.check_authz( + function () { + assert.ok(true); + done(); + }, + this.connection, + [], + ); + }); +}); describe('register', function () { - - beforeEach(_set_up) + beforeEach(_set_up); it('register sets master and child hooks to register pool', function (done) { assert.equal(false, this.plugin.register_hook.called); @@ -249,23 +302,25 @@ describe('register', function () { assert.equal('mail', this.plugin.register_hook.args[4][0]); assert.equal('check_authz', this.plugin.register_hook.args[4][1]); done(); - }) -}) + }); +}); describe('_load_ldap_ini', function () { - - beforeEach(_set_up) + beforeEach(_set_up); it('check if values get loaded and set', function (done) { this.plugin._init_ldappool(() => { this.plugin._load_ldap_ini(); - assert.equal('uid=user1,ou=users,dc=example,dc=com', this.server.notes.ldappool.config.binddn); + assert.equal( + 'uid=user1,ou=users,dc=example,dc=com', + this.server.notes.ldappool.config.binddn, + ); assert.equal('ykaHsOzEZD', this.server.notes.ldappool.config.bindpw); assert.equal('example.com', this.server.notes.ldappool.config.basedn); assert.equal('base', this.server.notes.ldappool.config.scope); }, this.server); done(); - }) + }); it('set _tmp_pool_config if pool is not available', function (done) { const plugin = this.plugin; @@ -277,12 +332,11 @@ describe('_load_ldap_ini', function () { assert.equal('example.com', conf.basedn); assert.equal('base', conf.scope); done(); - }) -}) + }); +}); describe('_init_ldappool', function () { - - beforeEach(_set_up) + beforeEach(_set_up); it('check if this.server.notes.ldappool is set correctly', function (done) { this.plugin._init_ldappool(() => { @@ -290,7 +344,7 @@ describe('_init_ldappool', function () { assert.equal(true, this.plugin._pool instanceof pool.LdapPool); done(); }, this.server); - }) + }); it('test proper _tmp_pool_config handling', function (done) { this.plugin._load_ldap_ini(); @@ -301,11 +355,11 @@ describe('_init_ldappool', function () { assert.equal('example.com', conf.basedn); done(); }, this.server); - }) -}) + }); +}); describe.skip('shutdown', function () { - beforeEach(_set_up) + beforeEach(_set_up); it('make sure ldappool gets closed', function (done) { this.plugin._init_ldappool(() => { @@ -313,8 +367,8 @@ describe.skip('shutdown', function () { this.plugin.shutdown(() => { assert.equal(true, client.unbound); done(); - }) - }) + }); + }); }, this.server); - }) -}) + }); +}); diff --git a/test/pool.js b/test/pool.js index 4cbce0c..d8169cc 100644 --- a/test/pool.js +++ b/test/pool.js @@ -1,30 +1,30 @@ 'use strict'; -const assert = require('assert') +const assert = require('assert'); const ldappool = require('../pool'); const testUser = { - uid : 'user1', - dn : 'uid=user1,ou=users,dc=example,dc=com', - password : 'ykaHsOzEZD', - mail : 'user1@example.com' + uid: 'user1', + dn: 'uid=user1,ou=users,dc=example,dc=com', + password: 'ykaHsOzEZD', + mail: 'user1@example.com', }; -Object.freeze(testUser) +Object.freeze(testUser); const testCfg = { - main : { - server : [ 'ldap://localhost:3389', 'ldaps://localhost:3636' ], - binddn : testUser.dn, - bindpw : testUser.password, - basedn : 'dc=example,dc=com' - } + main: { + server: ['ldap://localhost:3389', 'ldaps://localhost:3636'], + binddn: testUser.dn, + bindpw: testUser.password, + basedn: 'dc=example,dc=com', + }, }; -Object.freeze(testCfg) +Object.freeze(testCfg); -function _set_up () { +function _set_up() { this.user = JSON.parse(JSON.stringify(testUser)); - this.cfg = JSON.parse(JSON.stringify(testCfg)) + this.cfg = JSON.parse(JSON.stringify(testCfg)); } describe('_set_config', () => { @@ -33,8 +33,14 @@ describe('_set_config', () => { it('defaults', function (done) { const pool = new ldappool.LdapPool(this.cfg); const config = pool._set_config(); - assert.equal(pool._set_config().toString(), pool._set_config({}).toString()); - assert.equal(['ldap://localhost:389'].toString(), config.servers.toString()); + assert.equal( + pool._set_config().toString(), + pool._set_config({}).toString(), + ); + assert.equal( + ['ldap://localhost:389'].toString(), + config.servers.toString(), + ); assert.equal(undefined, config.timeout); assert.equal(false, config.tls_enabled); assert.equal(undefined, config.tls_rejectUnauthorized); @@ -42,21 +48,23 @@ describe('_set_config', () => { assert.equal(undefined, config.binddn); assert.equal(undefined, config.bindpw); assert.equal(undefined, config.basedn); - done() - }) + done(); + }); it('userdef', function () { const pool = new ldappool.LdapPool(this.cfg); - const cfg = { main : { - server : 'testserver', - timeout : 10000, - tls_enabled : true, - tls_rejectUnauthorized : true, - scope : 'one', - binddn : 'binddn-test', - bindpw : 'bindpw-test', - basedn : 'basedn-test' - }} + const cfg = { + main: { + server: 'testserver', + timeout: 10000, + tls_enabled: true, + tls_rejectUnauthorized: true, + scope: 'one', + binddn: 'binddn-test', + bindpw: 'bindpw-test', + basedn: 'basedn-test', + }, + }; const config = pool._set_config(cfg); assert.equal('testserver', config.servers); assert.equal(10000, config.timeout); @@ -66,8 +74,8 @@ describe('_set_config', () => { assert.equal('binddn-test', config.binddn); assert.equal('bindpw-test', config.bindpw); assert.equal('basedn-test', config.basedn); - }) -}) + }); +}); describe('_get_ldapjs_config', function () { beforeEach(_set_up); @@ -79,11 +87,11 @@ describe('_get_ldapjs_config', function () { assert.equal(undefined, config.timeout); assert.equal(undefined, config.tlsOptions); done(); - }) + }); it('userdef', function (done) { - const cfg = Object.assign({}, this.cfg) - cfg.main.server = [ 'ldap://localhost:3389' ]; + const cfg = Object.assign({}, this.cfg); + cfg.main.server = ['ldap://localhost:3389']; cfg.main.timeout = 42; cfg.main.tls_rejectUnauthorized = true; cfg.main.ldap_pool_size = 20; @@ -93,11 +101,10 @@ describe('_get_ldapjs_config', function () { assert.equal(42, config.timeout); assert.equal(true, config.tlsOptions.rejectUnauthorized); done(); - }) -}) + }); +}); describe('_create_client', function () { - beforeEach(_set_up); it('get valid and connected client', function (done) { @@ -107,14 +114,14 @@ describe('_create_client', function () { assert.ifError(err); assert.equal(undefined, client._starttls); client.bind(user.dn, user.password, function (err2) { - assert.ifError(err2) + assert.ifError(err2); client.unbind((err3) => { - if (err3) console.error(err3) - done() - }) + if (err3) console.error(err3); + done(); + }); }); }); - }) + }); it('client with tls', function (done) { this.cfg.main.tls_enabled = true; @@ -124,15 +131,14 @@ describe('_create_client', function () { assert.ifError(err); assert.ok(client._starttls.success); client.unbind((err2) => { - if (err2) console.error(err2) + if (err2) console.error(err2); done(); - }) + }); }); - }) -}) + }); +}); describe('close', function () { - beforeEach(_set_up); it('test if connections are closed after call', function (done) { @@ -142,16 +148,15 @@ describe('close', function () { assert.equal(true, client.connected); assert.equal(undefined, client.unbound); pool.close((err2) => { - assert.ifError(err2) + assert.ifError(err2); assert.equal(true, client.unbound); done(); - }) - }) - }) -}) + }); + }); + }); +}); describe('_bind_default', function () { - beforeEach(_set_up); it('bind with given binddn / bindpw', function (done) { @@ -160,7 +165,7 @@ describe('_bind_default', function () { assert.equal(true, client.connected); done(); }); - }) + }); it('bind with no binddn / bindpw', function (done) { this.cfg.main.binddn = undefined; @@ -170,8 +175,8 @@ describe('_bind_default', function () { pool._bind_default((err, client) => { assert.equal(false, client.connected); done(); - }) - }) + }); + }); it('bind with invalid binddn / bindpw', function (done) { this.cfg.main.binddn = 'invalid'; @@ -180,12 +185,12 @@ describe('_bind_default', function () { pool._bind_default((err, client) => { assert.equal('InvalidDnSyntaxError', err.name); client.unbind((err2) => { - assert.ifError(err2) + assert.ifError(err2); done(); - }) + }); }); - }) -}) + }); +}); describe('get', () => { beforeEach(_set_up); @@ -206,8 +211,8 @@ describe('get', () => { assert.equal(2, pool.pool.servers.length); assert.equal('ldap://localhost:3389', client3?.urls[0].href); done(); - }) - }) - }) - }) -}) + }); + }); + }); + }); +}); diff --git a/test/rcpt_to.js b/test/rcpt_to.js index 0fcb746..1319f12 100644 --- a/test/rcpt_to.js +++ b/test/rcpt_to.js @@ -1,79 +1,94 @@ 'use strict'; -const assert = require('assert') +const assert = require('assert'); -const fixtures = require('haraka-test-fixtures'); -const constants = require('haraka-constants'); -const ldappool = require('../pool'); +const fixtures = require('haraka-test-fixtures'); +const constants = require('haraka-constants'); +const ldappool = require('../pool'); -function _set_up (done) { +function _set_up(done) { this.user = { - uid : 'user1', - dn : 'uid=user1,ou=users,dc=example,dc=com', - password : 'ykaHsOzEZD', - mail : 'user1@example.com' + uid: 'user1', + dn: 'uid=user1,ou=users,dc=example,dc=com', + password: 'ykaHsOzEZD', + mail: 'user1@example.com', }; this.plugin = require('../rcpt_to'); this.connection = fixtures.connection.createConnection(); - this.connection.transaction = { }; + this.connection.transaction = {}; this.connection.server = { - notes : { - ldappool : new ldappool.LdapPool({ - main : { - server : [ 'ldap://localhost:3389' ], - binddn : this.user.dn, - bindpw : this.user.password, - basedn : 'dc=example,dc=com' - } - }) - } + notes: { + ldappool: new ldappool.LdapPool({ + main: { + server: ['ldap://localhost:3389'], + binddn: this.user.dn, + bindpw: this.user.password, + basedn: 'dc=example,dc=com', + }, + }), + }, }; this.connection.server.notes.ldappool.config.rcpt_to = { - searchfilter : '(&(objectclass=*)(mailLocalAddress=%a))' + searchfilter: '(&(objectclass=*)(mailLocalAddress=%a))', }; done(); } describe('_verify_existence', function () { - - beforeEach(_set_up) + beforeEach(_set_up); it('default user', function (done) { - this.plugin._verify_existence(this.user.mail, function (err, result) { - assert.equal(true, result); - done(); - }, this.connection); - }) + this.plugin._verify_existence( + this.user.mail, + function (err, result) { + assert.equal(true, result); + done(); + }, + this.connection, + ); + }); it('invalid address', function (done) { - this.plugin._verify_existence('unknown', function (err, result) { - assert.equal(false, result); - done(); - }, this.connection); - }) + this.plugin._verify_existence( + 'unknown', + function (err, result) { + assert.equal(false, result); + done(); + }, + this.connection, + ); + }); it('invalid search filter', function (done) { - this.connection.server.notes.ldappool.config.rcpt_to.searchfilter = '(&(objectclass=*)(|(mail=%a'; - this.plugin._verify_existence(this.user.mail, function (err, result) { - assert.equal('unbalanced parens', err.message); - assert.equal(false, result); - done(); - }, this.connection); - }) + this.connection.server.notes.ldappool.config.rcpt_to.searchfilter = + '(&(objectclass=*)(|(mail=%a'; + this.plugin._verify_existence( + this.user.mail, + function (err, result) { + assert.equal('unbalanced parens', err.message); + assert.equal(false, result); + done(); + }, + this.connection, + ); + }); it('no pool', function (done) { this.connection.server.notes.ldappool = undefined; - this.plugin._verify_existence(this.user.mail, function (err, userdn) { - assert.equal('LDAP Pool not found!', err); - assert.equal(false, userdn); - done(); - }, this.connection); - }) -}) + this.plugin._verify_existence( + this.user.mail, + function (err, userdn) { + assert.equal('LDAP Pool not found!', err); + assert.equal(false, userdn); + done(); + }, + this.connection, + ); + }); +}); describe('_get_search_conf', function () { - - beforeEach(_set_up) + beforeEach(_set_up); it('get defaults', function (done) { const opts = this.plugin._get_search_conf('testMail', this.connection); @@ -83,57 +98,88 @@ describe('_get_search_conf', function () { assert.equal(opts.scope, pool.config.scope); assert.equal(opts.attributes.toString(), ['dn'].toString()); done(); - }) + }); it('get userdef', function (done) { - this.connection.server.notes.ldappool.config.rcpt_to.basedn = 'hop around as you like'; - this.connection.server.notes.ldappool.config.rcpt_to.searchfilter = '(&(objectclass=posixAccount)(mail=%a))'; - this.connection.server.notes.ldappool.config.rcpt_to.scope = 'one two three'; + this.connection.server.notes.ldappool.config.rcpt_to.basedn = + 'hop around as you like'; + this.connection.server.notes.ldappool.config.rcpt_to.searchfilter = + '(&(objectclass=posixAccount)(mail=%a))'; + this.connection.server.notes.ldappool.config.rcpt_to.scope = + 'one two three'; const opts = this.plugin._get_search_conf('testMail', this.connection); assert.equal(opts.basedn, 'hop around as you like'); assert.equal(opts.filter, '(&(objectclass=posixAccount)(mail=testMail))'); assert.equal(opts.scope, 'one two three'); assert.equal(opts.attributes.toString(), ['dn'].toString()); done(); - }) -}) + }); +}); describe('check_rcpt', function () { - - beforeEach(_set_up) + beforeEach(_set_up); it('ok', function (done) { - this.plugin.check_rcpt(function (err) { - assert.equal(constants.ok, err); - done(); - }, this.connection, [{ - address : () => { return 'user1@example.com'; } - }]); - }) + this.plugin.check_rcpt( + function (err) { + assert.equal(constants.ok, err); + done(); + }, + this.connection, + [ + { + address: () => { + return 'user1@example.com'; + }, + }, + ], + ); + }); it('denysoft on error', function (done) { - this.connection.server.notes.ldappool.config.rcpt_to.searchfilter = '(&(objectclass=*)(|(mail=%a'; - this.plugin.check_rcpt(function (err) { - assert.equal(constants.denysoft, err); - done(); - }, this.connection, [{ - address : () => { return 'user1@example.com'; } - }]); - }) + this.connection.server.notes.ldappool.config.rcpt_to.searchfilter = + '(&(objectclass=*)(|(mail=%a'; + this.plugin.check_rcpt( + function (err) { + assert.equal(constants.denysoft, err); + done(); + }, + this.connection, + [ + { + address: () => { + return 'user1@example.com'; + }, + }, + ], + ); + }); it('ignore if missing params[0]', function (done) { - this.plugin.check_rcpt(function (err) { - assert.equal(undefined, err); - done(); - }, this.connection, []); - }) + this.plugin.check_rcpt( + function (err) { + assert.equal(undefined, err); + done(); + }, + this.connection, + [], + ); + }); it('deny on invalid address', function (done) { - this.plugin.check_rcpt(function (err) { - assert.equal(constants.deny, err); - done(); - }, this.connection, [{ - address : () => { return 'unknown@address'; } - }]); - }) -}) + this.plugin.check_rcpt( + function (err) { + assert.equal(constants.deny, err); + done(); + }, + this.connection, + [ + { + address: () => { + return 'unknown@address'; + }, + }, + ], + ); + }); +});