express-passport-ldap-mongoose
Advanced tools
Comparing version 2.1.0 to 3.0.0
@@ -7,6 +7,6 @@ let mongoose = require('mongoose') | ||
// these fields are from ldap | ||
uid: { type: String, lowercase: true }, | ||
givenName: { type: String}, | ||
username: { type: String, lowercase: true }, | ||
cn: { type: String}, | ||
sn: { type: String}, | ||
displayName: {type: String}, | ||
dn: {type: String}, | ||
mail: { type: String, lowercase: true} | ||
@@ -13,0 +13,0 @@ }, |
@@ -40,5 +40,46 @@ /* jshint node:true */ | ||
// use the library express-passport-ldap-mongoose | ||
LdapAuth.init(CONFIG.ldap.dn, CONFIG.ldap.url, app, | ||
// backward compatible mode | ||
/*LdapAuth.init(CONFIG.ldap.dn, CONFIG.ldap.url, app, | ||
(id) => User.findOne({ uid: id }).exec(), | ||
(user) => User.findOneAndUpdate({ uid: user.uid }, user, { upsert: true, new: true }).exec() | ||
)*/ | ||
// new mode, simple user | ||
let usernameAttr = 'uid' | ||
let searchBase = CONFIG.ldap.dn | ||
let options = { | ||
ldapOpts: { | ||
url: CONFIG.ldap.url | ||
}, | ||
userDn: `uid={{username}},${CONFIG.ldap.dn}`, | ||
userSearchBase: searchBase, | ||
usernameAttribute: usernameAttr | ||
} | ||
let admOptions = { | ||
ldapOpts: { | ||
url: CONFIG.ldap.url, | ||
//tlsOptions: { rejectUnauthorized: false } | ||
}, | ||
adminDn: `cn=read-only-admin,dc=example,dc=com`, | ||
adminPassword: 'password', | ||
userSearchBase: searchBase, | ||
usernameAttribute: usernameAttr | ||
//starttls: true | ||
} | ||
let userOptions = { | ||
ldapOpts: { | ||
url: CONFIG.ldap.url, | ||
//tlsOptions: { rejectUnauthorized: false } | ||
}, | ||
userDn: `uid={{username}},dc=example,dc=com`, | ||
userSearchBase: searchBase, | ||
usernameAttribute: usernameAttr | ||
//starttls: true | ||
} | ||
LdapAuth.init(userOptions, '', app, | ||
(id) => User.findOne({ username: id }).exec(), | ||
(user) => { | ||
console.log(`${user[usernameAttr]} has logged in`) | ||
return User.findOneAndUpdate({ username: user[usernameAttr] }, user, { upsert: true, new: true }).exec() | ||
} | ||
) | ||
@@ -51,2 +92,4 @@ | ||
// Start server | ||
app.listen(4000, '127.0.0.1') | ||
let port=4000 | ||
console.log(`server listen on port ${port}`) | ||
app.listen(port, '127.0.0.1') |
106
index.js
@@ -6,6 +6,7 @@ /** | ||
const passport = require('passport') | ||
const LdapStrategy = require('passport-ldapauth') | ||
const CustomStrategy = require('passport-custom').Strategy | ||
const { authenticate } = require('ldap-authentication') | ||
var _backwardCompatible = false | ||
var _dn | ||
var _ldapurl | ||
var _findFunc | ||
@@ -15,3 +16,3 @@ var _insertFunc | ||
var _logoutUrl | ||
var _router | ||
var _usernameAttributeName | ||
@@ -21,4 +22,5 @@ /** | ||
* | ||
* @param {string} dn - ldap dn | ||
* @param {string} ldapurl - ldap server url | ||
* @param {object|string} opt - if is an object, the options object to pass to ldapauth. if is a string, is ldap search base (backward compatible) | ||
* in opt object, literal `{{username}}` will be replaced with value in req.body.username | ||
* @param {string} ldapurl - ldap server url (if url is defined in opt, this will be ignored) | ||
* @param {object} router - express router | ||
@@ -30,5 +32,9 @@ * @param {function} findFunc - function(id) to find the user in local db by id | ||
*/ | ||
var init = function (dn, ldapurl, router, findFunc, insertFunc, loginUrl, logoutUrl) { | ||
_dn = dn | ||
_ldapurl = ldapurl | ||
var init = function (opt, ldapurl, router, findFunc, insertFunc, loginUrl, logoutUrl) { | ||
if (typeof(opt) === 'string') { | ||
// backward compatible mode | ||
console.log('express-passport-ldap-mongoose: DeprecationWarning: calling init with DN string as first argument will be removed in a future version. Use an option object instead') | ||
_dn = opt | ||
_backwardCompatible = true | ||
} | ||
_router = router | ||
@@ -39,28 +45,61 @@ _findFunc = findFunc | ||
_logoutUrl = logoutUrl || '/logout' | ||
passport.use(new LdapStrategy((req, callback) => { | ||
// Fetching things from database or whatever | ||
// use x-www-form-urlencoded to fill in username and password | ||
// req should be already urlencodedParser ed. body | ||
// should be filled with username and password. | ||
process.nextTick(() => { | ||
var opts = { | ||
server: { | ||
url: _ldapurl, | ||
bindDn: `uid=${req.body.username},${_dn}`, | ||
bindCredentials: `${req.body.password}`, | ||
searchBase: _dn, | ||
searchFilter: `uid=${req.body.username}`, | ||
reconnect: true | ||
_usernameAttributeName = '' | ||
passport.use('ldap', new CustomStrategy(async (req, done) => { | ||
try { | ||
if (!req.body.username || !req.body.password) { | ||
throw new Error('username and password must be both provided') | ||
} | ||
let username = req.body.username | ||
let password = req.body.password | ||
// construct the parameter to pass in authenticate() function | ||
let options | ||
if (_backwardCompatible) { | ||
_usernameAttributeName = 'uid' | ||
options = { | ||
ldapOpts: { | ||
url: ldapurl | ||
}, | ||
userDn: `uid=${username},${_dn}`, | ||
userPassword: password, | ||
userSearchBase: _dn, | ||
usernameAttribute: 'uid', | ||
username: username | ||
} | ||
} else { | ||
_usernameAttributeName = opt.usernameAttribute | ||
options = { | ||
ldapOpts: opt.ldapOpts, | ||
userPassword: password, | ||
userSearchBase: opt.userSearchBase, | ||
usernameAttribute: _usernameAttributeName, | ||
username: username, | ||
starttls: opt.starttls | ||
} | ||
if (opt.userDn) { | ||
options.userDn = opt.userDn.replace('{{username}}', username) | ||
} | ||
if (opt.adminDn) { | ||
options.adminDn = opt.adminDn | ||
} | ||
if (opt.adminPassword) { | ||
options.adminPassword = opt.adminPassword | ||
} | ||
} | ||
callback(null, opts) | ||
}) | ||
}, | ||
(user, done) => { | ||
return done(null, user) | ||
})) | ||
// ldap authenticate the user | ||
let user = await authenticate(options) | ||
// success | ||
done(null, user) | ||
} catch (error) { | ||
// authentication failure | ||
done(error, null) | ||
} | ||
})) | ||
passport.serializeUser((user, done) => { | ||
done(null, user.uid) | ||
if (user[_usernameAttributeName]) { | ||
done(null, user[_usernameAttributeName]) | ||
} else { | ||
done('User from ldap server does not have field ' + _usernameAttributeName) | ||
} | ||
}) | ||
@@ -110,8 +149,9 @@ | ||
var login = function (req, res, next) { | ||
passport.authenticate('ldapauth', (err, user, info) => { | ||
passport.authenticate('ldap', (err, user) => { | ||
if (err) { | ||
return next(err) | ||
res.status(401).json({success: false, message: err.message}) | ||
return | ||
} | ||
if (!user) { | ||
res.status(401).json({ success: false, message: info.message }) | ||
res.status(401).json({ success: false, message: 'User cannot be found' }) | ||
} else { | ||
@@ -118,0 +158,0 @@ req.login(user, loginErr => { |
{ | ||
"name": "express-passport-ldap-mongoose", | ||
"version": "2.1.0", | ||
"version": "3.0.0", | ||
"description": "A library to use passport-ldapauth and local MongoDB to authenticate and save users", | ||
@@ -10,4 +10,5 @@ "main": "index.js", | ||
"dependencies": { | ||
"ldap-authentication": "^2.1.1", | ||
"passport": "^0.4.1", | ||
"passport-ldapauth": "^2.1.3" | ||
"passport-custom": "^1.1.0" | ||
}, | ||
@@ -20,4 +21,4 @@ "devDependencies": { | ||
"jest": "^24.9.0", | ||
"mongoose": "^5.8.4", | ||
"superagent": "^5.1.3", | ||
"mongoose": "^5.8.6", | ||
"superagent": "^5.2.1", | ||
"supertest": "^4.0.2" | ||
@@ -24,0 +25,0 @@ }, |
@@ -6,15 +6,18 @@ # express-passport-ldap-mongoose | ||
A library to use passport-ldapauth and local MongoDB to authenticate and save users | ||
A turn key library that uses [ldap-authentication](https://github.com/shaozi/ldap-authentication) | ||
with Passport and local database (MongoDB) to authenticate and save users | ||
When an application needs to authenticate a user against an LDAP server, it normally also needs to | ||
save the user into local MongoDB for further references. `express-passport-ldap-mongoose` is designed | ||
to handle this requirement with a simple wrapper layer on top of expressjs, passportjs, passport-ldapauth, | ||
to handle this requirement with a simple wrapper layer on top of expressjs, passportjs, | ||
[ldap-authentication](https://github.com/shaozi/ldap-authentication), | ||
and MongoDB. | ||
## Requirement | ||
## Requirements | ||
* node Express | ||
* Mongoose | ||
* Mongoose (optional) | ||
* Passport | ||
* Passport-ldapauth | ||
* [ldap-authentication](https://github.com/shaozi/ldap-authentication) | ||
* The login submit field names should be `username` for username, and `password` for password | ||
@@ -28,2 +31,3 @@ ## Installation | ||
## Usage | ||
`express-passport-ldap-mongoose` configures passportjs and adds the login and logout route to | ||
@@ -37,28 +41,65 @@ your express app or router. All you need to do is call the `init` function of the library | ||
app.use(sessionMiddleWare) | ||
LdapAuth.init(dn, ldapurl, app, findUserFunc, upsertUserFunc, loginPath, logoutPath) | ||
LdapAuth.init(options, '', app, findUserFunc, upsertUserFunc, loginPath, logoutPath) | ||
``` | ||
## MongoDB model | ||
The `User` model in local MongoDB must have the `uid` key that maps to LDAP `uid` property. This | ||
`uid` field is used to uniquely identify a user and is normally the user's login name. | ||
When search for a user by its username in LDAP, a `usernameAttribute` is needed. | ||
The `User` model in local MongoDB must have the same key as the value of `usernameAttribute` | ||
that maps to the LDAP attribute. In some cases, and in the example we are using `uid`. | ||
it is used to uniquely identify a user and equals to the user's login username. | ||
## Parameters | ||
* `dn`: The bind DN of LDAP server. Example: `dc=example.com,dc=com` | ||
* `ldapurl`: URL of LDAP server. Example: `ldaps://ldap.example.com`, `ldap://ldap.example.com` | ||
* `options`: If the first parameter is an object, | ||
it is the options object to pass to `ldap-authentication`'s `authenticate()` function. | ||
If is a string (deprecated), is the ldap search base (for backward compatible) | ||
If options is an object, literal `{{username}}` in the `userDn` will be replaced with the value in | ||
`req.body.username` which will be the user input username. | ||
See [ldap-authentication](https://github.com/shaozi/ldap-authentication) for detail explanation on each options. | ||
String Example (deprecated): `dc=example.com,dc=com` | ||
Options object Example: | ||
```javascript | ||
let options = { | ||
ldapOpts: { | ||
url: 'ldap://localhost' | ||
}, | ||
// note in this example it only use the user to directly | ||
// bind to the LDAP server. You can also use an admin | ||
// here. See the document of ldap-authentication. | ||
userDn: `uid=${req.body.username},${ldapBaseDn}`, | ||
userPassword: req.body.password, | ||
userSearchBase: ldapBaseDn, | ||
usernameAttribute: 'uid', | ||
username: req.body.username | ||
} | ||
``` | ||
* `ldapurl` (deprecated): URL of LDAP server. Example: `ldaps://ldap.example.com`, `ldap://ldap.example.com` | ||
It will be ignored if the first parameter of the function is an object | ||
* `app`: Express app or router | ||
* `findUserFunc`: `function(id)`. A function takes a string id and return a promise that resolves to a user or null. | ||
* `findUserFunc`: `function(id)`. A function takes a string id and return a promise that resolves to a user or null. | ||
This function is called everytime passport do deserialization. It is normally a `FindOne` or `FindById` call against | ||
local mongo database. Example: `(id) => {return User.findOne({ uid: id }).exec()}` | ||
local mongo database. Example: `(id) => {return User.findOne({ uid: id }).exec()}`. However, it does not have to be | ||
any database related. It is just a functin that can return a user from a user id. | ||
* `upsertUserFunc`: `function(user)`. A function take a user object (obtained from ldap server and saved in express `req`) | ||
and upsert into local database; returns a promise that resolves to a local db user object. | ||
and upsert into local database; returns a promise that resolves to a local db user object. Again, it does not have to | ||
be any database related. It is essentially a function that update some internal record of a user. | ||
Example: `(user) => {return User.findOneAndUpdate({ uid: user.uid }, user, { upsert: true, new: true }).exec()}` | ||
* `loginPath`: (optional, default `/login`) The login path for express to parse the login posted json data. The posted data | ||
must be in json format, and with `username` and `password` as the key names. An `app.post(loginPath, loginHandler)` | ||
must be in json format, and with `username` and `password` as the key names. An `app.post(loginPath, loginHandler)` | ||
will be automatically added and handled by the library. | ||
* `logoutPath`: (optional, default `/logout`) The logout path for express to parse the logout request. An `app.get(logoutPath, logoutHandler)` | ||
* `logoutPath`: (optional, default `/logout`) The logout path for express to parse the logout request. An `app.get(logoutPath, logoutHandler)` | ||
will be automatically added and handled by the library. | ||
## Example | ||
Complete example is at https://github.com/shaozi/express-passport-ldap-mongoose-example | ||
Complete example is in the example folder. | ||
Another example on how to use Passport and [ldap-authentication](https://github.com/shaozi/ldap-authentication) can be found in [passport-ldap-example](https://github.com/shaozi/passport-ldap-example). | ||
```javascript | ||
@@ -96,6 +137,17 @@ const mongoose = require('mongoose') | ||
// use the library express-passport-ldap-mongoose | ||
LdapAuth.init(CONFIG.ldap.dn, CONFIG.ldap.url, app, (id) => { | ||
return User.findOne({ uid: id }).exec() | ||
let usernameAttributeName = 'uid' | ||
LdapAuth.init({ | ||
ldapOpts: { | ||
url: 'ldap://localhost' | ||
}, | ||
// note in this example it only use the user to directly | ||
// bind to the LDAP server. You can also use an admin | ||
// here. See the document of ldap-authentication. | ||
userDn: `uid={{username}},${ldapBaseDn}`, | ||
userSearchBase: ldapBaseDn, | ||
usernameAttribute: usernameAttributeName | ||
}, '', app, (id) => { | ||
return User.findOne({ usernameAttributeName: id }).exec() | ||
}, (user) => { | ||
return User.findOneAndUpdate({ uid: user.uid }, user, { upsert: true, new: true }).exec() | ||
return User.findOneAndUpdate({ username: user[usernameAttributeName] }, user, { upsert: true, new: true }).exec() | ||
}) | ||
@@ -102,0 +154,0 @@ |
@@ -47,3 +47,12 @@ const request = require('supertest'); | ||
auth.init('dc=example,dc=com', 'ldap://ldap.forumsys.com', app, | ||
let userOptions = { | ||
ldapOpts: { | ||
url: 'ldap://ldap.forumsys.com' | ||
}, | ||
userDn: `uid={{username}},dc=example,dc=com`, | ||
userSearchBase: 'dc=example,dc=com', | ||
usernameAttribute: 'uid' | ||
} | ||
auth.init(userOptions, '', app, | ||
findUserById, upsertUser | ||
@@ -61,2 +70,4 @@ ) | ||
expect(response.statusCode).toBe(401) | ||
expect(response.body.success).toBeFalsy() | ||
expect(response.body.message).toEqual("username and password must be both provided") | ||
}) | ||
@@ -71,5 +82,5 @@ }) | ||
expect(response.body.success).toBeFalsy() | ||
expect(response.body.message).toEqual("Invalid username/password") | ||
expect(response.body.message).toEqual("Invalid Credentials") | ||
expect(response.body.user).toBeUndefined() | ||
user = User.find(u => { return u.uid === "gauss" }) | ||
user = User.find(u => { return u.username === "gauss" }) | ||
expect(user).toBeUndefined() | ||
@@ -76,0 +87,0 @@ }) |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
24108
12
411
158
3
+ Addedldap-authentication@^2.1.1
+ Addedpassport-custom@^1.1.0
+ Addedabstract-logging@2.0.1(transitive)
+ Addedasn1@0.2.6(transitive)
+ Addedextsprintf@1.4.1(transitive)
+ Addedldap-authentication@2.3.3(transitive)
+ Addedldap-filter@0.3.3(transitive)
+ Addedldapjs@2.3.3(transitive)
+ Addedpassport-custom@1.1.1(transitive)
+ Addedsafer-buffer@2.1.2(transitive)
+ Addedvasync@2.2.1(transitive)
+ Addedverror@1.10.0(transitive)
- Removedpassport-ldapauth@^2.1.3
- Removed@types/body-parser@1.19.5(transitive)
- Removed@types/connect@3.4.38(transitive)
- Removed@types/express@5.0.0(transitive)
- Removed@types/express-serve-static-core@5.0.2(transitive)
- Removed@types/http-errors@2.0.4(transitive)
- Removed@types/ldapjs@1.0.11(transitive)
- Removed@types/mime@1.3.5(transitive)
- Removed@types/node@22.9.3(transitive)
- Removed@types/passport@1.0.17(transitive)
- Removed@types/qs@6.9.17(transitive)
- Removed@types/range-parser@1.2.7(transitive)
- Removed@types/send@0.17.4(transitive)
- Removed@types/serve-static@1.15.7(transitive)
- Removedasn1@0.2.3(transitive)
- Removedassert-plus@0.1.5(transitive)
- Removedbalanced-match@1.0.2(transitive)
- Removedbcryptjs@2.4.3(transitive)
- Removedbrace-expansion@1.1.11(transitive)
- Removedbunyan@1.8.15(transitive)
- Removedconcat-map@0.0.1(transitive)
- Removeddashdash@1.14.1(transitive)
- Removeddtrace-provider@0.8.8(transitive)
- Removedextsprintf@1.2.0(transitive)
- Removedglob@6.0.4(transitive)
- Removedinflight@1.0.6(transitive)
- Removedinherits@2.0.4(transitive)
- Removedldap-filter@0.2.2(transitive)
- Removedldapauth-fork@4.3.3(transitive)
- Removedldapjs@1.0.2(transitive)
- Removedlru-cache@5.1.1(transitive)
- Removedminimatch@3.1.2(transitive)
- Removedminimist@1.2.8(transitive)
- Removedmkdirp@0.5.6(transitive)
- Removedmoment@2.30.1(transitive)
- Removedmv@2.1.1(transitive)
- Removednan@2.22.0(transitive)
- Removedncp@2.0.0(transitive)
- Removedpassport-ldapauth@2.1.4(transitive)
- Removedpath-is-absolute@1.0.1(transitive)
- Removedrimraf@2.4.5(transitive)
- Removedsafe-json-stringify@1.2.0(transitive)
- Removedundici-types@6.19.8(transitive)
- Removedvasync@1.6.4(transitive)
- Removedverror@1.6.0(transitive)
- Removedyallist@3.1.1(transitive)