From 9af735d4741434a8460c3b9f69cf92fcaa377d68 Mon Sep 17 00:00:00 2001 From: Johannes Zellner Date: Thu, 11 Aug 2016 11:23:01 +0200 Subject: [PATCH 1/9] Add passport-ldapjs node module --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 4e86b566..2721d171 100644 --- a/package.json +++ b/package.json @@ -65,6 +65,7 @@ "npmlog": "^4.0.0", "openpgp": "^2.3.2", "passport": "^0.3.2", + "passport-ldapjs": "^1.0.2", "passport-local": "^1.0.0", "request": "^2.74.0", "serve-favicon": "^2.3.0", From dff9996fda9cc17672aa2b4c44f3218117002ead Mon Sep 17 00:00:00 2001 From: Johannes Zellner Date: Thu, 11 Aug 2016 13:01:02 +0200 Subject: [PATCH 2/9] Add users.findByUsername() --- lib/models/users.js | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/lib/models/users.js b/lib/models/users.js index 62f8c10e..27c218be 100644 --- a/lib/models/users.js +++ b/lib/models/users.js @@ -61,6 +61,29 @@ module.exports.findByAccessToken = (accessToken, callback) => { }); }; +module.exports.findByUsername = (username, callback) => { + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + connection.query('SELECT `id`, `username`, `email`, `access_token` FROM `users` WHERE `username`=? LIMIT 1', [username], (err, rows) => { + connection.release(); + + if (err) { + return callback(err); + } + + if (!rows.length) { + return callback(null, false); + } + + let user = tools.convertKeys(rows[0]); + return callback(null, user); + }); + }); +}; + /** * Fetches user by username and password * From 3f86ad2945fafff5d6655283f6ac36d7fb579cd9 Mon Sep 17 00:00:00 2001 From: Johannes Zellner Date: Thu, 11 Aug 2016 13:15:51 +0200 Subject: [PATCH 3/9] Add users.add() --- lib/models/users.js | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/lib/models/users.js b/lib/models/users.js index 27c218be..d3e7bf51 100644 --- a/lib/models/users.js +++ b/lib/models/users.js @@ -84,6 +84,29 @@ module.exports.findByUsername = (username, callback) => { }); }; +module.exports.add = (username, password, email, callback) => { + db.getConnection((err, connection) => { + if (err) { + return callback(err); + } + + connection.query('INSERT INTO `users` (`username`, `password`, `email`, `created`) VALUES (?, ?, ?, NOW())', [username, password, email], (err, result) => { + connection.release(); + + if (err) { + return callback(err); + } + + let id = result && result.insertId; + if (!id) { + return callback(new Error('Could not store user row')); + } + + return callback(null, id); + }); + }); +}; + /** * Fetches user by username and password * From 65c2e9db988d2b12e5b7852d7c9c44e1891ab53b Mon Sep 17 00:00:00 2001 From: Johannes Zellner Date: Thu, 11 Aug 2016 13:21:21 +0200 Subject: [PATCH 4/9] Add ldap section to default configuration --- config/default.toml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/config/default.toml b/config/default.toml index 66aa7fc3..a13295f4 100644 --- a/config/default.toml +++ b/config/default.toml @@ -87,3 +87,11 @@ host="0.0.0.0" username="testuser" password="testpass" logger=false + +[ldap] +# enable to use ldap user backend +enabled=false +host="localhost" +port=3002 +baseDN="ou=users,dc=company" +filter="(|(username={{username}})(mail={{username}}))" From 4f3b2e9dcb427d7786384ab3b757b00b02da5f65 Mon Sep 17 00:00:00 2001 From: Johannes Zellner Date: Thu, 11 Aug 2016 13:21:48 +0200 Subject: [PATCH 5/9] Add ldap authentication --- lib/passport.js | 74 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 61 insertions(+), 13 deletions(-) diff --git a/lib/passport.js b/lib/passport.js index 111f24a1..b1e6b737 100644 --- a/lib/passport.js +++ b/lib/passport.js @@ -1,8 +1,11 @@ 'use strict'; let config = require('config'); +let log = require('npmlog'); + let passport = require('passport'); let LocalStrategy = require('passport-local').Strategy; +let LdapStrategy = require('passport-ldapjs').Strategy; let csrf = require('csurf'); let bodyParser = require('body-parser'); let users = require('./models/users'); @@ -30,7 +33,7 @@ module.exports.logout = (req, res) => { }; module.exports.login = (req, res, next) => { - passport.authenticate('local', (err, user, info) => { + passport.authenticate(config.ldap.enabled ? 'ldap' : 'local', (err, user, info) => { if (err) { req.flash('danger', err.message); return next(err); @@ -58,21 +61,66 @@ module.exports.login = (req, res, next) => { })(req, res, next); }; -passport.use(new LocalStrategy((username, password, done) => { - users.authenticate(username, password, (err, user) => { - if (err) { - return done(err); - } +if (config.ldap.enabled) { + log.info('Using LDAP auth'); - if (!user) { - return done(null, false, { - message: 'Incorrect username or password' - }); + var opts = { + server: { + url: 'ldap://' + config.ldap.host + ':' + config.ldap.port, + }, + base: config.ldap.baseDN, + search: { + filter: config.ldap.filter, + attributes: ['username', 'mail'], + scope: 'sub' } + }; - return done(null, user); - }); -})); + passport.use(new LdapStrategy(opts, function (profile, done) { + users.findByUsername(profile.username, (err, user) => { + if (err) { + return done(err); + } + + if (!user) { + // password is empty for ldap + users.add(profile.username, '', profile.mail, (err, id) => { + if (err) { + return done(err); + } + + return done(null, { + id: id, + username: profile.username + }); + }); + } else { + return done(null, { + id: user.id, + username: user.username + }); + } + }); + })); +} else { + log.info('Using local auth'); + + passport.use(new LocalStrategy((username, password, done) => { + users.authenticate(username, password, (err, user) => { + if (err) { + return done(err); + } + + if (!user) { + return done(null, false, { + message: 'Incorrect username or password' + }); + } + + return done(null, user); + }); + })); +} passport.serializeUser((user, done) => { done(null, user.id); From eed8616dbbcd38fc7c75c640bef4371274b4483e Mon Sep 17 00:00:00 2001 From: Johannes Zellner Date: Thu, 11 Aug 2016 14:41:30 +0200 Subject: [PATCH 6/9] Add ldap.passwordresetlink to configuration file --- config/default.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/config/default.toml b/config/default.toml index a13295f4..f5a91368 100644 --- a/config/default.toml +++ b/config/default.toml @@ -95,3 +95,4 @@ host="localhost" port=3002 baseDN="ou=users,dc=company" filter="(|(username={{username}})(mail={{username}}))" +passwordresetlink="" From b9f4b286c5f3c465463e323696de7cde26fe61b5 Mon Sep 17 00:00:00 2001 From: Johannes Zellner Date: Thu, 11 Aug 2016 14:44:09 +0200 Subject: [PATCH 7/9] Make minimal ldap configs available for html templates --- app.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app.js b/app.js index 062a2532..d6b05349 100644 --- a/app.js +++ b/app.js @@ -135,6 +135,10 @@ passport.setup(app); app.use((req, res, next) => { res.locals.flash = req.flash.bind(req); res.locals.user = req.user; + res.locals.ldap = { + enabled: config.ldap.enabled, + passwordresetlink: config.ldap.passwordresetlink + }; let menu = [{ title: 'Home', From 67fc7939df272bf068ca3d462889ca69a732e69c Mon Sep 17 00:00:00 2001 From: Johannes Zellner Date: Thu, 11 Aug 2016 14:44:43 +0200 Subject: [PATCH 8/9] Hide/show account management ui based on authentication mode --- views/users/account.hbs | 104 +++++++++++++++++++++------------------- views/users/forgot.hbs | 36 ++++++++------ views/users/login.hbs | 7 ++- 3 files changed, 84 insertions(+), 63 deletions(-) diff --git a/views/users/account.hbs b/views/users/account.hbs index 352ce1dc..1fc6724a 100644 --- a/views/users/account.hbs +++ b/views/users/account.hbs @@ -7,58 +7,66 @@
-
+{{#if ldap.enabled}} +

+ This account is managed through LDAP.
+
+ Associated Email Address: {{email}} +

+{{else}} + - + -
- - General Settings - +
+ + General Settings + + +
+ +
+ + This address is used for account recovery in case you loose your password +
+
+ +
+
+ + Password change + + +

+ You only need to fill out this form if you want to change your current password +

+ +
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+ +
- -
- - This address is used for account recovery in case you loose your password +
+
-
-
- - Password change - - -

- You only need to fill out this form if you want to change your current password -

- -
- -
- -
-
-
- -
- -
-
-
- -
- -
-
- -
- -
-
- -
-
- -
+ +{{/if}} diff --git a/views/users/forgot.hbs b/views/users/forgot.hbs index 369b3168..a38a3325 100644 --- a/views/users/forgot.hbs +++ b/views/users/forgot.hbs @@ -8,22 +8,30 @@
-

Please provide the username or email address that you used when you signed up for your Mailtrain account.

-

We will send you an email that will allow you to reset your password.

+{{#if ldap.enabled}} +

+ Accounts are managed through LDAP.
+
+ Reset Password +

+{{else}} +

Please provide the username or email address that you used when you signed up for your Mailtrain account.

+

We will send you an email that will allow you to reset your password.

-
+ - + -
- -
- +
+ +
+ +
-
-
-
- +
+
+ +
-
- + +{{/if}} diff --git a/views/users/login.hbs b/views/users/login.hbs index 5beaa470..bf88eef9 100644 --- a/views/users/login.hbs +++ b/views/users/login.hbs @@ -32,7 +32,12 @@
- or Forgot password? + or + {{#if ldap.enabled}} + Forgot password? + {{else}} + Forgot password? + {{/if}}
From 2c387351c63cae3434c068b523261f5e89732b1d Mon Sep 17 00:00:00 2001 From: Johannes Zellner Date: Thu, 11 Aug 2016 14:46:52 +0200 Subject: [PATCH 9/9] Deny access for empty password authentication attempts --- lib/models/users.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/models/users.js b/lib/models/users.js index d3e7bf51..10e92784 100644 --- a/lib/models/users.js +++ b/lib/models/users.js @@ -116,6 +116,10 @@ module.exports.add = (username, password, email, callback) => { */ module.exports.authenticate = (username, password, callback) => { + if (password === '') { + return callback(null, false); + } + let login = (connection, callback) => { connection.query('SELECT `id`, `password`, `access_token` FROM `users` WHERE `username`=? OR email=? LIMIT 1', [username, username], (err, rows) => { if (err) {