]> git.immae.eu Git - perso/Immae/Projets/Nodejs/Surfer.git/commitdiff
Consolidate user verification
authorJohannes Zellner <johannes@cloudron.io>
Sat, 23 Feb 2019 22:15:23 +0000 (23:15 +0100)
committerJohannes Zellner <johannes@cloudron.io>
Sat, 23 Feb 2019 22:15:23 +0000 (23:15 +0100)
package-lock.json
package.json
src/auth.js

index 8535c99320bd490f7a179ad93c4638de6a6fe045..c66f538363775debe0e49cdfb7e9ad3a7aba7616 100644 (file)
@@ -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",
       "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",
       "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",
       }
     },
     "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": {
       "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",
       "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",
       "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",
       "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": {
         }
       }
     },
+    "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",
       }
     },
     "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": {
       "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",
         "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",
         "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",
       "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",
       "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": {
index 0ec6b5081769f48047a9a363caca5edbfb9339ba..6cf0187476573fe039722f3e307e1e7dbdd22bd3 100644 (file)
     "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",
index 67c20505d0e9e8364349336ea864fa770d75217c..e148fb75043529c3049b98d6dd4595d8929ca801 100644 (file)
@@ -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
         });
     });
 };