Workflow funktioniert nun wieder. Es gab Probleme nach Aenderungen.
Build and Publish Site / docker (push) Successful in 23s

ABER: Die Applikation funktioniert nur lokal. Die deployte Version geht noch nicht.
This commit is contained in:
2026-07-03 13:24:08 +02:00
parent 97a22cf704
commit b518ae8edb
1845 changed files with 292358 additions and 57 deletions
@@ -0,0 +1,12 @@
If you know how to fix the issue, make a pull request instead.
- [ ] I have a question that is inappropriate for [StackOverflow](https://stackoverflow.com/). (Please ask any appropriate questions, such as how to use the library, there).
- [ ] I believe this is an issue in this library and not in the underlying libraries [ldapjs](https://github.com/joyent/node-ldapjs/) or [ldapauth-fork](https://github.com/vesse/node-ldapauth-fork). (This library is a passport strategy and does not implement the LDAP communication)
**Note:** if the issue template is not used, the issue will be closed.
## Problem Description
## Steps to Reproduce
+67
View File
@@ -0,0 +1,67 @@
## Changes
* v3.0.0
* Update ldapauth-fork to v5 which upgrade ldapjs to v2
* v2.1.4
* Allow any version of @types/node
* v2.1.3
* [#86](https://github.com/vesse/passport-ldapauth/pull/86) Allow configuring missing credentials response status.
* v2.1.2
* [#80](https://github.com/vesse/passport-ldapauth/pull/80) Run error handler only once since the a new LdapAuth instance is created for every authenticate request.
* v2.1.1
* Bump deps
* v2.1.0
* [#77](https://github.com/vesse/passport-ldapauth/pull/77) Add `noSuchObject` error message
* v2.0.0
* `ldapauth-fork` major version update now uses Bunyan logger
* Added TypeScript type definitions
* v1.0.0
* `ldapauth-fork` is now an event emitter. Emitted errors will cause authentication error.
* [#38](https://github.com/vesse/passport-ldapauth/pull/38) Added option to handle erros as failures with `handleErrorsAsFailures`. Additionally a *synchronous* `failureErrorCallback` function that receives the error as argument can be provided.
* v0.6.0
* Added option `credentialsLookup` that can be used eg. to add Basic Auth header parsing support.
* v0.5.0
* Updated deps. ldapauth-fork update changes bind credentials handling to work better with falsy values needed in anonymous bind.
* v0.4.0
* Updated ldapauth-fork which updates ldapjs to 1.0.0
* v0.3.1
* [#35](https://github.com/vesse/passport-ldapauth/issues/35) - Show more specific error messages from Microsoft AD login errors if identified.
* v0.3.0
* [#10](https://github.com/vesse/passport-ldapauth/issues/10) - Add support for fetching groups. While this is really coming from [ldapauth-fork](https://github.com/vesse/node-ldapauth-fork), updated the minor version of this library as well to draw attention to new features.
* v0.2.6
* [#24](https://github.com/vesse/passport-ldapauth/pull/24) - Pass `req` to options function, enables request specific LDAP configuration.
* v0.2.5
* [#21](https://github.com/vesse/passport-ldapauth/issues/21) - Handle `constraintViolationError` as a login failure instead of an error.
* v0.2.4
* Inherit from [passport-strategy](https://github.com/jaredhanson/passport-strategy) like `passport-local` and others do.
* v0.2.3
* Documentation using the same keys as ldapjs (bindDn and bindCredentials)
* v0.2.2
* Allow configuring flash messages when calling `passport.authenticate()`
* Return HTTP 400 when username or password is missing
* v0.2.1
* Passport as peerDependency, prevents version incompatibility
* v0.2.0
* [#8](https://github.com/vesse/passport-ldapauth/issues/8) - Possibility to provide a callback function instead of options object to constructor (contributed by Linagora)
* Update Passport dependency to 0.2.0
* Get rid of `var self = this;`
* v0.1.2
* [#6](https://github.com/vesse/passport-ldapauth/issues/6) - Handle NoSuchObjectError as login failure.
* v0.1.1
* Documentation changes due to renaming git repository of `ldapauth-fork`
* v0.1.0
* Use [ldapauth-fork](https://github.com/vesse/node-ldapauth-fork) instead of
[ldapauth](https://github.com/trentm/node-ldapauth)
* ldapjs upgraded to 0.6.3
* New options including `tlsOptions`
* Refactored tests
* v0.0.6 (14 July 2013)
* Fixes [#1](https://github.com/vesse/passport-ldapauth/issues/1)
* Updated devDependencies
* v0.0.5 (16 April 2013)
* Create LDAP client on every request to prevent socket being closed due
to inactivity.
* v0.0.4 (14 April 2013)
* Fixed passport-ldapauth version range.
* v0.0.3 (14 April 2013)
* Initial release.
+21
View File
@@ -0,0 +1,21 @@
The MIT License
Copyright (c) 2013 Vesa Poikajärvi
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+201
View File
@@ -0,0 +1,201 @@
# passport-ldapauth
[![Build Status](https://travis-ci.org/vesse/passport-ldapauth.svg)](https://travis-ci.org/vesse/passport-ldapauth)
[![npm](https://img.shields.io/npm/dm/passport-ldapauth.svg)](http://npmjs.com/package/passport-ldapauth)
[![Sponsored by Wakeone](https://img.shields.io/badge/sponsored%20by-wakeone-389fc1.svg)](https://wakeone.co)
[Passport](http://passportjs.org/) authentication strategy against LDAP / AD server. This module is a Passport strategy wrapper for [ldapauth-fork](https://github.com/vesse/node-ldapauth-fork).
This module lets you authenticate using LDAP or AD in your Node.js applications. By plugging into Passport, LDAP authentication can be integrated into any framework that supports Connect-style middleware.
## Install
```
npm install passport-ldapauth
```
## Usage
### Configure strategy
```javascript
var LdapStrategy = require('passport-ldapauth');
passport.use(new LdapStrategy({
server: {
url: 'ldap://localhost:389',
...
}
}));
```
* `server`: LDAP settings. These are passed directly to [ldapauth-fork](https://github.com/vesse/node-ldapauth-fork). See its documentation for all available options.
* `url`: e.g. `ldap://localhost:389`
* `bindDN`: e.g. `cn='root'`
* `bindCredentials`: Password for bindDN
* `searchBase`: e.g. `o=users,o=example.com`
* `searchFilter`: LDAP search filter, e.g. `(uid={{username}})`. Use literal `{{username}}` to have the given username used in the search.
* `searchAttributes`: Optional array of attributes to fetch from LDAP server, e.g. `['displayName', 'mail']`. Defaults to `undefined`, i.e. fetch all attributes
* `tlsOptions`: Optional object with options accepted by Node.js [tls](http://nodejs.org/api/tls.html#tls_tls_connect_options_callback) module.
* `usernameField`: Field name where the username is found, defaults to _username_
* `passwordField`: Field name where the password is found, defaults to _password_
* `credentialsLookup`: Optional, synchronous function that provides the login credentials from `req`. See [below](#credentialslookup) for more.
* `missingCredentialsStatus`: Returned HTTP status code when credentials could not be found in the request. Defaults to _400_
* `handleErrorsAsFailures`: When `true`, unknown errors and ldapjs emitted errors are handled as authentication failures instead of errors (default: `false`).
* `failureErrorCallback`: Optional, synchronous function that is called with the received error when `handleErrorsAsFailures` is enabled.
* `passReqToCallback`: When `true`, `req` is the first argument to the verify callback (default: `false`):
passport.use(new LdapStrategy(..., function(req, user, done) {
...
done(null, user);
}
));
Note: you can pass a function instead of an object as `options`, see the [example below](#asynchronous-configuration-retrieval)
### Authenticate requests
Use `passport.authenticate()`, specifying the `'ldapauth'` strategy, to authenticate requests.
#### `authenticate()` options
In addition to [default authentication options](http://passportjs.org/guide/authenticate/) the following flash message options are available for `passport.authenticate()`:
* `badRequestMessage`: missing username/password (default: 'Missing credentials')
* `invalidCredentials`: `InvalidCredentialsError` and `/no such user/i` LDAP errors (default: 'Invalid username/password')
* `noSuchObject`: `NoSuchObjectError` LDAP errors (default: 'Bad search base')
* `userNotFound`: LDAP returns no error but also no user (default: 'Invalid username/password')
* `constraintViolation`: user account is locked (default: 'Exceeded password retry limit, account locked')
And for [Microsoft AD messages](http://www-01.ibm.com/support/docview.wss?uid=swg21290631), these flash message options can also be used (used instead of `invalidCredentials` if matching error code is found):
* `invalidLogonHours`: not being allowed to login at this current time (default: 'Not Permitted to login at this time')
* `invalidWorkstation`: not being allowed to login from this current location (default: 'Not permited to logon at this workstation')
* `passwordExpired`: expired password (default: 'Password expired')
* `accountDisabled`: disabled account (default: 'Account disabled')
* `accountExpired`: expired account (default: 'Account expired')
* `passwordMustChange`: password change (default: 'User must reset password')
* `accountLockedOut`: locked out account (default: 'User account locked')
## Express example
```javascript
var express = require('express'),
passport = require('passport'),
bodyParser = require('body-parser'),
LdapStrategy = require('passport-ldapauth');
var OPTS = {
server: {
url: 'ldap://localhost:389',
bindDN: 'cn=root',
bindCredentials: 'secret',
searchBase: 'ou=passport-ldapauth',
searchFilter: '(uid={{username}})'
}
};
var app = express();
passport.use(new LdapStrategy(OPTS));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));
app.use(passport.initialize());
app.post('/login', passport.authenticate('ldapauth', {session: false}), function(req, res) {
res.send({status: 'ok'});
});
app.listen(8080);
```
### Active Directory over SSL example
Simple example config for connecting over `ldaps://` to a server requiring some internal CA certificate (often the case in corporations using Windows AD).
```javascript
var fs = require('fs');
var opts = {
server: {
url: 'ldaps://ad.corporate.com:636',
bindDN: 'cn=non-person,ou=system,dc=corp,dc=corporate,dc=com',
bindCredentials: 'secret',
searchBase: 'dc=corp,dc=corporate,dc=com',
searchFilter: '(&(objectcategory=person)(objectclass=user)(|(samaccountname={{username}})(mail={{username}})))',
searchAttributes: ['displayName', 'mail'],
tlsOptions: {
ca: [
fs.readFileSync('/path/to/root_ca_cert.crt')
]
}
}
};
...
```
## `credentialsLookup`
A synchronous function that receives the `req` object and returns an objec with keys `username` and `password` (or `name` and `pass`) can be provided. Note, that when this is provided the default lookup is not performed. This can be used to eg. enable basic auth header support:
```javascript
var basicAuth = require('basic-auth');
var ldapOpts = {
server: { ... },
credentialsLookup: basicAuth
}
```
## Asynchronous configuration retrieval
Instead of providing a static configuration object, you can pass a function as `options` that will take care of fetching the configuration. It will be called with the `req` object and a callback function having the standard `(err, result)` signature. Notice that the provided function will be called on every authenticate request.
```javascript
var getLDAPConfiguration = function(req, callback) {
// Fetching things from database or whatever
process.nextTick(function() {
var opts = {
server: {
url: 'ldap://localhost:389',
bindDN: 'cn=root',
bindCredentials: 'secret',
searchBase: 'ou=passport-ldapauth',
searchFilter: '(uid={{username}})'
}
};
callback(null, opts);
});
};
var LdapStrategy = require('passport-ldapauth');
passport.use(new LdapStrategy(getLDAPConfiguration,
function(user, done) {
...
return done(null, user);
}
));
```
## `ldapsearch`
[ldapsearch](http://linux.die.net/man/1/ldapsearch) is a great command line tool for testing your config. The user search query performed in the Express example above when user logging in has uid `john` is the same as the following `ldapsearch` call:
```bash
ldapsearch \
-H ldap://localhost:389 \
-x \
-D cn=root \
-w secret \
-b ou=passport-ldapauth \
"(uid=john)"
```
If the query does not return expected user the configuration is likely incorrect.
## License
MIT
`passport-ldapauth` has been partially sponsored by [Wakeone Ltd](https://wakeone.co/).
@@ -0,0 +1,7 @@
var Strategy = require('./strategy');
// Export directly from package since there is nothing else to export
exports = module.exports = Strategy;
// Export constructor like before for compatibility
exports.Strategy = Strategy;
@@ -0,0 +1,144 @@
// Type definitions for passport-ldapauth 2.0
// Project: https://github.com/vesse/passport-ldapauth
// Definitions by: Vesa Poikajärvi <https://github.com/vesse>
// TypeScript Version: 2.1
/// <reference types="node"/>
import { IncomingMessage } from 'http';
import { Options as LdapAuthOptions } from 'ldapauth-fork';
import {
Strategy as PassportStrategy,
AuthenticateOptions as PassportAuthenticateOptions
} from 'passport';
declare namespace Strategy {
/**
* Return value type for credentialsLookup
*/
interface CredentialsLookupResult {
username?: string;
password?: string;
user?: string;
pass?: string;
}
/**
* Credentials lookup function (eg. basic-auth)
*/
type CredentialsLookup = (req: IncomingMessage) => CredentialsLookupResult;
/**
* Callback notified of an error when errors are handled as failures
*/
type FailureErrorCallback = (err: any) => void;
/**
* passport-ldapauth options
*/
interface Options {
/**
* ldapauth-fork connection options
*/
server: LdapAuthOptions;
/**
* Form field name for username (default: username)
*/
usernameField?: string;
/**
* Form field name for password (default: password)
*/
passwordField?: string;
/**
* If set to true, request is passed to verify callback
*/
passReqToCallback?: boolean;
/**
* Credentials lookup function to be used instead of default search from request
*/
credentialsLookup?: CredentialsLookup;
/**
* Set to true to handle errors as login failures
*/
handleErrorsAsFailures?: boolean;
/**
* Synchronous failure error callback for handling the failure if using handleErrorsAsFailures
*/
failureErrorCallback?: FailureErrorCallback;
}
/**
* Callback function returning the options if using OptionsFunction
*/
type OptionsFunctionCallback = (error: any, options: Options) => void;
/**
* Callback for getting options dunamically for every authenticate call
*/
type OptionsFunction = (req: IncomingMessage, callback: OptionsFunctionCallback) => void;
/**
* Flash message localizations for authenticate
*/
interface AuthenticateOptions extends PassportAuthenticateOptions {
badRequestMessage?: string;
invalidCredentials?: string;
userNotFound?: string;
constraintViolation?: string;
invalidLogonHours?: string;
invalidWorkstation?: string;
passwordExpired?: string;
accountDisabled?: string;
accountExpired?: string;
passwordMustChange?: string;
accountLockedOut?: string;
noSuchObject?: string;
}
/**
* Options that verify done callback can pass
*/
interface VerifyOptions {
message: string;
}
/**
* Callback executed in the verify function once done
*
* @param error Possible error, setting this will result in error from Passport
* @param user The user object, or false if there was no error but the authentication should be failed
* @param options
*/
type VerifyDoneCallback = (error: any, user?: any, options?: VerifyOptions) => void;
/**
* Verify callback when passReqToCallback = false
*/
type VerifyCallback = (user: any, callback: VerifyDoneCallback) => void;
/**
* Verify callback when passReqToCallback = true
*/
type VerifyCallbackWithRequest = (req: IncomingMessage, user: any, callback: VerifyDoneCallback) => void;
}
declare class Strategy implements PassportStrategy {
/**
* @param options Strategy options or function returning the options
* @param verify User provided verify callback for checking the LDAP result
*/
constructor(options: Strategy.Options | Strategy.OptionsFunction, verify?: Strategy.VerifyCallback | Strategy.VerifyCallbackWithRequest);
/**
* Name of the strategy
*/
name: string;
/**
* @param req
* @param options
*/
authenticate(req: IncomingMessage, options?: Strategy.AuthenticateOptions): void;
}
export = Strategy;
@@ -0,0 +1,364 @@
'use strict';
var passport = require('passport-strategy');
var LdapAuth = require('ldapauth-fork');
var util = require('util');
/**
* Passport wrapper for ldapauth
*/
/**
* Options callback callback (ie. the callback given if giving a callback
* for options instead of an object)
*
* @callback optionsCallbackCallback
* @param {(Error|undefined)} err - Possible error
* @param {Object} options - Options object
*/
/**
* Options callback
*
* @callback optionsCallback
* @param {Object} req - HTTP request
* @param {optionsCallbackCallback} callback - The callback returning the options
*/
/**
* Verify done callback
*
* @callback verifyDoneCallback
* @param {(Error|undefined)} err - Possible error
* @param {(Object|boolean)} user - The verified user or false if not allowed
* @param {Object} [info] info - Additional info message
*/
/**
* Found LDAP user verify callback
*
* @callback verifyCallback
* @param {Object} user - The user object from LDAP
* @param {verifyDoneCallback} callback - The verify callback
*/
/**
* Found LDAP user verify callback with request
*
* @callback verifyReqCallback
* @param {Object} req - The HTTP request
* @param {Object} user - The user object from LDAP
* @param {verifyDoneCallback} callback - The verify callback
*/
/**
* @typedef credentialsLookupResult
* @type {object}
* @property {string} username - Username to use
* @property {string} password - Password to use
*/
/**
* @typedef credentialsLookupResultAlt
* @type {object}
* @property {string} user - Username to use
* @property {string} pass - Password to use
*/
/**
* Credentials lookup function
*
* @callback credentialsLookup
* @param {Object} req - The HTTP request
* @return {(credentialsLookupResult|credentialsLookupResultAlt)} - Found credentials
*/
/**
* Synchronous function for doing something with an error if handling
* errors as failures
*
* @callback failureErrorCallback
* @param {Error} err - The error occurred
*/
/**
* Add default values to options
*
* @private
* @param {Object} options - Options object
* @returns {Object} The given options with defaults filled
*/
var setDefaults = function(options) {
options.usernameField || (options.usernameField = 'username');
options.passwordField || (options.passwordField = 'password');
return options;
};
/**
* Strategy constructor
* <br>
*
* The LDAP authentication strategy authenticates requests based on the
* credentials submitted through an HTML-based login form.
* <br>
*
* Applications may supply a `verify` callback which accepts `user` object
* and then calls the `done` callback supplying a `user`, which should be set
* to `false` if user is not allowed to authenticate. If an exception occured,
* `err` should be set.
* <br>
*
* Options can be also given as function that accepts a callback end calls it
* with error and options arguments. Notice that the callback is executed on
* every authenticate call.
* <br>
*
* Example:
* <pre>
* var LdapStrategy = require('passport-ldapauth').Strategy;
* passport.use(new LdapStrategy({
* server: {
* url: 'ldap://localhost:389',
* bindDN: 'cn=root',
* bindCredentials: 'secret',
* searchBase: 'ou=passport-ldapauth',
* searchFilter: '(uid={{username}})',
* reconnect: true
* }
* },
* function(user, done) {
* return cb(null, user);
* }
* ));
* </pre>
* @constructor
* @param {(Object|optionsCallback)} options - Configuration options or options returning function
* @param {Object} options.server - [ldapauth-fork options]{@link https://github.com/vesse/node-ldapauth-fork}
* @param {string} [options.usernameField=username] - Form field name for username
* @param {string} [options.passwordField=password] - Form field name for password
* @param {Number} [options.missingCredentialsStatus=400] - HTTP status code returned when credentials are missing from the request
* @param {boolean} [options.passReqToCallback=false] - If true, request is passed to verify callback
* @param {credentialsLookup} [options.credentialsLookup] - Credentials lookup function to use instead of default
* @param {boolean} [options.handleErrorAsFailures=false] - Set to true to handle errors as login failures
* @param {failureErrorCallback} [options.failureErrorCallback] - Function receives the occurred error when errors handled as failures
* @param {(verifyCallback|verifyReqCallback|undefined)} [verify] - User verify callback
*/
var Strategy = function(options, verify) {
// We now accept function as options as well so we cannot assume anymore
// that a call with a function parameter only would have skipped options
// and just provided a verify callback
if (!options) {
throw new Error('LDAP authentication strategy requires options');
}
this.options = null;
this.getOptions = null;
if (typeof options === 'object') {
this.options = setDefaults(options);
} else if (typeof options === 'function') {
this.getOptions = options;
}
passport.Strategy.call(this);
this.name = 'ldapauth';
this.verify = verify;
};
util.inherits(Strategy, passport.Strategy);
/* eslint-disable */
/**
* Get value for given field from given object. Taken from passport-local,
* copyright 2011-2013 Jared Hanson
*
* @private
* @param {Object} obj - The HTTP request object
* @param {string} field - The field name to look for
* @returns {string|null} - Found value for the field or null
*/
var lookup = function(obj, field) {
var i, len, chain, prop;
if (!obj) { return null; }
chain = field.split(']').join('').split('[');
for (i = 0, len = chain.length; i < len; i++) {
prop = obj[chain[i]];
if (typeof(prop) === 'undefined') { return null; }
if (typeof(prop) !== 'object') { return prop; }
obj = prop;
}
return null;
};
/* eslint-enable */
/**
* Verify the outcome of caller verify function - even if authentication (and
* usually authorization) is taken care by LDAP there may be reasons why
* a verify callback is provided, and again reasons why it may reject login
* for a valid user.
*
* @private
* @returns {undefined}
*/
var verify = function() {
// Callback given to user given verify function.
return function(err, user, info) {
if (err) {
return this.error(err);
}
if (!user) {
return this.fail(info);
}
return this.success(user, info);
}.bind(this);
};
/**
* Execute failureErrorCallback if provided
*
* @private
* @param {Error} err - The error occurred
* @returns {undefined}
*/
var handleErrorAsFailure = function(err) {
if (typeof this.options.failureErrorCallback === 'function') {
this.options.failureErrorCallback(err);
}
return this.fail(err, 500);
};
/**
* The actual authenticate implementation
*
* @private
* @param {Object} req - The HTTP request
* @param {Object} [options] - Flash messages
* @returns {undefined}
*/
var handleAuthentication = function(req, options) {
var username;
var password;
var ldap;
var errorHandler;
options || (options = {}); // eslint-disable-line no-param-reassign
if (typeof this.options.credentialsLookup === 'function') {
var credentials = this.options.credentialsLookup(req);
if (credentials != null) {
// name and pass as a courtesy for those who use basic-auth directly as
// they're likely the main user group.
username = credentials.username || credentials.name;
password = credentials.password || credentials.pass;
}
} else {
username = lookup(req.body, this.options.usernameField) || lookup(req.query, this.options.usernameField);
password = lookup(req.body, this.options.passwordField) || lookup(req.query, this.options.passwordField);
}
if (!username || !password) {
return this.fail({ message: options.badRequestMessage || 'Missing credentials' }, this.options.missingCredentialsStatus || 400);
}
errorHandler = this.options.handleErrorsAsFailures === true ? handleErrorAsFailure.bind(this) : this.error.bind(this);
/**
* AD possible messages
* http://www-01.ibm.com/support/docview.wss?uid=swg21290631
*/
var messages = {
'530': options.invalidLogonHours || 'Not Permitted to login at this time',
'531': options.invalidWorkstation || 'Not permited to logon at this workstation',
'532': options.passwordExpired || 'Password expired',
'533': options.accountDisabled || 'Account disabled',
'534': options.accountDisabled || 'Account disabled',
'701': options.accountExpired || 'Account expired',
'773': options.passwordMustChange || 'User must reset password',
'775': options.accountLockedOut || 'User account locked',
default: options.invalidCredentials || 'Invalid username/password'
};
ldap = new LdapAuth(this.options.server);
ldap.once('error', errorHandler);
ldap.on('error', function() { /* Ignored */ });
ldap.authenticate(username, password, function(err, user) {
ldap.close(function(){
// We don't care about the closing
});
if (err) {
// Invalid credentials / user not found are not errors but login failures
if (err.name === 'InvalidCredentialsError' || err.name === 'NoSuchObjectError' || (typeof err === 'string' && err.match(/no such user/i))) {
var message;
if (err.name === 'NoSuchObjectError') {
message = options.noSuchObject || 'Bad search base';
} else {
message = options.invalidCredentials || 'Invalid username/password';
}
if (err.message) {
var ldapComment = err.message.match(/data ([0-9a-fA-F]*), v[0-9a-fA-F]*/);
if (ldapComment && ldapComment[1]){
message = messages[ldapComment[1]] || messages['default'];
}
}
return this.fail({ message: message }, 401);
}
if (err.name === 'ConstraintViolationError'){
return this.fail({ message: options.constraintViolation || 'Exceeded password retry limit, account locked' }, 401);
}
// Other errors are (most likely) real errors
return errorHandler(err);
}
if (!user) {
return this.fail({ message: options.userNotFound || 'Invalid username/password' }, 401);
}
// Execute given verify function
if (this.verify) {
if (this.options.passReqToCallback) {
return this.verify(req, user, verify.call(this));
} else {
return this.verify(user, verify.call(this));
}
} else {
return this.success(user);
}
}.bind(this));
};
/**
* Authenticate the request coming from a form or such.
*
* @param {Object} req - The HTTP request
* @param {Object} [options] - Authentication options (flash messages). All messages have default values.
* @param {string} [options.badRequestMessage] - Message for missing username/password
* @param {string} [options.invalidCredentials] - Message for InvalidCredentialsError, NoSuchObjectError, and /no such user/ LDAP errors
* @param {string} [options.userNotFound] - Message for user not found
* @param {string} [options.constraintViolation] - Message when account is locked (or other constraint violation)
* @param {string} [options.invalidLogonHours] - Message for Windows AD invalidLogonHours error
* @param {string} [options.invalidWorkstation] - Message for Windows AD invalidWorkstation error
* @param {string} [options.passwordExpired] - Message for Windows AD passwordExpired error
* @param {string} [options.accountDisabled] - Message for Windows AD accountDisabled error
* @param {string} [options.accountExpired] - Message for Windows AD accountExpired error
* @param {string} [options.passwordMustChange] - Message for Windows AD passwordMustChange error
* @param {string} [options.accountLockedOut] - Message for Windows AD accountLockedOut error
* @returns {undefined}
*/
Strategy.prototype.authenticate = function(req, options) {
if ((typeof this.options === 'object') && (!this.getOptions)) {
return handleAuthentication.call(this, req, options);
}
var callback = function(err, configuration) {
if (err) {
return this.fail(err);
}
this.options = setDefaults(configuration);
handleAuthentication.call(this, req, options);
};
// Added functionality: getOptions can accept now up to 2 parameters
if (this.getOptions.length === 1) { // Accepts 1 parameter, backwards compatibility
this.getOptions(callback.bind(this));
} else { // Accepts 2 parameters, pass request as well
this.getOptions(req, callback.bind(this));
}
};
module.exports = Strategy;
+66
View File
@@ -0,0 +1,66 @@
{
"name": "passport-ldapauth",
"description": "LDAP authentication strategy for Passport",
"author": "Vesa Poikajärvi <vesa.poikajarvi@iki.fi>",
"contributors": [
"Simon Gaeremynck <gaeremyncks@gmail.com>",
"Michael Bailly <mbailly@linagora.com>",
"Jason Gelinas <jason.gelinas@citi.com>",
"arumi <arumi@wge7033.secheron.net>",
"Anthony Hernandez <anthony.hernandez@clownphobia.com>",
"Chris Harwood <harwood@teralogics.com>",
"David Dolcimascolo <david.dolcimascolo@stordata.fr>"
],
"keywords": [
"ldap",
"passport",
"authentication",
"ldapauth"
],
"version": "3.0.1",
"license": "MIT",
"main": "./lib/passport-ldapauth",
"types": "./lib/passport-ldapauth/strategy.d.ts",
"engines": {
"node": ">=0.8.0"
},
"repository": {
"type": "git",
"url": "https://github.com/vesse/passport-ldapauth.git"
},
"bugs": {
"url": "https://github.com/vesse/passport-ldapauth/issues"
},
"dependencies": {
"ldapauth-fork": "^5.0.1",
"passport-strategy": "^1.0.0"
},
"devDependencies": {
"@types/basic-auth": "^1.1.3",
"@types/bunyan": "^1.8.6",
"@types/express": "^4.17.9",
"@types/node": "^14.14.7",
"@types/passport": "^1.0.4",
"basic-auth": "^2.0.1",
"body-parser": "^1.19.0",
"bunyan": "^1.8.14",
"chai": "^4.2.0",
"eslint": "^7.13.0",
"express": "^4.17.1",
"ldapjs": "^2.2.1",
"merge": "^2.1.0",
"mocha": "^8.2.1",
"passport": "^0.4.1",
"supertest": "^6.0.1",
"typescript": "^4.0.5",
"watch": "^1.0.2"
},
"scripts": {
"prepare": "npm run lint",
"lint": "eslint ./lib",
"lint:watch": "watch 'npm run lint' ./lib --wait 0.5",
"test": "NODE_PATH=lib mocha --reporter spec test/*-test.js",
"test:watch": "watch 'npm test' ./lib ./test --wait 0.5",
"test:typescript": "cd test/typescript && tsc"
}
}
+59
View File
@@ -0,0 +1,59 @@
var express = require('express'),
passport = require('passport'),
LdapStrategy = require('passport-ldapauth').Strategy,
bodyParser = require('body-parser');
var server = null;
var init_passport = function(opts, testopts) {
if (testopts.no_callback === true) {
passport.use(new LdapStrategy(opts));
} else {
passport.use(new LdapStrategy(opts, function(user, cb) {
return cb(null, user);
}));
}
};
passport.serializeUser(function(user, cb) {
cb(null, user.dn);
});
passport.deserializeUser(function(dn, cb) {
cb(null, {dn: dn});
});
exports.start = function(opts, testopts, cb) {
var app = express();
init_passport(opts, testopts);
app.use(bodyParser.json());
app.use(passport.initialize());
app.post('/login', passport.authenticate('ldapauth', {session: false}), function(req, res) {
res.send({status: 'ok'});
});
app.post('/custom-cb-login', function(req, res, next) {
passport.authenticate('ldapauth', function(err, user, info) {
if (err) return next(err);
if (!user) return res.status(401).send(info);
req.logIn(user, function(err) {
if (err) return next(err);
return res.json(user);
})
})(req, res, next);
});
if (typeof cb === 'function') return cb(app);
return;
};
exports.close = function(cb) {
if (server) server.close();
server = null;
if (typeof cb === 'function') return cb();
return;
};
+82
View File
@@ -0,0 +1,82 @@
var ldap = require('ldapjs');
authorize = function(req, res, next) {
return next();
};
var SUFFIX = 'ou=passport-ldapauth';
var server = null;
db = {
'valid': {
dn: 'cn=valid,ou=passport-ldapauth',
attributes: {
uid: 'valid',
name: 'Valid User'
}
}
};
exports.start = function(port, cb) {
if (server) {
if (typeof cb === 'function') return cb();
return;
}
server = ldap.createServer();
server.bind('cn=root', function(req, res, next) {
if (req.dn.toString() !== 'cn=root' || req.credentials !== 'secret') {
return next(new ldap.InvalidCredentialsError());
}
res.end();
return next();
});
server.bind(SUFFIX, authorize, function(req, res, next) {
var dn = req.dn.toString();
if (dn !== 'cn=valid, ou=passport-ldapauth' || req.credentials !== 'valid') {
return next(new ldap.InvalidCredentialsError());
}
res.end();
return next();
});
server.search(SUFFIX, authorize, function(req, res, next) {
if (req.filter.attribute === 'uid' && req.filter.value === 'valid') {
res.send(db['valid']);
} else if (req.filter.attribute === 'uid' && req.filter.value === 'ms-ad') {
return next(new ldap.InvalidCredentialsError("0090308: LdapErr: DSID-0C09030B, comment: AcceptSecurityContext error, data 533, v893 HEX: 0x533 - account disabled"));
} else if (req.filter.attribute === 'member' && req.filter.value === db.valid.dn) {
res.send({
dn: 'cn=Group 1, ou=passport-ldapauth',
attributes: {
name: 'Group 1'
}
});
res.send({
dn: 'cn=Group 2, ou=passport-ldapauth',
attributes: {
name: 'Group 2'
}
});
}
res.end();
return next();
});
server.listen(port, function() {
if (typeof cb === 'function') return cb();
});
};
exports.close = function(cb) {
if (server) server.close();
server = null;
if (typeof cb === 'function') return cb();
return;
};
if (!module.parent) {
exports.start(1389);
}
@@ -0,0 +1,428 @@
var should = require('chai').Should(),
LdapStrategy = require('passport-ldapauth'),
request = require('supertest'),
basicAuth = require('basic-auth'),
ldapserver = require('./ldapserver'),
appserver = require('./appserver');
var LDAP_PORT = 1399;
var expressapp = null;
// Base options that are cloned where needed to edit
var BASE_OPTS = {
server: {
url: 'ldap://localhost:' + LDAP_PORT.toString(),
bindDn: 'cn=root',
bindCredentials: 'secret',
searchBase: 'ou=passport-ldapauth',
searchFilter: '(uid={{username}})'
}
},
BASE_TEST_OPTS = {
no_callback: false
};
var start_servers = function(opts, test_opts) {
return function(cb) {
ldapserver.start(LDAP_PORT, function() {
appserver.start(opts, test_opts, function(app) {
expressapp = app;
cb();
});
});
}
}
var stop_servers = function(cb) {
appserver.close(function() {
ldapserver.close(function() {
cb();
});
});
};
describe('LDAP authentication strategy', function() {
describe('by itself', function() {
it('should export Strategy constructor directly', function(cb) {
require('passport-ldapauth').should.be.a('function');
cb();
});
it('should export Strategy constructor separately as well', function(cb) {
var strategy = require('passport-ldapauth').Strategy;
strategy.should.be.a('function');
(function() {
new strategy(BASE_OPTS);
}).should.not.throw(Error);
cb();
});
it('should be named ldapauth', function(cb) {
var s = new LdapStrategy(BASE_OPTS);
s.name.should.equal('ldapauth');
cb();
});
it('should throw an error if no arguments are provided', function(cb) {
(function() {
new LdapStrategy();
}).should.throw(Error);
cb();
});
it('should throw an error if options are not accepted by ldapauth', function(cb) {
var s = new LdapStrategy({}, function() {});
(function() {
s.authenticate({body: {username: 'valid', password: 'valid'}});
}).should.throw(Error);
cb();
});
it('should initialize without a verify callback', function(cb) {
(function() {
new LdapStrategy({server: {}})
}).should.not.throw(Error);
cb();
});
});
describe('with basic settings', function() {
before(start_servers(BASE_OPTS, BASE_TEST_OPTS));
after(stop_servers);
it('should return unauthorized if credentials are not given', function(cb) {
request(expressapp)
.post('/login')
.send({})
.expect(400)
.end(cb);
});
it('should allow access with valid credentials', function(cb) {
request(expressapp)
.post('/login')
.send({username: 'valid', password: 'valid'})
.expect(200)
.end(cb);
});
it('should allow access with valid credentials in query string', function(cb) {
request(expressapp)
.post('/login?username=valid&password=valid')
.expect(200)
.end(cb);
});
it('should return unauthorized with invalid credentials', function(cb) {
request(expressapp)
.post('/login')
.send({username: 'valid', password: 'invalid'})
.expect(401)
.end(cb);
});
it('should return unauthorized with non-existing user', function(cb) {
request(expressapp)
.post('/login')
.send({username: 'nonexisting', password: 'invalid'})
.expect(401)
.end(cb);
});
it('should return more specific flash message for AD reply', function(cb) {
request(expressapp)
.post('/custom-cb-login')
.send({username: 'ms-ad', password: 'invalid'})
.expect(401)
.end(function(err, res) {
should.not.exist(err);
res.body.message.should.equal('Account disabled')
cb(err, res);
});
});
});
describe('without a verify callback', function() {
before(start_servers(BASE_OPTS, {no_callback: true}));
after(stop_servers);
it('should still authenticate', function(cb) {
request(expressapp)
.post('/login')
.send({username: 'valid', password: 'valid'})
.expect(200)
.end(cb);
});
it('should reject invalid event', function(cb) {
request(expressapp)
.post('/login')
.send({username: 'valid', password: 'invalid'})
.expect(401)
.end(cb);
});
});
describe('with optional options', function() {
afterEach(stop_servers);
it('should read given fields instead of defaults', function(cb) {
var OPTS = JSON.parse(JSON.stringify(BASE_OPTS));
OPTS.usernameField = 'ldapuname';
OPTS.passwordField = 'ldappwd';
start_servers(OPTS, BASE_TEST_OPTS)(function() {
request(expressapp)
.post('/login')
.send({ldapuname: 'valid', ldappwd: 'valid'})
.expect(200)
.end(cb);
});
});
it('should pass request to verify callback if defined so', function(cb) {
var OPTS = JSON.parse(JSON.stringify(BASE_OPTS));
OPTS.passReqToCallback = true;
start_servers(OPTS, BASE_TEST_OPTS)(function() {
var req = {body: {username: 'valid', password: 'valid', testkey: 1}},
s = new LdapStrategy(OPTS, function(req, user, done) {
req.should.have.keys('body');
req.body.should.have.keys(['username', 'password', 'testkey']);
done(null, user);
});
s.success = function(user) {
should.exist(user);
user.uid.should.equal('valid');
cb();
};
s.error = function() {}; // Just to have this when not run via passport
s.authenticate(req);
});
});
it('should allow access with valid credentials in the header', function(cb) {
var OPTS = JSON.parse(JSON.stringify(BASE_OPTS));
OPTS.credentialsLookup = basicAuth;
start_servers(OPTS, BASE_TEST_OPTS)(function() {
request(expressapp)
.post('/login')
.set('Authorization', 'Basic dmFsaWQ6dmFsaWQ=')
.expect(200)
.end(cb);
});
});
it('should support returning a custom status code when credentials are missing', function(cb) {
var OPTS = JSON.parse(JSON.stringify(BASE_OPTS));
OPTS.missingCredentialsStatus = 401;
start_servers(OPTS, BASE_TEST_OPTS)(function() {
request(expressapp)
.post('/login')
.expect(401)
.end(cb);
});
});
});
describe('with options as function', function() {
var OPTS = JSON.parse(JSON.stringify(BASE_OPTS));
OPTS.usernameField = 'cb_uname';
OPTS.passwordField = 'cb_pwd';
var opts = function(cb) {
process.nextTick(function() {
cb(null, OPTS);
});
};
before(start_servers(opts, BASE_TEST_OPTS));
after(stop_servers);
it('should use the options returned from the function', function(cb) {
request(expressapp)
.post('/login')
.send({cb_uname: 'valid', cb_pwd: 'valid'})
.expect(200)
.end(cb);
});
it('should not allow login if using wrong fields', function(cb) {
request(expressapp)
.post('/login')
.send({username: 'valid', password: 'valid'})
.expect(400)
.end(cb);
});
});
describe('with options as function returning dynamic sets', function() {
var OPTS = JSON.parse(JSON.stringify(BASE_OPTS));
OPTS.usernameField = 'first_uname';
OPTS.passwordField = 'first_pwd';
var OPTS2 = JSON.parse(JSON.stringify(BASE_OPTS));
OPTS2.usernameField = 'second_uname';
OPTS2.passwordField = 'second_pwd';
var opts = function(req, cb) {
process.nextTick(function() {
if (req.body.set == 'first') {
cb(null, OPTS);
} else {
cb(null, OPTS2);
}
});
};
before(start_servers(opts, BASE_TEST_OPTS));
after(stop_servers);
it('should use the first set options returned from the function', function(cb) {
request(expressapp)
.post('/login')
.send({first_uname: 'valid', first_pwd: 'valid', set: 'first'})
.expect(200)
.end(cb);
});
it('should not allow first set login if using wrong fields', function(cb) {
request(expressapp)
.post('/login')
.send({second_uname: 'valid', second_pwd: 'valid', set: 'first'})
.expect(400)
.end(cb);
});
it('should use the second set options returned from the function', function(cb) {
request(expressapp)
.post('/login')
.send({second_uname: 'valid', second_pwd: 'valid', set: 'second'})
.expect(200)
.end(cb);
});
it('should not allow second set login if using wrong fields', function(cb) {
request(expressapp)
.post('/login')
.send({first_uname: 'valid', first_pwd: 'valid', set: 'second'})
.expect(400)
.end(cb);
});
});
describe('with group fetch settings defined', function() {
var OPTS;
var groupTest = function(opts, cb) {
start_servers(opts, BASE_TEST_OPTS)(function() {
var req = {body: {username: 'valid', password: 'valid'}},
s = new LdapStrategy(opts, function(user, done) {
req.should.have.keys('body');
req.body.should.have.keys(['username', 'password']);
done(null, user);
});
s.success = function(user) {
should.exist(user);
user.uid.should.equal('valid');
user._groups.length.should.equal(2);
user._groups[0].name.should.equal('Group 1');
user._groups[1].name.should.equal('Group 2');
cb();
};
s.error = function() {}; // Just to have this when not run via passport
s.authenticate(req);
});
}
beforeEach(function(cb) {
OPTS = JSON.parse(JSON.stringify(BASE_OPTS));
OPTS.server.groupSearchBase = 'ou=passport-ldapauth';
OPTS.server.groupSearchScope = 'sub';
cb();
});
afterEach(stop_servers);
it('should return groups for user with string filter', function(cb) {
OPTS.server.groupSearchFilter = '(member={{dn}})';
groupTest(OPTS, cb);
});
it('should return groups for user with function filter', function(cb) {
OPTS.server.groupSearchFilter = function(user) {
return '(member={{dn}})'.replace(/{{dn}}/, user.dn)
};
groupTest(OPTS, cb);
});
});
describe('with invalid LDAP url', function() {
var OPTS = JSON.parse(JSON.stringify(BASE_OPTS));
OPTS.server.url = 'ldap://nonexistingdomain.fi:389';
before(start_servers(OPTS, BASE_TEST_OPTS));
after(stop_servers);
it('should return with an error', function(cb) {
request(expressapp)
.post('/login')
.send({username: 'valid', password: 'valid'})
.expect(500)
.end(cb);
});
});
describe('with handleErrorsAsFailures', function() {
after(stop_servers);
it('should return with a failure', function(cb) {
var callbackCalled = false;
var testCompleted = false;
var OPTS = JSON.parse(JSON.stringify(BASE_OPTS));
OPTS.server.url = 'ldap://nonexistingdomain.fi:389';
OPTS.handleErrorsAsFailures = true;
OPTS.failureErrorCallback = function(err) {
should.exist(err);
callbackCalled = true;
}
start_servers(OPTS, BASE_TEST_OPTS)(function() {
var req = {body: {username: 'valid', password: 'valid'}},
s = new LdapStrategy(OPTS);
s.fail = function(msg, code) {
code.should.equal(500);
callbackCalled.should.be.true;
// There can be more than one event emitted and mocha fails
// if callback is called more than once
if (testCompleted === false) {
testCompleted = true;
cb();
}
}
s.error = function() {}; // Just to have this when not run via passport
s.authenticate(req);
});
});
});
});
@@ -0,0 +1,76 @@
"use strict";
exports.__esModule = true;
var Passport = require("passport");
var LdapStrategy = require("../../");
var Express = require("express");
var Logger = require("bunyan");
var BasicAuth = require("basic-auth");
var app = Express();
var user = { dn: 'test' };
var log = new Logger({
name: 'ldap',
component: 'client',
stream: process.stderr,
level: 'trace'
});
var options = {
server: {
url: 'ldap://ldap.forumsys.com:389',
bindDN: 'cn=read-only-admin,dc=example,dc=com',
bindCredentials: 'password',
searchBase: 'dc=example,dc=com',
searchFilter: '(uid={{username}})',
log: log,
cache: true,
includeRaw: true,
groupSearchFilter: '(member={{dn}})',
groupSearchBase: 'dc=example,dc=com'
},
credentialsLookup: BasicAuth
};
var optionsAsFunction = function (req, callback) {
callback(null, options);
};
var regularCallback = function (user, callback) {
if (user.foo) {
callback(new Error('Foo user is an error'), null, { message: 'Foo user' });
}
else if (!user) {
callback(null, false, { message: 'No user' });
}
else {
callback(null, user);
}
};
var reqCallback = function (req, user, callback) {
if (user.foo) {
callback(new Error('Foo user is an error'), null, { message: 'Foo user' });
}
else if (!user) {
callback(null, false, { message: 'No user' });
}
else {
callback(null, user);
}
};
var credentialsLookup = function (req) { return ({
user: 'username',
pass: 'password'
}); };
Passport.serializeUser(function (user, done) { return done(null, user.dn); });
Passport.deserializeUser(function (dn, done) { return done(null, user); });
Passport.use(new LdapStrategy(options, regularCallback));
Passport.use('withreq', new LdapStrategy(options, reqCallback));
Passport.use('dynopts', new LdapStrategy(optionsAsFunction));
var authOpts = {
badRequestMessage: 'Bad request you did there'
};
app.post('/login', Passport.authenticate('ldapauth', authOpts));
app.post('/login', function (req, res, next) {
Passport.authenticate('ldapauth', function (err, user) {
req.logIn(user, function (err) {
res.send({ ok: 1 });
});
})(req, res, next);
});
//# sourceMappingURL=test.js.map
@@ -0,0 +1 @@
{"version":3,"file":"test.js","sourceRoot":"","sources":["test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,91 @@
/**
* Just a test file that should compile properly with tsc
*/
import * as Passport from 'passport';
import * as LdapStrategy from '../../';
import * as Express from 'express';
import { Request } from 'express';
import * as Logger from 'bunyan';
import * as BasicAuth from 'basic-auth';
const app = Express();
interface User {
dn: string;
foo?: boolean;
}
const user: User = { dn: 'test' };
const log = new Logger({
name: 'ldap',
component: 'client',
stream: process.stderr,
level: 'trace'
});
const options: LdapStrategy.Options = {
server: {
url: 'ldap://ldap.forumsys.com:389',
bindDN: 'cn=read-only-admin,dc=example,dc=com',
bindCredentials: 'password',
searchBase: 'dc=example,dc=com',
searchFilter: '(uid={{username}})',
log: log,
cache: true,
includeRaw: true,
groupSearchFilter: '(member={{dn}})',
groupSearchBase: 'dc=example,dc=com'
},
credentialsLookup: BasicAuth
}
const optionsAsFunction: LdapStrategy.OptionsFunction = (req: Request, callback: LdapStrategy.OptionsFunctionCallback) => {
callback(null, options);
}
const regularCallback: LdapStrategy.VerifyCallback = (user: User, callback: LdapStrategy.VerifyDoneCallback) => {
if (user.foo) {
callback(new Error('Foo user is an error'), null, { message: 'Foo user' });
} else if (!user) {
callback(null, false, { message: 'No user' });
} else {
callback(null, user);
}
}
const reqCallback: LdapStrategy.VerifyCallbackWithRequest = (req: Request, user: User, callback: LdapStrategy.VerifyDoneCallback) => {
if (user.foo) {
callback(new Error('Foo user is an error'), null, { message: 'Foo user' });
} else if (!user) {
callback(null, false, { message: 'No user' });
} else {
callback(null, user);
}
}
const credentialsLookup: LdapStrategy.CredentialsLookup = (req: Request): LdapStrategy.CredentialsLookupResult => ({
user: 'username',
pass: 'password'
});
Passport.serializeUser((user: User, done) => done(null, user.dn));
Passport.deserializeUser((dn, done) => done(null, user));
Passport.use(new LdapStrategy(options, regularCallback));
Passport.use('withreq', new LdapStrategy(options, reqCallback));
Passport.use('dynopts', new LdapStrategy(optionsAsFunction));
const authOpts: LdapStrategy.AuthenticateOptions = {
badRequestMessage: 'Bad request you did there'
}
app.post('/login', Passport.authenticate('ldapauth', authOpts));
app.post('/login', (req, res, next) => {
Passport.authenticate('ldapauth', (err: Error, user: User) => {
req.logIn(user, (err: Error) => {
res.send({ok: 1});
})
})(req, res, next);
});
@@ -0,0 +1,12 @@
{
"compilerOptions": {
"module": "commonjs",
"noImplicitAny": true,
"removeComments": true,
"preserveConstEnums": true,
"sourceMap": true
},
"files": [
"test.ts"
]
}