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', diff --git a/config/default.toml b/config/default.toml index 66aa7fc3..f5a91368 100644 --- a/config/default.toml +++ b/config/default.toml @@ -87,3 +87,12 @@ 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}}))" +passwordresetlink="" diff --git a/lib/models/users.js b/lib/models/users.js index 62f8c10e..10e92784 100644 --- a/lib/models/users.js +++ b/lib/models/users.js @@ -61,6 +61,52 @@ 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); + }); + }); +}; + +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 * @@ -70,6 +116,10 @@ module.exports.findByAccessToken = (accessToken, 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) { 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); diff --git a/package.json b/package.json index fbae6eda..780b699a 100644 --- a/package.json +++ b/package.json @@ -65,6 +65,7 @@ "npmlog": "^4.0.0", "openpgp": "^2.3.3", "passport": "^0.3.2", + "passport-ldapjs": "^1.0.2", "passport-local": "^1.0.0", "request": "^2.74.0", "serve-favicon": "^2.3.0", 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}}