diff options
author | Johannes Zellner <johannes@cloudron.io> | 2019-02-23 23:15:23 +0100 |
---|---|---|
committer | Johannes Zellner <johannes@cloudron.io> | 2019-02-23 23:15:23 +0100 |
commit | 47ba3ae4ff8e5a49b06de5bfea908bf6a0c599d6 (patch) | |
tree | 617213afba0a65b4d56d49993a853569ba5523d5 /src | |
parent | 7af3d8556de81996d476d92807928fafdc91c41b (diff) | |
download | Surfer-47ba3ae4ff8e5a49b06de5bfea908bf6a0c599d6.tar.gz Surfer-47ba3ae4ff8e5a49b06de5bfea908bf6a0c599d6.tar.zst Surfer-47ba3ae4ff8e5a49b06de5bfea908bf6a0c599d6.zip |
Consolidate user verification
Diffstat (limited to 'src')
-rw-r--r-- | src/auth.js | 107 |
1 files changed, 60 insertions, 47 deletions
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'), | |||
7 | bcrypt = require('bcryptjs'), | 7 | bcrypt = require('bcryptjs'), |
8 | uuid = require('uuid/v4'), | 8 | uuid = require('uuid/v4'), |
9 | BearerStrategy = require('passport-http-bearer').Strategy, | 9 | BearerStrategy = require('passport-http-bearer').Strategy, |
10 | LdapStrategy = require('passport-ldapjs').Strategy, | 10 | ldapjs = require('ldapjs'), |
11 | HttpError = require('connect-lastmile').HttpError, | 11 | HttpError = require('connect-lastmile').HttpError, |
12 | HttpSuccess = require('connect-lastmile').HttpSuccess, | 12 | HttpSuccess = require('connect-lastmile').HttpSuccess, |
13 | webdavErrors = require('webdav-server').v2.Errors; | 13 | webdavErrors = require('webdav-server').v2.Errors; |
14 | 14 | ||
15 | const LDAP_URL = process.env.LDAP_URL; | ||
16 | const LDAP_USERS_BASE_DN = process.env.LDAP_USERS_BASE_DN; | ||
15 | const LOCAL_AUTH_FILE = path.resolve(process.env.LOCAL_AUTH_FILE || './.users.json'); | 17 | const LOCAL_AUTH_FILE = path.resolve(process.env.LOCAL_AUTH_FILE || './.users.json'); |
16 | const TOKENSTORE_FILE = path.resolve(process.env.TOKENSTORE_FILE || './.tokens.json'); | 18 | const TOKENSTORE_FILE = path.resolve(process.env.TOKENSTORE_FILE || './.tokens.json'); |
19 | const AUTH_METHOD = (LDAP_URL && LDAP_USERS_BASE_DN) ? 'ldap' : 'local'; | ||
20 | |||
21 | if (AUTH_METHOD === 'ldap') { | ||
22 | console.log('Use ldap auth'); | ||
23 | } else { | ||
24 | console.log(`Use local auth file ${LOCAL_AUTH_FILE}`); | ||
25 | } | ||
17 | 26 | ||
18 | var tokenStore = { | 27 | var tokenStore = { |
19 | data: {}, | 28 | data: {}, |
@@ -68,54 +77,62 @@ passport.deserializeUser(function (id, done) { | |||
68 | done(null, { uid: id }); | 77 | done(null, { uid: id }); |
69 | }); | 78 | }); |
70 | 79 | ||
71 | var LDAP_URL = process.env.LDAP_URL; | 80 | function verifyUser(username, password, callback) { |
72 | var LDAP_USERS_BASE_DN = process.env.LDAP_USERS_BASE_DN; | 81 | if (AUTH_METHOD === 'ldap') { |
82 | var ldapClient = ldapjs.createClient({ url: process.env.LDAP_URL }); | ||
83 | ldapClient.on('error', function (error) { | ||
84 | console.error('LDAP error', error); | ||
85 | }); | ||
73 | 86 | ||
74 | if (LDAP_URL && LDAP_USERS_BASE_DN) { | 87 | ldapClient.bind(process.env.LDAP_BIND_DN, process.env.LDAP_BIND_PASSWORD, function (error) { |
75 | console.log('Using ldap auth'); | 88 | if (error) return callback(error); |
76 | 89 | ||
77 | exports.login = [ passport.authenticate('ldap'), issueAccessToken() ]; | 90 | var filter = `(|(uid=${username})(mail=${username})(username=${username})(sAMAccountName=${username}))`; |
78 | } else { | 91 | ldapClient.search(process.env.LDAP_USERS_BASE_DN, { filter: filter }, function (error, result) { |
79 | console.log(`Using local user file: ${LOCAL_AUTH_FILE}`); | 92 | if (error) return callback(error); |
80 | 93 | ||
81 | exports.login = [ | 94 | var items = []; |
82 | function (req, res, next) { | ||
83 | var users = safe.JSON.parse(safe.fs.readFileSync(LOCAL_AUTH_FILE)); | ||
84 | if (!users) return res.send(401); | ||
85 | if (!users[req.body.username]) return res.send(401); | ||
86 | 95 | ||
87 | bcrypt.compare(req.body.password, users[req.body.username].passwordHash, function (error, valid) { | 96 | result.on('searchEntry', function(entry) { items.push(entry.object); }); |
88 | if (error || !valid) return res.send(401); | 97 | result.on('error', callback); |
98 | result.on('end', function (result) { | ||
99 | if (result.status !== 0 || items.length === 0) return callback(error); | ||
89 | 100 | ||
90 | req.user = { | 101 | // pick the first found |
91 | username: req.body.username | 102 | var user = items[0]; |
92 | }; | ||
93 | 103 | ||
94 | next(); | 104 | ldapClient.bind(user.dn, password, function (error) { |
105 | if (error) return callback('Invalid credentials'); | ||
106 | |||
107 | callback(null, { username: username }); | ||
108 | }); | ||
109 | }); | ||
95 | }); | 110 | }); |
96 | }, | 111 | }); |
97 | issueAccessToken() | 112 | } else { |
98 | ]; | 113 | var users = safe.JSON.parse(safe.fs.readFileSync(LOCAL_AUTH_FILE)); |
114 | if (!users || !users[username]) return callback('Invalid credentials'); | ||
115 | |||
116 | bcrypt.compare(password, users[username].passwordHash, function (error, valid) { | ||
117 | if (error || !valid) return callback('Invalid credentials'); | ||
118 | |||
119 | callback(null, { username: username }); | ||
120 | }); | ||
121 | } | ||
99 | } | 122 | } |
100 | 123 | ||
101 | var opts = { | 124 | exports.login = [ |
102 | server: { | 125 | function (req, res, next) { |
103 | url: LDAP_URL, | 126 | verifyUser(req.body.username, req.body.password, function (error, user) { |
104 | }, | 127 | if (error) return next(new HttpError(401, 'Invalid credentials')); |
105 | base: LDAP_USERS_BASE_DN, | ||
106 | search: { | ||
107 | filter: '(|(username={{username}})(mail={{username}}))', | ||
108 | attributes: ['displayname', 'username', 'mail', 'uid'], | ||
109 | scope: 'sub' | ||
110 | }, | ||
111 | uidTag: 'cn', | ||
112 | usernameField: 'username', | ||
113 | passwordField: 'password', | ||
114 | }; | ||
115 | 128 | ||
116 | passport.use(new LdapStrategy(opts, function (profile, done) { | 129 | req.user = user; |
117 | done(null, profile); | 130 | |
118 | })); | 131 | next(); |
132 | }); | ||
133 | }, | ||
134 | issueAccessToken() | ||
135 | ]; | ||
119 | 136 | ||
120 | exports.verify = passport.authenticate('bearer', { session: false }); | 137 | exports.verify = passport.authenticate('bearer', { session: false }); |
121 | 138 | ||
@@ -162,18 +179,14 @@ WebdavUserManager.prototype.getDefaultUser = function (callback) { | |||
162 | }; | 179 | }; |
163 | 180 | ||
164 | WebdavUserManager.prototype.getUserByNamePassword = function (username, password, callback) { | 181 | WebdavUserManager.prototype.getUserByNamePassword = function (username, password, callback) { |
165 | var users = safe.JSON.parse(safe.fs.readFileSync(LOCAL_AUTH_FILE)); | 182 | verifyUser(username, password, function (error, user) { |
166 | if (!users) return callback(webdavErrors.UserNotFound); | 183 | if (error) return callback(webdavErrors.UserNotFound); |
167 | if (!users[username]) return callback(webdavErrors.UserNotFound); | ||
168 | |||
169 | bcrypt.compare(password, users[username].passwordHash, function (error, valid) { | ||
170 | if (error || !valid) return callback(webdavErrors.UserNotFound); | ||
171 | 184 | ||
172 | callback(null, { | 185 | callback(null, { |
173 | username: username, | 186 | username: user.username, |
174 | isAdministrator: true, | 187 | isAdministrator: true, |
175 | isDefaultUser: false, | 188 | isDefaultUser: false, |
176 | uid: username | 189 | uid: user.username |
177 | }); | 190 | }); |
178 | }); | 191 | }); |
179 | }; | 192 | }; |