From: Johannes Zellner Date: Sat, 23 Feb 2019 22:15:23 +0000 (+0100) Subject: Consolidate user verification X-Git-Url: https://git.immae.eu/?p=perso%2FImmae%2FProjets%2FNodejs%2FSurfer.git;a=commitdiff_plain;h=47ba3ae4ff8e5a49b06de5bfea908bf6a0c599d6 Consolidate user verification --- diff --git a/package-lock.json b/package-lock.json index 8535c99..c66f538 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,11 +4,6 @@ "lockfileVersion": 1, "requires": true, "dependencies": { - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" - }, "accepts": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.4.tgz", @@ -74,14 +69,14 @@ "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=" }, "asn1": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.1.tgz", - "integrity": "sha1-7Mc/ddMeo8btnUdCjbNf7Meyxtw=" + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", + "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" }, "assert-plus": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz", - "integrity": "sha1-7nQAlBMALYTOxyGcasgRgS5yMWA=" + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" }, "async": { "version": "1.5.2", @@ -103,6 +98,14 @@ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=" }, + "backoff": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/backoff/-/backoff-2.5.0.tgz", + "integrity": "sha1-9hbtqdPktmuMp/ynn2lXIsX44m8=", + "requires": { + "precond": "0.2" + } + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -195,12 +198,14 @@ } }, "bunyan": { - "version": "0.22.1", - "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-0.22.1.tgz", - "integrity": "sha1-Agw4O+1iWvXGyINN2MSsoN0Pdlw=", + "version": "1.8.12", + "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-1.8.12.tgz", + "integrity": "sha1-8VDw9nSKvdcq6uhPBEA74u8RN5c=", "requires": { - "dtrace-provider": "0.2.8", - "mv": "0.0.5" + "dtrace-provider": "~0.8", + "moment": "^2.10.6", + "mv": "~2", + "safe-json-stringify": "~1" } }, "bytes": { @@ -430,10 +435,13 @@ "dev": true }, "dtrace-provider": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.2.8.tgz", - "integrity": "sha1-4kPxkhmqlfvw2PL/sH9b1k6U/iA=", - "optional": true + "version": "0.8.7", + "resolved": "https://registry.npmjs.org/dtrace-provider/-/dtrace-provider-0.8.7.tgz", + "integrity": "sha1-3JObTT4GIM/gwc2APQ0tftBP/QQ=", + "optional": true, + "requires": { + "nan": "^2.10.0" + } }, "ecc-jsbn": { "version": "0.1.1", @@ -554,9 +562,9 @@ "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" }, "extsprintf": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.0.0.tgz", - "integrity": "sha1-TVi4Fazlvr/E6/A8+YsKdgSpm4Y=" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.2.0.tgz", + "integrity": "sha1-WtlGwi9bMrp/jNdCZxHG6KP8JSk=" }, "fast-deep-equal": { "version": "1.0.0", @@ -861,11 +869,6 @@ "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", "optional": true }, - "json-schema": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.2.tgz", - "integrity": "sha1-UDVPGfYDkXxpX3C4Wvp3w7DyNQY=" - }, "json-schema-traverse": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", @@ -876,37 +879,36 @@ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" }, - "jsprim": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-0.3.0.tgz", - "integrity": "sha1-zRNGbqJIDb2DlqVw1H0x3aR2+LE=", + "ldap-filter": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/ldap-filter/-/ldap-filter-0.2.2.tgz", + "integrity": "sha1-8rhCvguG2jNSeYUFsx68rlkNd9A=", "requires": { - "extsprintf": "1.0.0", - "json-schema": "0.2.2", - "verror": "1.3.3" + "assert-plus": "0.1.5" }, "dependencies": { - "verror": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.3.3.tgz", - "integrity": "sha1-impKw6jHdLb2h/7OSb3/14VS4s0=", - "requires": { - "extsprintf": "1.0.0" - } + "assert-plus": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz", + "integrity": "sha1-7nQAlBMALYTOxyGcasgRgS5yMWA=" } } }, "ldapjs": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/ldapjs/-/ldapjs-0.7.1.tgz", - "integrity": "sha1-aEeYpodkC6sa+9gCz1MvMEkt+1Y=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/ldapjs/-/ldapjs-1.0.2.tgz", + "integrity": "sha1-VE/3Ayt7g8aPBwEyjZKXqmlDQPk=", "requires": { - "asn1": "0.2.1", - "assert-plus": "0.1.5", - "bunyan": "0.22.1", - "dtrace-provider": "0.2.8", - "nopt": "2.1.1", - "pooling": "0.4.6" + "asn1": "0.2.3", + "assert-plus": "^1.0.0", + "backoff": "^2.5.0", + "bunyan": "^1.8.3", + "dashdash": "^1.14.0", + "dtrace-provider": "~0.8", + "ldap-filter": "0.2.2", + "once": "^1.4.0", + "vasync": "^1.6.4", + "verror": "^1.8.1" } }, "lru-cache": { @@ -1030,6 +1032,12 @@ } } }, + "moment": { + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", + "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==", + "optional": true + }, "morgan": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.0.tgz", @@ -1063,9 +1071,50 @@ } }, "mv": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/mv/-/mv-0.0.5.tgz", - "integrity": "sha1-FerHWUeYhN8RMdbeVrziC2VPU5E=", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", + "integrity": "sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI=", + "optional": true, + "requires": { + "mkdirp": "~0.5.1", + "ncp": "~2.0.0", + "rimraf": "~2.4.0" + }, + "dependencies": { + "glob": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", + "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", + "optional": true, + "requires": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "rimraf": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", + "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=", + "optional": true, + "requires": { + "glob": "^6.0.1" + } + } + } + }, + "nan": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz", + "integrity": "sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==", + "optional": true + }, + "ncp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", + "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", "optional": true }, "negotiator": { @@ -1073,14 +1122,6 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" }, - "nopt": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-2.1.1.tgz", - "integrity": "sha1-ket8SwF+fACtytH9bWOUTQ/bdcE=", - "requires": { - "abbrev": "1" - } - }, "oauth-sign": { "version": "0.8.2", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", @@ -1140,15 +1181,6 @@ "passport-strategy": "1.x.x" } }, - "passport-ldapjs": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/passport-ldapjs/-/passport-ldapjs-1.0.3.tgz", - "integrity": "sha512-pWyqehzK5IAtg53S6uIc9PHqgxL3xDcog3XDhtvidNd4+3z8XTGV2qQKPaUZnkkRLmWqZ7Dm3gnwnAtp6R1LNQ==", - "requires": { - "ldapjs": "^0.7.1", - "passport-strategy": "^1.0.0" - } - }, "passport-strategy": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", @@ -1202,24 +1234,10 @@ "pinkie": "^2.0.0" } }, - "pooling": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/pooling/-/pooling-0.4.6.tgz", - "integrity": "sha1-dqMXNx6oo2O0hY+keZ5gJF8w5mQ=", - "requires": { - "assert-plus": "0.1.5", - "bunyan": "0.22.1", - "dtrace-provider": "0.2.8", - "once": "1.3.0", - "vasync": "1.4.0" - }, - "dependencies": { - "once": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.3.0.tgz", - "integrity": "sha1-FRr4a/wfCMS58H0GqyUP/L61ZYE=" - } - } + "precond": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz", + "integrity": "sha1-qpWRvKokkj8eD0hJ0kD0fvwQdaw=" }, "proxy-addr": { "version": "2.0.2", @@ -1357,6 +1375,12 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" }, + "safe-json-stringify": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/safe-json-stringify/-/safe-json-stringify-1.2.0.tgz", + "integrity": "sha512-gH8eh2nZudPQO6TytOvbxnuhYBOvDBBLW52tz5q6X58lJcd/tkmqFR+5Z9adS8aJtURSXWThWy/xJtJwixErvg==", + "optional": true + }, "safetydance": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/safetydance/-/safetydance-0.1.1.tgz", @@ -1692,20 +1716,31 @@ "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" }, "vasync": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/vasync/-/vasync-1.4.0.tgz", - "integrity": "sha1-bqWmNYI1iGjYdDy91v+tyQg7kQ8=", + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/vasync/-/vasync-1.6.4.tgz", + "integrity": "sha1-3+k2Fq0OeugBszKp2Iv8XNyOHR8=", "requires": { - "jsprim": "0.3.0", - "verror": "1.1.0" + "verror": "1.6.0" + }, + "dependencies": { + "verror": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.6.0.tgz", + "integrity": "sha1-fROyex+swuLakEBetepuW90lLqU=", + "requires": { + "extsprintf": "1.2.0" + } + } } }, "verror": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.1.0.tgz", - "integrity": "sha1-KktOsUogcFHnWm+U7lExW/FzobA=", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", "requires": { - "extsprintf": "1.0.0" + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" } }, "webdav-server": { diff --git a/package.json b/package.json index 0ec6b50..6cf0187 100644 --- a/package.json +++ b/package.json @@ -33,12 +33,12 @@ "del": "^2.2.0", "express": "^4.16.2", "express-session": "^1.15.6", + "ldapjs": "^1.0.2", "mkdirp": "^0.5.1", "morgan": "^1.9.0", "multiparty": "^4.1.2", "passport": "^0.2.2", "passport-http-bearer": "^1.0.1", - "passport-ldapjs": "^1.0.3", "readline-sync": "^1.4.9", "request": "^2.83.0", "safetydance": "^0.1.1", diff --git a/src/auth.js b/src/auth.js index 67c2050..e148fb7 100644 --- a/src/auth.js +++ b/src/auth.js @@ -7,13 +7,22 @@ var passport = require('passport'), bcrypt = require('bcryptjs'), uuid = require('uuid/v4'), BearerStrategy = require('passport-http-bearer').Strategy, - LdapStrategy = require('passport-ldapjs').Strategy, + ldapjs = require('ldapjs'), HttpError = require('connect-lastmile').HttpError, HttpSuccess = require('connect-lastmile').HttpSuccess, webdavErrors = require('webdav-server').v2.Errors; +const LDAP_URL = process.env.LDAP_URL; +const LDAP_USERS_BASE_DN = process.env.LDAP_USERS_BASE_DN; const LOCAL_AUTH_FILE = path.resolve(process.env.LOCAL_AUTH_FILE || './.users.json'); const TOKENSTORE_FILE = path.resolve(process.env.TOKENSTORE_FILE || './.tokens.json'); +const AUTH_METHOD = (LDAP_URL && LDAP_USERS_BASE_DN) ? 'ldap' : 'local'; + +if (AUTH_METHOD === 'ldap') { + console.log('Use ldap auth'); +} else { + console.log(`Use local auth file ${LOCAL_AUTH_FILE}`); +} var tokenStore = { data: {}, @@ -68,54 +77,62 @@ passport.deserializeUser(function (id, done) { done(null, { uid: id }); }); -var LDAP_URL = process.env.LDAP_URL; -var LDAP_USERS_BASE_DN = process.env.LDAP_USERS_BASE_DN; +function verifyUser(username, password, callback) { + if (AUTH_METHOD === 'ldap') { + var ldapClient = ldapjs.createClient({ url: process.env.LDAP_URL }); + ldapClient.on('error', function (error) { + console.error('LDAP error', error); + }); -if (LDAP_URL && LDAP_USERS_BASE_DN) { - console.log('Using ldap auth'); + ldapClient.bind(process.env.LDAP_BIND_DN, process.env.LDAP_BIND_PASSWORD, function (error) { + if (error) return callback(error); - exports.login = [ passport.authenticate('ldap'), issueAccessToken() ]; -} else { - console.log(`Using local user file: ${LOCAL_AUTH_FILE}`); + var filter = `(|(uid=${username})(mail=${username})(username=${username})(sAMAccountName=${username}))`; + ldapClient.search(process.env.LDAP_USERS_BASE_DN, { filter: filter }, function (error, result) { + if (error) return callback(error); - exports.login = [ - function (req, res, next) { - var users = safe.JSON.parse(safe.fs.readFileSync(LOCAL_AUTH_FILE)); - if (!users) return res.send(401); - if (!users[req.body.username]) return res.send(401); + var items = []; - bcrypt.compare(req.body.password, users[req.body.username].passwordHash, function (error, valid) { - if (error || !valid) return res.send(401); + result.on('searchEntry', function(entry) { items.push(entry.object); }); + result.on('error', callback); + result.on('end', function (result) { + if (result.status !== 0 || items.length === 0) return callback(error); - req.user = { - username: req.body.username - }; + // pick the first found + var user = items[0]; - next(); + ldapClient.bind(user.dn, password, function (error) { + if (error) return callback('Invalid credentials'); + + callback(null, { username: username }); + }); + }); }); - }, - issueAccessToken() - ]; + }); + } else { + var users = safe.JSON.parse(safe.fs.readFileSync(LOCAL_AUTH_FILE)); + if (!users || !users[username]) return callback('Invalid credentials'); + + bcrypt.compare(password, users[username].passwordHash, function (error, valid) { + if (error || !valid) return callback('Invalid credentials'); + + callback(null, { username: username }); + }); + } } -var opts = { - server: { - url: LDAP_URL, - }, - base: LDAP_USERS_BASE_DN, - search: { - filter: '(|(username={{username}})(mail={{username}}))', - attributes: ['displayname', 'username', 'mail', 'uid'], - scope: 'sub' - }, - uidTag: 'cn', - usernameField: 'username', - passwordField: 'password', -}; +exports.login = [ + function (req, res, next) { + verifyUser(req.body.username, req.body.password, function (error, user) { + if (error) return next(new HttpError(401, 'Invalid credentials')); -passport.use(new LdapStrategy(opts, function (profile, done) { - done(null, profile); -})); + req.user = user; + + next(); + }); + }, + issueAccessToken() +]; exports.verify = passport.authenticate('bearer', { session: false }); @@ -162,18 +179,14 @@ WebdavUserManager.prototype.getDefaultUser = function (callback) { }; WebdavUserManager.prototype.getUserByNamePassword = function (username, password, callback) { - var users = safe.JSON.parse(safe.fs.readFileSync(LOCAL_AUTH_FILE)); - if (!users) return callback(webdavErrors.UserNotFound); - if (!users[username]) return callback(webdavErrors.UserNotFound); - - bcrypt.compare(password, users[username].passwordHash, function (error, valid) { - if (error || !valid) return callback(webdavErrors.UserNotFound); + verifyUser(username, password, function (error, user) { + if (error) return callback(webdavErrors.UserNotFound); callback(null, { - username: username, + username: user.username, isAdministrator: true, isDefaultUser: false, - uid: username + uid: user.username }); }); };