asyncified setting and verifying the password
Signed-off-by: Claudius <opensource@amenthes.de>
This commit is contained in:
parent
df666dd214
commit
1d403e183d
5 changed files with 48 additions and 18 deletions
|
@ -7,7 +7,6 @@ Manual Installation
|
||||||
- Database (PostgreSQL, MySQL, MariaDB, SQLite, MSSQL) use charset `utf8`
|
- Database (PostgreSQL, MySQL, MariaDB, SQLite, MSSQL) use charset `utf8`
|
||||||
- npm (and its dependencies, [node-gyp](https://github.com/nodejs/node-gyp#installation))
|
- npm (and its dependencies, [node-gyp](https://github.com/nodejs/node-gyp#installation))
|
||||||
- yarn
|
- yarn
|
||||||
- `libssl-dev` for building scrypt (see [here](https://github.com/ml1nk/node-scrypt/blob/master/README.md#installation-instructions) for further information)
|
|
||||||
- Bash (for the setup script)
|
- Bash (for the setup script)
|
||||||
- For **building** CodiMD we recommend to use a machine with at least **2GB** RAM
|
- For **building** CodiMD we recommend to use a machine with at least **2GB** RAM
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
'use strict'
|
'use strict'
|
||||||
// external modules
|
// external modules
|
||||||
var Sequelize = require('sequelize')
|
var Sequelize = require('sequelize')
|
||||||
var scrypt = require('@mlink/scrypt')
|
var scrypt = require('scrypt-kdf')
|
||||||
|
|
||||||
// core
|
// core
|
||||||
var logger = require('../logger')
|
var logger = require('../logger')
|
||||||
|
@ -46,11 +46,7 @@ module.exports = function (sequelize, DataTypes) {
|
||||||
}, {
|
}, {
|
||||||
instanceMethods: {
|
instanceMethods: {
|
||||||
verifyPassword: function (attempt) {
|
verifyPassword: function (attempt) {
|
||||||
if (scrypt.verifyKdfSync(Buffer.from(this.password, 'hex'), attempt)) {
|
return scrypt.verify(Buffer.from(this.password, 'hex'), attempt)
|
||||||
return this
|
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
classMethods: {
|
classMethods: {
|
||||||
|
@ -153,9 +149,11 @@ module.exports = function (sequelize, DataTypes) {
|
||||||
// suggested way to hash passwords to be able to do this asynchronously:
|
// suggested way to hash passwords to be able to do this asynchronously:
|
||||||
// @see https://github.com/sequelize/sequelize/issues/1821#issuecomment-44265819
|
// @see https://github.com/sequelize/sequelize/issues/1821#issuecomment-44265819
|
||||||
if (!user.changed('password')) { return done() }
|
if (!user.changed('password')) { return done() }
|
||||||
const hash = scrypt.kdfSync(user.get('password'), scrypt.paramsSync(0.1)).toString('hex')
|
|
||||||
user.setDataValue('password', hash)
|
scrypt.kdf(user.getDataValue('password'), { logN: 15 }).then(keyBuf => {
|
||||||
done()
|
user.setDataValue('password', keyBuf.toString('hex'))
|
||||||
|
done()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
User.beforeCreate(updatePasswordHashHook)
|
User.beforeCreate(updatePasswordHashHook)
|
||||||
|
|
|
@ -23,8 +23,14 @@ passport.use(new LocalStrategy({
|
||||||
}
|
}
|
||||||
}).then(function (user) {
|
}).then(function (user) {
|
||||||
if (!user) return done(null, false)
|
if (!user) return done(null, false)
|
||||||
if (!user.verifyPassword(password)) return done(null, false)
|
user.verifyPassword(password).then(verified => {
|
||||||
return done(null, user)
|
if (verified) {
|
||||||
|
return done(null, user)
|
||||||
|
} else {
|
||||||
|
logger.warn('invalid password given for %s', user.email)
|
||||||
|
return done(null, false)
|
||||||
|
}
|
||||||
|
})
|
||||||
}).catch(function (err) {
|
}).catch(function (err) {
|
||||||
logger.error(err)
|
logger.error(err)
|
||||||
return done(err)
|
return done(err)
|
||||||
|
|
|
@ -58,7 +58,6 @@
|
||||||
"jquery-ui": "^1.12.1",
|
"jquery-ui": "^1.12.1",
|
||||||
"js-cookie": "^2.1.3",
|
"js-cookie": "^2.1.3",
|
||||||
"js-sequence-diagrams": "git+https://github.com/codimd/js-sequence-diagrams.git",
|
"js-sequence-diagrams": "git+https://github.com/codimd/js-sequence-diagrams.git",
|
||||||
"wurl": "^2.5.3",
|
|
||||||
"js-yaml": "^3.13.1",
|
"js-yaml": "^3.13.1",
|
||||||
"jsdom-nogyp": "^0.8.3",
|
"jsdom-nogyp": "^0.8.3",
|
||||||
"keymaster": "^1.6.2",
|
"keymaster": "^1.6.2",
|
||||||
|
@ -111,7 +110,7 @@
|
||||||
"readline-sync": "^1.4.7",
|
"readline-sync": "^1.4.7",
|
||||||
"request": "^2.88.0",
|
"request": "^2.88.0",
|
||||||
"reveal.js": "~3.7.0",
|
"reveal.js": "~3.7.0",
|
||||||
"@mlink/scrypt": "^6.1.2",
|
"scrypt-kdf": "^2.0.1",
|
||||||
"select2": "^3.5.2-browserify",
|
"select2": "^3.5.2-browserify",
|
||||||
"sequelize": "^3.28.0",
|
"sequelize": "^3.28.0",
|
||||||
"sequelize-cli": "^2.5.1",
|
"sequelize-cli": "^2.5.1",
|
||||||
|
@ -132,6 +131,7 @@
|
||||||
"viz.js": "^1.7.0",
|
"viz.js": "^1.7.0",
|
||||||
"winston": "^3.1.0",
|
"winston": "^3.1.0",
|
||||||
"ws": "^6.0.0",
|
"ws": "^6.0.0",
|
||||||
|
"wurl": "^2.5.3",
|
||||||
"xss": "^1.0.3"
|
"xss": "^1.0.3"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
|
|
35
test/user.js
35
test/user.js
|
@ -19,8 +19,10 @@ describe('User Sequelize model', function () {
|
||||||
const intentionallyInvalidPassword = 'stuff'
|
const intentionallyInvalidPassword = 'stuff'
|
||||||
|
|
||||||
return User.create(userData).then(u => {
|
return User.create(userData).then(u => {
|
||||||
assert(u.verifyPassword(userData.password))
|
return Promise.all([
|
||||||
assert(!u.verifyPassword(intentionallyInvalidPassword))
|
u.verifyPassword(userData.password).then(result => assert.strictEqual(result, true)),
|
||||||
|
u.verifyPassword(intentionallyInvalidPassword).then(result => assert.strictEqual(result, false))
|
||||||
|
]).catch(e => assert.fail(e))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -31,7 +33,32 @@ describe('User Sequelize model', function () {
|
||||||
|
|
||||||
const u = User.build()
|
const u = User.build()
|
||||||
u.setDataValue('password', testKey) // this circumvents the setter - which we don't need in this case!
|
u.setDataValue('password', testKey) // this circumvents the setter - which we don't need in this case!
|
||||||
assert(u.verifyPassword(validPassword))
|
return Promise.all([
|
||||||
assert(!u.verifyPassword(intentionallyInvalidPassword))
|
u.verifyPassword(validPassword).then(result => assert.strictEqual(result, true)),
|
||||||
|
u.verifyPassword(intentionallyInvalidPassword).then(result => assert.strictEqual(result, false))
|
||||||
|
]).catch(e => assert.fail(e))
|
||||||
|
})
|
||||||
|
|
||||||
|
it('deals with various characters correctly', function () {
|
||||||
|
const combinations = [
|
||||||
|
// ['correct password', 'scrypt syle hash']
|
||||||
|
['test', '736372797074000e00000008000000018c7b8c1ac273fd339badde759b3efc418bc61b776debd02dfe95989383cf9980ad21d2403dce33f4b551f5e98ce84edb792aee62600b1303ab8d4e6f0a53b0746e73193dbf557b888efc83a2d6a055a9'],
|
||||||
|
['my secret pw', /* different hash! */ '736372797074000f0000000800000001f6083e9593365acd07550f7c72f19973fb7d52c3ef0a78026ff66c48ab14493843c642167b5e6b7f31927e8eeb912bc2639e41955fae15da5099998948cfeacd022f705624931c3b30104e6bb296b805'],
|
||||||
|
['ohai', '736372797074000e00000008000000010efec4e5ce6a5294491f1b1cccc38d3562f84844b9271aef635f8bc338cf4e0e0bac62ebb11379e85894c1f694e038fc39b087b4fdacd1280b50a7382d7ffbfc82f2190bef70d47708d2a94b75126294'],
|
||||||
|
['my secret pw', '736372797074000f0000000800000001ffb4cd10a1dfe9e64c1e5416fd6d55b390b6822e78b46fd1f963fe9f317a1e05f9c5fee15e1f618286f4e38b55364ae1e7dc295c9dc33ee0f5712e86afe37e5784ff9c7cf84cf0e631dd11f84f3621e7'],
|
||||||
|
['i am so extremely long, it\'s not even funny. Wait, you\'re still reading?', '736372797074000f00000008000000012d205f7bb529bb3a8b8bb25f5ab46197c7e9baf1aad64cf5e7b2584c84748cacf5e60631d58d21cb51fa34ea93b517e2fe2eb722931db5a70ff5a1330d821288ee7380c4136369f064b71b191a785a5b']
|
||||||
|
]
|
||||||
|
const intentionallyInvalidPassword = 'stuff'
|
||||||
|
|
||||||
|
return Promise.all(combinations.map((combination, index) => {
|
||||||
|
const u = User.build()
|
||||||
|
u.setDataValue('password', combination[1])
|
||||||
|
return Promise.all([
|
||||||
|
u.verifyPassword(combination[0])
|
||||||
|
.then(result => assert.strictEqual(result, true, `password #${index} "${combination[0]}" should have been verified`)),
|
||||||
|
u.verifyPassword(intentionallyInvalidPassword)
|
||||||
|
.then(result => assert.strictEqual(result, false, `password #${index} "${combination[0]}" should NOT have been verified`))
|
||||||
|
])
|
||||||
|
})).catch(e => assert.fail(e))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in a new issue