fixed tests deleted obsolete ones. need moar token tests
This commit is contained in:
Родитель
830833f8a1
Коммит
f59f012e98
|
@ -105,7 +105,7 @@ module.exports = function (
|
||||||
|
|
||||||
Heap.prototype.createForgotPasswordToken = function (emailRecord) {
|
Heap.prototype.createForgotPasswordToken = function (emailRecord) {
|
||||||
log.trace({ op: 'Heap.createForgotPasswordToken', uid: emailRecord && emailRecord.uid })
|
log.trace({ op: 'Heap.createForgotPasswordToken', uid: emailRecord && emailRecord.uid })
|
||||||
return ForgotPasswordToken.create(emailRecord.uid)
|
return ForgotPasswordToken.create(emailRecord.uid, emailRecord.email)
|
||||||
.then(
|
.then(
|
||||||
function (forgotPasswordToken) {
|
function (forgotPasswordToken) {
|
||||||
var account = this.accounts[forgotPasswordToken.uid]
|
var account = this.accounts[forgotPasswordToken.uid]
|
||||||
|
@ -183,7 +183,7 @@ module.exports = function (
|
||||||
log.trace({ op: 'Heap.forgotPasswordToken', id: id })
|
log.trace({ op: 'Heap.forgotPasswordToken', id: id })
|
||||||
var forgotPasswordToken = this.forgotPasswordTokens[id]
|
var forgotPasswordToken = this.forgotPasswordTokens[id]
|
||||||
if (!forgotPasswordToken) { return P.reject(error.invalidToken()) }
|
if (!forgotPasswordToken) { return P.reject(error.invalidToken()) }
|
||||||
var account = this.accounts[sessionToken.uid]
|
var account = this.accounts[forgotPasswordToken.uid]
|
||||||
if (!account) { return P.reject(error.unknownAccount()) }
|
if (!account) { return P.reject(error.unknownAccount()) }
|
||||||
forgotPasswordToken.email = account.email
|
forgotPasswordToken.email = account.email
|
||||||
return P(forgotPasswordToken)
|
return P(forgotPasswordToken)
|
||||||
|
@ -205,7 +205,7 @@ module.exports = function (
|
||||||
|
|
||||||
// UPDATE
|
// UPDATE
|
||||||
|
|
||||||
Heap.prototype.udateForgotPasswordToken = function (forgotPasswordToken) {
|
Heap.prototype.updateForgotPasswordToken = function (forgotPasswordToken) {
|
||||||
log.trace({ op: 'Heap.udateForgotPasswordToken', uid: forgotPasswordToken && forgotPasswordToken.uid })
|
log.trace({ op: 'Heap.udateForgotPasswordToken', uid: forgotPasswordToken && forgotPasswordToken.uid })
|
||||||
return P(true)
|
return P(true)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
module.exports = function (log, crypto, uuid, isA, error, db, mailer, config) {
|
module.exports = function (log, crypto, P, uuid, isA, error, db, mailer, config) {
|
||||||
|
|
||||||
const HEX_STRING = /^(?:[a-fA-F0-9]{2})+$/
|
const HEX_STRING = /^(?:[a-fA-F0-9]{2})+$/
|
||||||
|
|
||||||
|
@ -78,16 +78,19 @@ module.exports = function (log, crypto, uuid, isA, error, db, mailer, config) {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
// .then(
|
.then(
|
||||||
// function (account) {
|
function (account) {
|
||||||
// return sendVerifyCode(
|
if (account.verified) {
|
||||||
// account.email,
|
return P(account)
|
||||||
// account.emailCode,
|
}
|
||||||
// account.uid
|
return sendVerifyCode(
|
||||||
// )
|
account.email,
|
||||||
// .then(function () { return account })
|
account.emailCode,
|
||||||
// }
|
account.uid
|
||||||
// )
|
)
|
||||||
|
.then(function () { return account })
|
||||||
|
}
|
||||||
|
)
|
||||||
.done(
|
.done(
|
||||||
function (account) {
|
function (account) {
|
||||||
request.reply(
|
request.reply(
|
||||||
|
@ -247,7 +250,7 @@ module.exports = function (log, crypto, uuid, isA, error, db, mailer, config) {
|
||||||
db.account(uid)
|
db.account(uid)
|
||||||
.then(
|
.then(
|
||||||
function (account) {
|
function (account) {
|
||||||
if (code !== account.code) {
|
if (code !== account.emailCode) {
|
||||||
throw error.invalidVerificationCode()
|
throw error.invalidVerificationCode()
|
||||||
}
|
}
|
||||||
return db.verifyEmail(account)
|
return db.verifyEmail(account)
|
||||||
|
|
|
@ -7,15 +7,7 @@ module.exports = function (log, isA, error, db, Token) {
|
||||||
const HEX_STRING = /^(?:[a-fA-F0-9]{2})+$/
|
const HEX_STRING = /^(?:[a-fA-F0-9]{2})+$/
|
||||||
|
|
||||||
function clientData(srpToken) {
|
function clientData(srpToken) {
|
||||||
return {
|
return srpToken.clientData()
|
||||||
srpToken: srpToken.id,
|
|
||||||
passwordStretching: srpToken.passwordStretching,
|
|
||||||
srp: {
|
|
||||||
type: 'SRP-6a/SHA256/2048/v1',
|
|
||||||
salt: srpToken.s,
|
|
||||||
B: srpToken.B.toString('hex')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function bundleAuth(K, authToken) {
|
function bundleAuth(K, authToken) {
|
||||||
|
|
|
@ -23,7 +23,7 @@ module.exports = function (
|
||||||
var auth = require('./auth')(log, isA, error, db, Token)
|
var auth = require('./auth')(log, isA, error, db, Token)
|
||||||
var defaults = require('./defaults')(log, P, db)
|
var defaults = require('./defaults')(log, P, db)
|
||||||
var idp = require('./idp')(log, serverPublicKey)
|
var idp = require('./idp')(log, serverPublicKey)
|
||||||
var account = require('./account')(log, crypto, uuid, isA, error, db, mailer, config)
|
var account = require('./account')(log, crypto, P, uuid, isA, error, db, mailer, config)
|
||||||
var password = require('./password')(log, isA, error, db, mailer)
|
var password = require('./password')(log, isA, error, db, mailer)
|
||||||
var session = require('./session')(log, isA, error, db)
|
var session = require('./session')(log, isA, error, db)
|
||||||
var sign = require('./sign')(log, isA, error, signer, config.domain)
|
var sign = require('./sign')(log, isA, error, signer, config.domain)
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
module.exports = function (log, isA, error, db, mailer) {
|
module.exports = function (log, isA, error, db, mailer) {
|
||||||
|
|
||||||
const HEX_STRING = /^(?:[a-fA-F0-9]{2})+$/
|
const HEX_STRING = /^(?:[a-fA-F0-9]{2})+$/
|
||||||
const FPT_LIFETIME = 1000 * 60 * 15
|
|
||||||
|
|
||||||
function bundleAccountReset(authToken, keyFetchToken, accountResetToken) {
|
function bundleAccountReset(authToken, keyFetchToken, accountResetToken) {
|
||||||
return authToken.bundleKeys('password/change', keyFetchToken, accountResetToken)
|
return authToken.bundleKeys('password/change', keyFetchToken, accountResetToken)
|
||||||
|
@ -14,21 +13,18 @@ module.exports = function (log, isA, error, db, mailer) {
|
||||||
function sendRecoveryCode(forgotPasswordToken) {
|
function sendRecoveryCode(forgotPasswordToken) {
|
||||||
log.trace({ op: 'sendRecoveryCode', id: forgotPasswordToken.id })
|
log.trace({ op: 'sendRecoveryCode', id: forgotPasswordToken.id })
|
||||||
return mailer.sendRecoveryCode(
|
return mailer.sendRecoveryCode(
|
||||||
Buffer(forgotPasswordToken.email, 'hex').toString(),
|
Buffer(forgotPasswordToken.email, 'hex').toString('utf8'),
|
||||||
forgotPasswordToken.passcode
|
forgotPasswordToken.passcode
|
||||||
)
|
)
|
||||||
|
.then(function () { return forgotPasswordToken })
|
||||||
}
|
}
|
||||||
|
|
||||||
function ttl(forgotPasswordToken) {
|
function ttl(forgotPasswordToken) {
|
||||||
return Math.max(
|
return forgotPasswordToken.ttl()
|
||||||
Math.ceil((FPT_LIFETIME - (Date.now() - forgotPasswordToken.created)) / 1000),
|
|
||||||
0
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function failVerifyAttempt(forgotPasswordToken) {
|
function failVerifyAttempt(forgotPasswordToken) {
|
||||||
forgotPasswordToken.tries--
|
return (forgotPasswordToken.failAttempt()) ?
|
||||||
return (forgotPasswordToken.tries < 1) ?
|
|
||||||
db.deleteForgotPasswordToken(forgotPasswordToken) :
|
db.deleteForgotPasswordToken(forgotPasswordToken) :
|
||||||
db.updateForgotPasswordToken(forgotPasswordToken)
|
db.updateForgotPasswordToken(forgotPasswordToken)
|
||||||
}
|
}
|
||||||
|
@ -90,7 +86,7 @@ module.exports = function (log, isA, error, db, mailer) {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.done(
|
.done(
|
||||||
function () {
|
function (forgotPasswordToken) {
|
||||||
request.reply(
|
request.reply(
|
||||||
{
|
{
|
||||||
forgotPasswordToken: forgotPasswordToken.data,
|
forgotPasswordToken: forgotPasswordToken.data,
|
||||||
|
@ -179,7 +175,7 @@ module.exports = function (log, isA, error, db, mailer) {
|
||||||
log.begin('Password.forgotVerify', request)
|
log.begin('Password.forgotVerify', request)
|
||||||
var forgotPasswordToken = request.auth.credentials
|
var forgotPasswordToken = request.auth.credentials
|
||||||
var code = +(request.payload.code)
|
var code = +(request.payload.code)
|
||||||
if (forgotPasswordToken.code === code && ttl(forgotPasswordToken) > 0) {
|
if (forgotPasswordToken.passcode === code && ttl(forgotPasswordToken) > 0) {
|
||||||
db.forgotPasswordVerified(forgotPasswordToken)
|
db.forgotPasswordVerified(forgotPasswordToken)
|
||||||
.done(
|
.done(
|
||||||
function (accountResetToken) {
|
function (accountResetToken) {
|
||||||
|
|
|
@ -1,15 +1,9 @@
|
||||||
var test = require('tap').test
|
var test = require('tap').test
|
||||||
var crypto = require('crypto')
|
var crypto = require('crypto')
|
||||||
var P = require('p-promise')
|
|
||||||
var config = require('../../config').root()
|
|
||||||
var log = { trace: function() {} }
|
var log = { trace: function() {} }
|
||||||
|
|
||||||
var dbs = require('../../kv')(config, log)
|
var tokens = require('../../tokens')(log)
|
||||||
|
var AccountResetToken = tokens.AccountResetToken
|
||||||
var mailer = {}
|
|
||||||
|
|
||||||
var models = require('../../models')(log, config, dbs, mailer)
|
|
||||||
var AccountResetToken = models.tokens.AccountResetToken
|
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'bundle / unbundle works',
|
'bundle / unbundle works',
|
||||||
|
@ -35,12 +29,3 @@ test(
|
||||||
.done(end, end)
|
.done(end, end)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
test(
|
|
||||||
'teardown',
|
|
||||||
function (t) {
|
|
||||||
dbs.cache.close()
|
|
||||||
dbs.store.close()
|
|
||||||
t.end()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
|
@ -1,540 +0,0 @@
|
||||||
var test = require('tap').test
|
|
||||||
var P = require('p-promise')
|
|
||||||
var config = require('../../config').root()
|
|
||||||
var log = { trace: function() {} }
|
|
||||||
|
|
||||||
var dbs = require('../../kv')(config, log)
|
|
||||||
|
|
||||||
var mailer = {
|
|
||||||
sendVerifyCode: function () { return P(null) }
|
|
||||||
}
|
|
||||||
|
|
||||||
var models = require('../../models')(log, config, dbs, mailer)
|
|
||||||
var Account = models.Account
|
|
||||||
var RecoveryEmail = models.RecoveryEmail
|
|
||||||
var SessionToken = models.tokens.SessionToken
|
|
||||||
var AccountResetToken = models.tokens.AccountResetToken
|
|
||||||
|
|
||||||
var a = {
|
|
||||||
uid: 'xxx',
|
|
||||||
email: Buffer('somebody@example.com').toString('hex'),
|
|
||||||
srp: {
|
|
||||||
verifier: 'BAD1',
|
|
||||||
salt: 'BAD2'
|
|
||||||
},
|
|
||||||
kA: 'BAD3',
|
|
||||||
wrapKb: 'BAD4'
|
|
||||||
}
|
|
||||||
|
|
||||||
test(
|
|
||||||
'Account.principal uses the given uid and adds the domain',
|
|
||||||
function (t) {
|
|
||||||
t.equal(Account.principal('xyz'), 'xyz@' + config.domain)
|
|
||||||
t.end()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
test(
|
|
||||||
'Account.create adds a new account',
|
|
||||||
function (t) {
|
|
||||||
Account.create(a)
|
|
||||||
.then(Account.get.bind(null, a.uid))
|
|
||||||
.then(
|
|
||||||
function (account) {
|
|
||||||
t.equal(account.email, a.email)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(Account.del.bind(null, a.uid))
|
|
||||||
.done(
|
|
||||||
function () { t.end() },
|
|
||||||
function (err) { t.fail(err); t.end() })
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
test(
|
|
||||||
'Account.create adds a primary recovery method',
|
|
||||||
function (t) {
|
|
||||||
var code = null
|
|
||||||
Account.create(a)
|
|
||||||
.then(Account.get.bind(null, a.uid))
|
|
||||||
.then(
|
|
||||||
function (account) {
|
|
||||||
code = Object.keys(account.recoveryEmailCodes)[0]
|
|
||||||
t.ok(code)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(
|
|
||||||
function () {
|
|
||||||
return RecoveryEmail.get(a.uid, code)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(
|
|
||||||
function (rm) {
|
|
||||||
t.equal(rm.uid, a.uid)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(Account.del.bind(null, a.uid))
|
|
||||||
.done(
|
|
||||||
function () { t.end() },
|
|
||||||
function (err) { t.fail(err); t.end() })
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
test(
|
|
||||||
'Account.create returns an error if the account exists',
|
|
||||||
function (t) {
|
|
||||||
Account.create(a)
|
|
||||||
.then(Account.create.bind(null, a))
|
|
||||||
.then(
|
|
||||||
function () {
|
|
||||||
t.fail('should not have created an account')
|
|
||||||
},
|
|
||||||
function (err) {
|
|
||||||
t.equal(err.errno, 101)
|
|
||||||
t.equal(err.message, 'Account already exists')
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(Account.del.bind(null, a.uid))
|
|
||||||
.done(
|
|
||||||
function () { t.end() },
|
|
||||||
function (err) { t.fail(err); t.end() })
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
test(
|
|
||||||
'Account.get of an invalid uid returns null',
|
|
||||||
function (t) {
|
|
||||||
Account.get('foobar')
|
|
||||||
.done(
|
|
||||||
function (x) {
|
|
||||||
t.equal(x, null)
|
|
||||||
t.end()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
test(
|
|
||||||
'Account.getId returns the uid given an email',
|
|
||||||
function (t) {
|
|
||||||
Account.create(a)
|
|
||||||
.then(Account.getId.bind(null, a.email))
|
|
||||||
.then(
|
|
||||||
function (id) {
|
|
||||||
t.equal(id, a.uid)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(Account.del.bind(null, a.uid))
|
|
||||||
.done(
|
|
||||||
function () { t.end() },
|
|
||||||
function (err) { t.fail(err); t.end() })
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
test(
|
|
||||||
'Account.exists returns false if the email is not in use',
|
|
||||||
function (t) {
|
|
||||||
Account.exists(Buffer('nobody@example.com').toString('hex')).done(
|
|
||||||
function (exists) {
|
|
||||||
t.equal(exists, false)
|
|
||||||
t.end()
|
|
||||||
},
|
|
||||||
function (err) { t.fail(err); t.end() }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
test(
|
|
||||||
'Account.exists returns true if the email is in use',
|
|
||||||
function (t) {
|
|
||||||
Account.create(a)
|
|
||||||
.then(Account.exists.bind(null, a.email))
|
|
||||||
.then(
|
|
||||||
function (exists) {
|
|
||||||
t.equal(exists, true)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(Account.del.bind(null, a.uid))
|
|
||||||
.done(
|
|
||||||
function () { t.end() },
|
|
||||||
function (err) { t.fail(err); t.end() }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
test(
|
|
||||||
'Account.del deletes the account with the given uid',
|
|
||||||
function (t) {
|
|
||||||
Account.create(a)
|
|
||||||
.then(Account.exists.bind(null, a.email))
|
|
||||||
.then(
|
|
||||||
function (exists) {
|
|
||||||
t.equal(exists, true)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(Account.del.bind(null, a.uid))
|
|
||||||
.then(Account.get.bind(null, a.uid))
|
|
||||||
.done(
|
|
||||||
function (account) {
|
|
||||||
t.equal(account, null)
|
|
||||||
t.end()
|
|
||||||
},
|
|
||||||
function (err) {
|
|
||||||
t.fail(err)
|
|
||||||
t.end()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
test(
|
|
||||||
'Account.del deletes all data related to the account',
|
|
||||||
function (t) {
|
|
||||||
var account = null
|
|
||||||
var session = null
|
|
||||||
var reset = null
|
|
||||||
// This test is only valid for memory db
|
|
||||||
if (!dbs.store.kv.data) {
|
|
||||||
return t.end()
|
|
||||||
}
|
|
||||||
t.equal(Object.keys(dbs.store.kv.data).length, 0)
|
|
||||||
Account.create(a)
|
|
||||||
.then(
|
|
||||||
function (a) {
|
|
||||||
account = a
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(SessionToken.create.bind(null, a.uid))
|
|
||||||
.then(
|
|
||||||
function (t) {
|
|
||||||
session = t
|
|
||||||
return account.addSessionToken(session)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(AccountResetToken.create.bind(null, a.uid))
|
|
||||||
.then(
|
|
||||||
function (t) {
|
|
||||||
reset = t
|
|
||||||
return account.setResetToken(reset)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(
|
|
||||||
function () {
|
|
||||||
// 5: uid, user, recovery, reset, session
|
|
||||||
t.equal(Object.keys(dbs.store.kv.data).length, 5)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(Account.del.bind(null, a.uid))
|
|
||||||
.done(
|
|
||||||
function () {
|
|
||||||
t.equal(Object.keys(dbs.store.kv.data).length, 0)
|
|
||||||
t.end()
|
|
||||||
},
|
|
||||||
function (err) {
|
|
||||||
t.fail(err)
|
|
||||||
t.end()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
test(
|
|
||||||
'Account.del of an invalid uid returns null',
|
|
||||||
function (t) {
|
|
||||||
Account.del('foobar')
|
|
||||||
.done(
|
|
||||||
function (x) {
|
|
||||||
t.equal(x, null)
|
|
||||||
t.end()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
test(
|
|
||||||
'Account.verify sets verified to true when the recovery method is primary',
|
|
||||||
function (t) {
|
|
||||||
Account.create(a)
|
|
||||||
.then(
|
|
||||||
function (account) {
|
|
||||||
var code = Object.keys(account.recoveryEmailCodes)[0]
|
|
||||||
return RecoveryEmail.get(account.uid, code)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(
|
|
||||||
function (x) {
|
|
||||||
x.verified = true
|
|
||||||
return Account.verify(x)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(
|
|
||||||
function (account) {
|
|
||||||
t.equal(account.verified, true)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(Account.del.bind(null, a.uid))
|
|
||||||
.done(
|
|
||||||
function () { t.end() },
|
|
||||||
function (err) { t.fail(err); t.end() }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
test(
|
|
||||||
'Account.verify does not set verified true if recovery method is not verified',
|
|
||||||
function (t) {
|
|
||||||
Account.create(a)
|
|
||||||
.then(
|
|
||||||
function (account) {
|
|
||||||
var code = Object.keys(account.recoveryEmailCodes)[0]
|
|
||||||
return RecoveryEmail.get(account.uid, code)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(
|
|
||||||
function (x) {
|
|
||||||
return Account.verify(x)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(
|
|
||||||
function (account) {
|
|
||||||
t.equal(account.verified, false)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(Account.del.bind(null, a.uid))
|
|
||||||
.done(
|
|
||||||
function () { t.end() },
|
|
||||||
function (err) { t.fail(err); t.end() }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
test(
|
|
||||||
'account.setResetToken deletes existing token',
|
|
||||||
function (t) {
|
|
||||||
var account = null
|
|
||||||
var token1 = null
|
|
||||||
var token2 = null
|
|
||||||
AccountResetToken.create(a.uid)
|
|
||||||
.then(
|
|
||||||
function (x) {
|
|
||||||
token1 = x
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(Account.create.bind(null, a))
|
|
||||||
.then(
|
|
||||||
function (x) {
|
|
||||||
account = x
|
|
||||||
return account.setResetToken(token1)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(AccountResetToken.create.bind(null, a.uid))
|
|
||||||
.then(
|
|
||||||
function (x) {
|
|
||||||
t.equal(account.resetTokenId, token1.id)
|
|
||||||
token2 = x
|
|
||||||
return account.setResetToken(token2)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(
|
|
||||||
function () {
|
|
||||||
t.equal(account.resetTokenId, token2.id)
|
|
||||||
return AccountResetToken.get(token1.id)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(
|
|
||||||
function (x) {
|
|
||||||
t.equal(x, null)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(Account.del.bind(null, a.uid))
|
|
||||||
.done(
|
|
||||||
function () { t.end() },
|
|
||||||
function (err) { t.fail(err); t.end() }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
test(
|
|
||||||
'Account.getByEmail works',
|
|
||||||
function (t) {
|
|
||||||
Account.create(a)
|
|
||||||
.then(Account.getByEmail.bind(null, a.email))
|
|
||||||
.then(
|
|
||||||
function (account) {
|
|
||||||
t.equal(account.email, a.email)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(Account.del.bind(null, a.uid))
|
|
||||||
.done(
|
|
||||||
function () { t.end() },
|
|
||||||
function (err) { t.fail(err); t.end() }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
test(
|
|
||||||
'account.addSessionToken works',
|
|
||||||
function (t) {
|
|
||||||
var account = null
|
|
||||||
var token = null
|
|
||||||
Account.create(a)
|
|
||||||
.then(
|
|
||||||
function (x) {
|
|
||||||
account = x
|
|
||||||
return SessionToken.create(x.uid)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(
|
|
||||||
function (t) {
|
|
||||||
token = t
|
|
||||||
return account.addSessionToken(t)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(
|
|
||||||
function (x) {
|
|
||||||
t.equal(x.sessionTokenIds[token.id], true)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(Account.del.bind(null, a.uid))
|
|
||||||
.done(
|
|
||||||
function () { t.end() },
|
|
||||||
function (err) { t.fail(err); t.end() }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
test(
|
|
||||||
'account.recoveryEmails returns an array of RecoveryEmail objects',
|
|
||||||
function (t) {
|
|
||||||
Account.create(a)
|
|
||||||
.then(
|
|
||||||
function (account) {
|
|
||||||
return account.recoveryEmails()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(
|
|
||||||
function (rms) {
|
|
||||||
t.equal(rms.length, 1)
|
|
||||||
t.equal(rms[0] instanceof RecoveryEmail, true)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(Account.del.bind(null, a.uid))
|
|
||||||
.done(
|
|
||||||
function () { t.end() },
|
|
||||||
function (err) { t.fail(err); t.end() }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
test(
|
|
||||||
'account.reset changes wrapKb and verifier',
|
|
||||||
function (t) {
|
|
||||||
var form = {
|
|
||||||
wrapKb: 'DEADBEEF',
|
|
||||||
srp: {
|
|
||||||
type: 'SRP-6a/SHA256/2048/v1',
|
|
||||||
verifier: 'FEEDFACE',
|
|
||||||
salt: '12345678'
|
|
||||||
},
|
|
||||||
passwordStretching: {
|
|
||||||
stuff: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Account.create(a)
|
|
||||||
.then(
|
|
||||||
function (account) {
|
|
||||||
return account.reset(form)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(
|
|
||||||
function (account) {
|
|
||||||
t.equal(account.wrapKb, form.wrapKb)
|
|
||||||
t.equal(account.srp.verifier, form.srp.verifier)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(Account.del.bind(null, a.uid))
|
|
||||||
.done(
|
|
||||||
function () { t.end() },
|
|
||||||
function (err) { t.fail(err); t.end() }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
test(
|
|
||||||
'account.reset deletes all tokens',
|
|
||||||
function (t) {
|
|
||||||
var account = null
|
|
||||||
var session = null
|
|
||||||
var reset = null
|
|
||||||
var form = {
|
|
||||||
wrapKb: 'DEADBEEF',
|
|
||||||
srp: {
|
|
||||||
type: 'SRP-6a/SHA256/2048/v1',
|
|
||||||
verifier: 'FEEDFACE',
|
|
||||||
salt: '12345678'
|
|
||||||
},
|
|
||||||
passwordStretching: {
|
|
||||||
stuff: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Account.create(a)
|
|
||||||
.then(
|
|
||||||
function (a) {
|
|
||||||
account = a
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(SessionToken.create.bind(null, a.uid))
|
|
||||||
.then(
|
|
||||||
function (x) {
|
|
||||||
session = x
|
|
||||||
return account.addSessionToken(session)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(AccountResetToken.create.bind(null, a.uid))
|
|
||||||
.then(
|
|
||||||
function (x) {
|
|
||||||
reset = x
|
|
||||||
return account.setResetToken(reset)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(
|
|
||||||
function () {
|
|
||||||
return account.reset(form)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(
|
|
||||||
function () {
|
|
||||||
return AccountResetToken.get(reset.id)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(
|
|
||||||
function (x) {
|
|
||||||
t.equal(x, null)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(
|
|
||||||
function () {
|
|
||||||
return SessionToken.get(session.id)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(
|
|
||||||
function (x) {
|
|
||||||
t.equal(x, null)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(Account.del.bind(null, a.uid))
|
|
||||||
.done(
|
|
||||||
function () { t.end() },
|
|
||||||
function (err) { t.fail(err); t.end() }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
test(
|
|
||||||
'teardown',
|
|
||||||
function (t) {
|
|
||||||
dbs.cache.close()
|
|
||||||
dbs.store.close()
|
|
||||||
t.end()
|
|
||||||
}
|
|
||||||
)
|
|
|
@ -1,16 +1,9 @@
|
||||||
var test = require('tap').test
|
var test = require('tap').test
|
||||||
var crypto = require('crypto')
|
var crypto = require('crypto')
|
||||||
var P = require('p-promise')
|
|
||||||
var config = require('../../config').root()
|
|
||||||
var log = { trace: function() {} }
|
var log = { trace: function() {} }
|
||||||
var dbs = require('../../kv')(config, log)
|
|
||||||
|
|
||||||
var mailer = {
|
var tokens = require('../../tokens')(log)
|
||||||
sendVerifyCode: function () { return P(null) }
|
var AuthToken = tokens.AuthToken
|
||||||
}
|
|
||||||
|
|
||||||
var models = require('../../models')(log, config, dbs, mailer)
|
|
||||||
var AuthToken = models.tokens.AuthToken
|
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'bundle / unbundle works',
|
'bundle / unbundle works',
|
||||||
|
@ -36,12 +29,3 @@ test(
|
||||||
.done(end, end)
|
.done(end, end)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
test(
|
|
||||||
'teardown',
|
|
||||||
function (t) {
|
|
||||||
dbs.cache.close()
|
|
||||||
dbs.store.close()
|
|
||||||
t.end()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
|
@ -1,347 +0,0 @@
|
||||||
var test = require('tap').test
|
|
||||||
var inherits = require('util').inherits
|
|
||||||
var crypto = require('crypto')
|
|
||||||
var P = require('p-promise')
|
|
||||||
var hkdf = require('../../hkdf')
|
|
||||||
var config = require('../../config').root()
|
|
||||||
var log = { trace: function() {} }
|
|
||||||
var dbs = require('../../kv')(config, log)
|
|
||||||
|
|
||||||
function fakeCrypto(bytes) {
|
|
||||||
return {
|
|
||||||
randomBytes: function (size, cb) {
|
|
||||||
cb(null, bytes)
|
|
||||||
},
|
|
||||||
createHmac: crypto.createHmac
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test vectors
|
|
||||||
// https://wiki.mozilla.org/Identity/AttachedServices/KeyServerProtocol#Test_Vectors
|
|
||||||
|
|
||||||
var useSession = {
|
|
||||||
sessionToken: Buffer(
|
|
||||||
'8081828384858687' +
|
|
||||||
'88898a8b8c8d8e8f' +
|
|
||||||
'9091929394959697' +
|
|
||||||
'98999a9b9c9d9e9f',
|
|
||||||
'hex'),
|
|
||||||
tokenId:
|
|
||||||
'31217a79ba0d62e9' +
|
|
||||||
'c6e33cee374f0879' +
|
|
||||||
'3171b2a39d14cc8f' +
|
|
||||||
'f680540b5028d069',
|
|
||||||
key:
|
|
||||||
'6c87cfeba3a216d4' +
|
|
||||||
'b1829e62478500ac' +
|
|
||||||
'd2953158130cae0b' +
|
|
||||||
'2c92ef8a2ea6089a'
|
|
||||||
}
|
|
||||||
|
|
||||||
var accountKeys = {
|
|
||||||
keyFetchToken: Buffer(
|
|
||||||
'6061626364656667' +
|
|
||||||
'68696a6b6c6d6e6f' +
|
|
||||||
'7071727374757677' +
|
|
||||||
'78797a7b7c7d7e7f',
|
|
||||||
'hex'),
|
|
||||||
tokenId:
|
|
||||||
'7f784ba2bd89097f' +
|
|
||||||
'743632d21316d987' +
|
|
||||||
'38e146a9e7123a98' +
|
|
||||||
'39a87c96b3bb99cb',
|
|
||||||
key:
|
|
||||||
'6dedf96237deb067' +
|
|
||||||
'f4232af00b3c7148' +
|
|
||||||
'e815635c147a7215' +
|
|
||||||
'a64906bdb2823471',
|
|
||||||
hmacKey:
|
|
||||||
'ca24f43285899356' +
|
|
||||||
'5d698251dbe6c7f7' +
|
|
||||||
'da5f9ad003835a41' +
|
|
||||||
'edf7c813124c5499',
|
|
||||||
xorKey:
|
|
||||||
'9dff4835ffdbacd6' +
|
|
||||||
'5e27f5dde15a1f18' +
|
|
||||||
'994ff75f70bab7db' +
|
|
||||||
'b5c4c9771e657704' +
|
|
||||||
'4666cf97273e2a96' +
|
|
||||||
'02993f5b1e258d8f' +
|
|
||||||
'3b4d837e505f8458' +
|
|
||||||
'41a986882ef36631',
|
|
||||||
kA:
|
|
||||||
'2021222324252627' +
|
|
||||||
'28292a2b2c2d2e2f' +
|
|
||||||
'3031323334353637' +
|
|
||||||
'38393a3b3c3d3e3f',
|
|
||||||
wrapKb:
|
|
||||||
'4041424344454647' +
|
|
||||||
'48494a4b4c4d4e4f' +
|
|
||||||
'5051525354555657' +
|
|
||||||
'58595a5b5c5d5e5f',
|
|
||||||
ciphertext:
|
|
||||||
'bdde6a16dbfe8af1' +
|
|
||||||
'760edff6cd773137' +
|
|
||||||
'a97ec56c448f81ec' +
|
|
||||||
'8dfdf34c2258493b' +
|
|
||||||
'06278dd4637b6cd1' +
|
|
||||||
'4ad075105268c3c0' +
|
|
||||||
'6b1cd12d040ad20f' +
|
|
||||||
'19f0dcd372ae386e',
|
|
||||||
hmac:
|
|
||||||
'6f7972302f00dfe8' +
|
|
||||||
'2d5a8ce0553b0ffe' +
|
|
||||||
'80e073078d4f30f9' +
|
|
||||||
'0c48537f8ca92222'
|
|
||||||
}
|
|
||||||
|
|
||||||
var sessionAuth = {
|
|
||||||
K: Buffer(
|
|
||||||
'e68fd0112bfa31dc' +
|
|
||||||
'ffc8e9c96a1cbadb' +
|
|
||||||
'4c3145978ff35c73' +
|
|
||||||
'e5bf8d30bbc7499a',
|
|
||||||
'hex'),
|
|
||||||
hmacKey:
|
|
||||||
'e252adb2c217c2a1' +
|
|
||||||
'02b4bd3f71294430' +
|
|
||||||
'e367145b107d1e8d' +
|
|
||||||
'e35684bbdf13f1e9',
|
|
||||||
xorKey:
|
|
||||||
'75a6ff483b6afe43' +
|
|
||||||
'f80f95b5e2061ce3' +
|
|
||||||
'961996ec4c2eeb9c' +
|
|
||||||
'350ebfabdd766549' +
|
|
||||||
'342a0b2d910c9f5b' +
|
|
||||||
'b2dee20f2af61849' +
|
|
||||||
'a4a20ff16ee4a25f' +
|
|
||||||
'cb6e832effa77f59',
|
|
||||||
keyFetchToken:
|
|
||||||
'6061626364656667' +
|
|
||||||
'68696a6b6c6d6e6f' +
|
|
||||||
'7071727374757677' +
|
|
||||||
'78797a7b7c7d7e7f',
|
|
||||||
sessionToken:
|
|
||||||
'8081828384858687' +
|
|
||||||
'88898a8b8c8d8e8f' +
|
|
||||||
'9091929394959697' +
|
|
||||||
'98999a9b9c9d9e9f',
|
|
||||||
ciphertext:
|
|
||||||
'15c79d2b5f0f9824' +
|
|
||||||
'9066ffde8e6b728c' +
|
|
||||||
'e668e49f385b9deb' +
|
|
||||||
'4d77c5d0a10b1b36' +
|
|
||||||
'b4ab89ae158919dc' +
|
|
||||||
'3a576884a67b96c6' +
|
|
||||||
'34339d62fa7134c8' +
|
|
||||||
'53f719b5633ae1c6',
|
|
||||||
hmac:
|
|
||||||
'b27381d49ca93e61' +
|
|
||||||
'3247c49a0cd0c901' +
|
|
||||||
'0332f186bb07c23f' +
|
|
||||||
'33ad176916d607c4'
|
|
||||||
}
|
|
||||||
|
|
||||||
var passwordChange = {
|
|
||||||
xorKey:
|
|
||||||
'aaf041fd5f2c23e9' +
|
|
||||||
'0c3636f93a170ef0' +
|
|
||||||
'60456d7edf7678df' +
|
|
||||||
'2d5297797626a07d' +
|
|
||||||
'a96803cfe941a0c8' +
|
|
||||||
'ea140e371871ea20' +
|
|
||||||
'1ec38ad41a233b8e' +
|
|
||||||
'39ff1bedf6ce0aec',
|
|
||||||
hmacKey:
|
|
||||||
'81a03345184a09fd' +
|
|
||||||
'9aef6ec1a1ddf80f' +
|
|
||||||
'c4e3d354bf8af42f' +
|
|
||||||
'a4b32696384cb9b9',
|
|
||||||
ciphertext:
|
|
||||||
'ca91239e3b49458e' +
|
|
||||||
'645f5c92567a609f' +
|
|
||||||
'10341f0dab030ea8' +
|
|
||||||
'552bed020a5bde02' +
|
|
||||||
'09c9a16c4de4066f' +
|
|
||||||
'42bda49cb4dc448f' +
|
|
||||||
'ae723867ae968d39' +
|
|
||||||
'8146a1564a73b453',
|
|
||||||
hmac:
|
|
||||||
'442223ac3a149d00' +
|
|
||||||
'cc319a73189b8572' +
|
|
||||||
'e323084b662f74a5' +
|
|
||||||
'b5d1f32925ea50de'
|
|
||||||
}
|
|
||||||
|
|
||||||
var accountReset = {
|
|
||||||
accountResetToken: Buffer(
|
|
||||||
'a0a1a2a3a4a5a6a7' +
|
|
||||||
'a8a9aaabacadaeaf' +
|
|
||||||
'b0b1b2b3b4b5b6b7' +
|
|
||||||
'b8b9babbbcbdbebf',
|
|
||||||
'hex'),
|
|
||||||
tokenId:
|
|
||||||
'b421fa511242b33f' +
|
|
||||||
'feebdef63089242f' +
|
|
||||||
'fde11c811fd5474d' +
|
|
||||||
'b888ade257861e23',
|
|
||||||
key:
|
|
||||||
'da5fb4a8e1a7fc77' +
|
|
||||||
'dfcf43be71455f69' +
|
|
||||||
'f6776e24f369e253' +
|
|
||||||
'ff1f541fbb5e9bc3',
|
|
||||||
xorKey:
|
|
||||||
'def723a6ece08e37' + 'd5b598a25a031eda' +
|
|
||||||
'acad44ef5186fef0' + '2a76417dc245379b' +
|
|
||||||
'1c5825ac741dd558' + '632d933cc9455875' +
|
|
||||||
'f099cbe46d926ace' + '201616119d47f115' +
|
|
||||||
'ab7623e63c29c518' + '187a6139570f8457' +
|
|
||||||
'03c84be42720bbb6' + '6097f90172a7ebf4' +
|
|
||||||
'0a44f140828f0cd4' + '16028e67e0ef3b4c' +
|
|
||||||
'f6e0b43055bd008a' + '1305b2b5f579b0f0' +
|
|
||||||
'ca91d70e28265713' + 'b4d2dc5197e64dec' +
|
|
||||||
'f0e6ee2b8acdef73' + 'ea1951f7dea374cf' +
|
|
||||||
'2f56ac2a76f5f1e1' + '2ba46852bf6d315e' +
|
|
||||||
'2e9419c8d4d43676' + '168044e45862c3e4' +
|
|
||||||
'3e4a390b00950870' + '953f36112d697b43' +
|
|
||||||
'6fd661567ca29c7e' + '68fea229b016cdad' +
|
|
||||||
'c19bf3430a0b52c7' + 'cdd232e774c10882' +
|
|
||||||
'507bd85a3b0c14fe' + '795367422374d774' +
|
|
||||||
'dfa43df9f91d723d' + '4480e2d2f0776794' +
|
|
||||||
'67481cab9c835602' + '69fa7f3086efc88e',
|
|
||||||
plaintext:
|
|
||||||
'4041424344454647' + '48494a4b4c4d4e4f' +
|
|
||||||
'5051525354555657' + '58595a5b5c5d5e5f' +
|
|
||||||
'1111111111111111' + '1111111111111111' +
|
|
||||||
'1111111111111111' + '1111111111111111' +
|
|
||||||
'1111111111111111' + '1111111111111111' +
|
|
||||||
'1111111111111111' + '1111111111111111' +
|
|
||||||
'1111111111111111' + '1111111111111111' +
|
|
||||||
'1111111111111111' + '1111111111111111' +
|
|
||||||
'1111111111111111' + '1111111111111111' +
|
|
||||||
'1111111111111111' + '1111111111111111' +
|
|
||||||
'1111111111111111' + '1111111111111111' +
|
|
||||||
'1111111111111111' + '1111111111111111' +
|
|
||||||
'1111111111111111' + '1111111111111111' +
|
|
||||||
'1111111111111111' + '1111111111111111' +
|
|
||||||
'1111111111111111' + '1111111111111111' +
|
|
||||||
'1111111111111111' + '1111111111111111' +
|
|
||||||
'1111111111111111' + '1111111111111111' +
|
|
||||||
'1111111111111111' + '1111111111111111',
|
|
||||||
ciphertext:
|
|
||||||
'9eb661e5a8a5c870' + '9dfcd2e9164e5095' +
|
|
||||||
'fcfc16bc05d3a8a7' + '722f1b269e1869c4' +
|
|
||||||
'0d4934bd650cc449' + '723c822dd8544964' +
|
|
||||||
'e188daf57c837bdf' + '310707008c56e004' +
|
|
||||||
'ba6732f72d38d409' + '096b7028461e9546' +
|
|
||||||
'12d95af53631aaa7' + '7186e81063b6fae5' +
|
|
||||||
'1b55e051939e1dc5' + '07139f76f1fe2a5d' +
|
|
||||||
'e7f1a52144ac119b' + '0214a3a4e468a1e1' +
|
|
||||||
'db80c61f39374602' + 'a5c3cd4086f75cfd' +
|
|
||||||
'e1f7ff3a9bdcfe62' + 'fb0840e6cfb265de' +
|
|
||||||
'3e47bd3b67e4e0f0' + '3ab57943ae7c204f' +
|
|
||||||
'3f8508d9c5c52767' + '079155f54973d2f5' +
|
|
||||||
'2f5b281a11841961' + '842e27003c786a52' +
|
|
||||||
'7ec770476db38d6f' + '79efb338a107dcbc' +
|
|
||||||
'd08ae2521b1a43d6' + 'dcc323f665d01993' +
|
|
||||||
'416ac94b2a1d05ef' + '684276533265c665' +
|
|
||||||
'ceb52ce8e80c632c' + '5591f3c3e1667685' +
|
|
||||||
'76590dba8d924713' + '78eb6e2197fed99f'
|
|
||||||
}
|
|
||||||
|
|
||||||
var KBundle = require('../../bundle/bundle')(fakeCrypto(accountKeys.keyFetchToken), P, hkdf)
|
|
||||||
var KToken = require('../../models/token')(log, inherits, KBundle)
|
|
||||||
|
|
||||||
var SBundle = require('../../bundle/bundle')(fakeCrypto(useSession.sessionToken), P, hkdf)
|
|
||||||
var SToken = require('../../models/token')(log, inherits, SBundle)
|
|
||||||
|
|
||||||
var RBundle = require('../../bundle/bundle')(fakeCrypto(accountReset.accountResetToken), P, hkdf)
|
|
||||||
var RToken = require('../../models/token')(log, inherits, RBundle)
|
|
||||||
|
|
||||||
var KeyFetchToken = require('../../models/key_fetch_token')(log, inherits, KToken, dbs.store)
|
|
||||||
var AccountResetToken = require('../../models/account_reset_token')(log, inherits, RToken, crypto, dbs.store)
|
|
||||||
var SessionToken = require('../../models/session_token')(log, inherits, SToken, dbs.store)
|
|
||||||
|
|
||||||
var AuthToken = require('../../models/auth_token')(log, inherits, SToken, dbs.store)
|
|
||||||
var tokens = {
|
|
||||||
AuthToken: AuthToken,
|
|
||||||
KeyFetchToken: KeyFetchToken,
|
|
||||||
AccountResetToken: AccountResetToken,
|
|
||||||
SessionToken: SessionToken
|
|
||||||
}
|
|
||||||
|
|
||||||
function FakeAccount() {
|
|
||||||
this.sessionTokenIds = {}
|
|
||||||
this.resetTokenId = null
|
|
||||||
}
|
|
||||||
var account = new FakeAccount()
|
|
||||||
FakeAccount.get = function () { return P(account) }
|
|
||||||
FakeAccount.prototype.addSessionToken = function (t) {
|
|
||||||
this.sessionTokenIds[t.id] = true
|
|
||||||
return P(null)
|
|
||||||
}
|
|
||||||
FakeAccount.prototype.setAuthToken = function (t) {
|
|
||||||
this.authTokenId = t.id
|
|
||||||
return P(null)
|
|
||||||
}
|
|
||||||
|
|
||||||
var AuthBundle = require('../../models/auth_bundle')(log, inherits, require('../../bundle'), FakeAccount, tokens)
|
|
||||||
|
|
||||||
test(
|
|
||||||
'create / get',
|
|
||||||
function (t) {
|
|
||||||
KeyFetchToken.create('xxx')
|
|
||||||
.then(
|
|
||||||
function () {
|
|
||||||
return KeyFetchToken.get(accountKeys.tokenId)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.done(
|
|
||||||
function (token) {
|
|
||||||
t.equal(token.uid, 'xxx')
|
|
||||||
t.equal(token.id.toString('hex'), accountKeys.tokenId)
|
|
||||||
t.equal(token.key.toString('hex'), accountKeys.key)
|
|
||||||
t.equal(token.hmacKey.toString('hex'), accountKeys.hmacKey)
|
|
||||||
t.equal(token.xorKey.toString('hex'), accountKeys.xorKey)
|
|
||||||
t.end()
|
|
||||||
},
|
|
||||||
function (err) {
|
|
||||||
t.fail(err)
|
|
||||||
t.end()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
test(
|
|
||||||
'/account/keys',
|
|
||||||
function (t) {
|
|
||||||
KeyFetchToken.create('xxx')
|
|
||||||
.done(
|
|
||||||
function (token) {
|
|
||||||
t.equal(token.uid, 'xxx')
|
|
||||||
t.equal(token.id.toString('hex'), accountKeys.tokenId)
|
|
||||||
t.equal(token.key.toString('hex'), accountKeys.key)
|
|
||||||
t.equal(token.hmacKey.toString('hex'), accountKeys.hmacKey)
|
|
||||||
t.equal(token.xorKey.toString('hex'), accountKeys.xorKey)
|
|
||||||
|
|
||||||
var b = token.bundle(accountKeys.kA, accountKeys.wrapKb)
|
|
||||||
t.equal(b.toString('hex'), accountKeys.ciphertext + accountKeys.hmac)
|
|
||||||
t.end()
|
|
||||||
},
|
|
||||||
function (err) {
|
|
||||||
t.fail(err)
|
|
||||||
t.end()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
test(
|
|
||||||
'teardown',
|
|
||||||
function (t) {
|
|
||||||
dbs.cache.close()
|
|
||||||
dbs.store.close()
|
|
||||||
t.end()
|
|
||||||
}
|
|
||||||
)
|
|
|
@ -1,17 +1,11 @@
|
||||||
var test = require('tap').test
|
var test = require('tap').test
|
||||||
var crypto = require('crypto')
|
var crypto = require('crypto')
|
||||||
var P = require('p-promise')
|
|
||||||
var config = require('../../config').root()
|
|
||||||
var log = { trace: function() {} }
|
var log = { trace: function() {} }
|
||||||
var dbs = require('../../kv')(config, log)
|
|
||||||
|
|
||||||
var sends = 0
|
var sends = 0
|
||||||
var mailer = {
|
|
||||||
sendRecoveryCode: function () { sends++; return P(null) }
|
|
||||||
}
|
|
||||||
|
|
||||||
var models = require('../../models')(log, config, dbs, mailer)
|
var tokens = require('../../tokens')(log)
|
||||||
var ForgotPasswordToken = models.tokens.ForgotPasswordToken
|
var ForgotPasswordToken = tokens.ForgotPasswordToken
|
||||||
|
|
||||||
var email = Buffer('test@example.com').toString('hex')
|
var email = Buffer('test@example.com').toString('hex')
|
||||||
|
|
||||||
|
@ -35,74 +29,20 @@ test(
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
test(
|
|
||||||
'sendRecoveryCode calls the mailer',
|
|
||||||
function (t) {
|
|
||||||
ForgotPasswordToken.create('xxx', email)
|
|
||||||
.then(
|
|
||||||
function (x) {
|
|
||||||
return x.sendRecoveryCode()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.done(
|
|
||||||
function () {
|
|
||||||
t.equal(sends, 1, 'mail sent')
|
|
||||||
t.end()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'failAttempt decrements `tries`',
|
'failAttempt decrements `tries`',
|
||||||
function (t) {
|
function (t) {
|
||||||
ForgotPasswordToken.create('xxx', email)
|
ForgotPasswordToken.create('xxx', email)
|
||||||
.then(
|
.done(
|
||||||
function (x) {
|
function (x) {
|
||||||
t.equal(x.tries, 3)
|
t.equal(x.tries, 3)
|
||||||
return x.failAttempt()
|
t.equal(x.failAttempt(), false)
|
||||||
}
|
|
||||||
)
|
|
||||||
.done(
|
|
||||||
function (x) {
|
|
||||||
t.equal(x.tries, 2)
|
t.equal(x.tries, 2)
|
||||||
|
t.equal(x.failAttempt(), false)
|
||||||
|
t.equal(x.tries, 1)
|
||||||
|
t.equal(x.failAttempt(), true)
|
||||||
t.end()
|
t.end()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
test(
|
|
||||||
'failAttempt deletes the token if out of tries',
|
|
||||||
function (t) {
|
|
||||||
var tokenId = null
|
|
||||||
ForgotPasswordToken.create('xxx', email)
|
|
||||||
.then(
|
|
||||||
function (x) {
|
|
||||||
tokenId = x.id
|
|
||||||
x.tries = 1
|
|
||||||
return x.failAttempt()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(
|
|
||||||
function () {
|
|
||||||
return ForgotPasswordToken.get(tokenId)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.done(
|
|
||||||
function (x) {
|
|
||||||
t.equal(x, null)
|
|
||||||
t.end()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
test(
|
|
||||||
'teardown',
|
|
||||||
function (t) {
|
|
||||||
dbs.cache.close()
|
|
||||||
dbs.store.close()
|
|
||||||
t.end()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
|
@ -1,47 +0,0 @@
|
||||||
var test = require('tap').test
|
|
||||||
var crypto = require('crypto')
|
|
||||||
var P = require('p-promise')
|
|
||||||
var config = require('../../config').root()
|
|
||||||
var log = { trace: function() {} }
|
|
||||||
var dbs = require('../../kv')(config, log)
|
|
||||||
|
|
||||||
var mailer = {
|
|
||||||
sendVerifyCode: function () { return P(null) }
|
|
||||||
}
|
|
||||||
|
|
||||||
var models = require('../../models')(log, config, dbs, mailer)
|
|
||||||
var KeyFetchToken = models.tokens.KeyFetchToken
|
|
||||||
|
|
||||||
test(
|
|
||||||
'bundle / unbundle works',
|
|
||||||
function (t) {
|
|
||||||
function end() { t.end() }
|
|
||||||
KeyFetchToken.create('xxx')
|
|
||||||
.then(
|
|
||||||
function (x) {
|
|
||||||
var kA = crypto.randomBytes(32).toString('hex')
|
|
||||||
var wrapKb = crypto.randomBytes(32).toString('hex')
|
|
||||||
var b = x.bundle(kA, wrapKb)
|
|
||||||
var ub = x.unbundle(b)
|
|
||||||
t.equal(ub.kA, kA)
|
|
||||||
t.equal(ub.wrapKb, wrapKb)
|
|
||||||
return x
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(
|
|
||||||
function (x) {
|
|
||||||
return x.del()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.done(end, end)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
test(
|
|
||||||
'teardown',
|
|
||||||
function (t) {
|
|
||||||
dbs.cache.close()
|
|
||||||
dbs.store.close()
|
|
||||||
t.end()
|
|
||||||
}
|
|
||||||
)
|
|
|
@ -1,137 +0,0 @@
|
||||||
var test = require('tap').test
|
|
||||||
var crypto = require('crypto')
|
|
||||||
var P = require('p-promise')
|
|
||||||
var config = require('../../config').root()
|
|
||||||
var log = { trace: function() {} }
|
|
||||||
var dbs = require('../../kv')(config, log)
|
|
||||||
|
|
||||||
const HEX_STRING = /^(?:[a-fA-F0-9]{2})+$/
|
|
||||||
|
|
||||||
var sends = 0
|
|
||||||
var mailer = {
|
|
||||||
sendVerifyCode: function () { sends++; return P(null) }
|
|
||||||
}
|
|
||||||
|
|
||||||
var models = require('../../models')(log, config, dbs, mailer)
|
|
||||||
var RecoveryEmail = models.RecoveryEmail
|
|
||||||
|
|
||||||
var email = Buffer('me@example.com').toString('hex')
|
|
||||||
|
|
||||||
test(
|
|
||||||
'RecoveryEmail.create generates a random 32 byte code as a hex string',
|
|
||||||
function (t) {
|
|
||||||
function end() { t.end() }
|
|
||||||
RecoveryEmail.create('xxx', email, true)
|
|
||||||
.then(
|
|
||||||
function (x) {
|
|
||||||
t.equal(x.code.length, 8)
|
|
||||||
t.equal(HEX_STRING.test(x.code), true)
|
|
||||||
return x
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(
|
|
||||||
function (x) {
|
|
||||||
return x.del()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.done(end, end)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
test(
|
|
||||||
'RecoveryEmail.create calls mailer.sendVerifyCode',
|
|
||||||
function (t) {
|
|
||||||
sends = 0
|
|
||||||
function end() { t.end() }
|
|
||||||
RecoveryEmail.create('xxx', email, true)
|
|
||||||
.then(
|
|
||||||
function (x) {
|
|
||||||
t.equal(sends, 1)
|
|
||||||
sends = 0
|
|
||||||
return x.del()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.done(end, end)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
test(
|
|
||||||
'recoveryEmail.verify sets verified to true if the codes match',
|
|
||||||
function (t) {
|
|
||||||
function end() { t.end() }
|
|
||||||
RecoveryEmail.create('xxx', email, true)
|
|
||||||
.then(
|
|
||||||
function (x) {
|
|
||||||
t.equal(x.verified, false)
|
|
||||||
var c = x.code
|
|
||||||
return x.verify(c)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(
|
|
||||||
function (x) {
|
|
||||||
t.equal(x.verified, true)
|
|
||||||
return x.del()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.done(end, end)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
test(
|
|
||||||
'recoveryEmail.verify does not set verified if codes do not match',
|
|
||||||
function (t) {
|
|
||||||
function end() { t.end() }
|
|
||||||
RecoveryEmail.create('xxx', email, true)
|
|
||||||
.then(
|
|
||||||
function (x) {
|
|
||||||
t.equal(x.verified, false)
|
|
||||||
var c = crypto.randomBytes(32).toString('hex')
|
|
||||||
return x.verify(c)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(
|
|
||||||
function (x) {
|
|
||||||
t.equal(x.verified, false)
|
|
||||||
return x.del()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.done(end, end)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
test(
|
|
||||||
'recoveryEmail.verify will not unset the verified flag from true to false',
|
|
||||||
function (t) {
|
|
||||||
function end() { t.end() }
|
|
||||||
RecoveryEmail.create('xxx', email, true)
|
|
||||||
.then(
|
|
||||||
function (x) {
|
|
||||||
t.equal(x.verified, false)
|
|
||||||
var c = x.code
|
|
||||||
return x.verify(c)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(
|
|
||||||
function (x) {
|
|
||||||
t.equal(x.verified, true)
|
|
||||||
return x.verify('bad1')
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(
|
|
||||||
function (x) {
|
|
||||||
t.equal(x.verified, true)
|
|
||||||
return x.del()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.done(end, end)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
test(
|
|
||||||
'teardown',
|
|
||||||
function (t) {
|
|
||||||
dbs.cache.close()
|
|
||||||
dbs.store.close()
|
|
||||||
t.end()
|
|
||||||
}
|
|
||||||
)
|
|
|
@ -3,15 +3,21 @@ var P = require('p-promise')
|
||||||
var srp = require('srp')
|
var srp = require('srp')
|
||||||
var config = require('../../config').root()
|
var config = require('../../config').root()
|
||||||
var log = { trace: function() {} }
|
var log = { trace: function() {} }
|
||||||
var dbs = require('../../kv')(config, log)
|
|
||||||
|
|
||||||
var mailer = {
|
var Token = require('../../tokens')(log)
|
||||||
sendVerifyCode: function () { return P(null) }
|
var DB = require('../../db/heap')(
|
||||||
}
|
log,
|
||||||
|
Token.error,
|
||||||
|
Token.AuthToken,
|
||||||
|
Token.SessionToken,
|
||||||
|
Token.KeyFetchToken,
|
||||||
|
Token.AccountResetToken,
|
||||||
|
Token.SrpToken,
|
||||||
|
Token.ForgotPasswordToken
|
||||||
|
)
|
||||||
|
var db = new DB()
|
||||||
|
|
||||||
var models = require('../../models')(log, config, dbs, mailer)
|
var SrpToken = Token.SrpToken
|
||||||
var Account = models.Account
|
|
||||||
var SrpSession = models.SrpSession
|
|
||||||
|
|
||||||
var alice = {
|
var alice = {
|
||||||
uid: 'xxx',
|
uid: 'xxx',
|
||||||
|
@ -34,14 +40,14 @@ alice.srp.verifier = srp.getv(
|
||||||
'sha256'
|
'sha256'
|
||||||
).toString('hex')
|
).toString('hex')
|
||||||
|
|
||||||
Account.create(alice)
|
db.createAccount(alice)
|
||||||
.done(
|
.done(
|
||||||
function (a) {
|
function (a) {
|
||||||
|
|
||||||
test(
|
test(
|
||||||
'create login session works',
|
'create login session works',
|
||||||
function (t) {
|
function (t) {
|
||||||
SrpSession.create(a)
|
SrpToken.create(a)
|
||||||
.done(
|
.done(
|
||||||
function (s) {
|
function (s) {
|
||||||
t.equal(s.uid, a.uid)
|
t.equal(s.uid, a.uid)
|
||||||
|
@ -57,17 +63,17 @@ Account.create(alice)
|
||||||
function (t) {
|
function (t) {
|
||||||
var session = null
|
var session = null
|
||||||
var K = null
|
var K = null
|
||||||
SrpSession.create(a)
|
SrpToken.create(a)
|
||||||
.then(
|
.then(
|
||||||
function (s) {
|
function (s) {
|
||||||
session = s
|
session = s
|
||||||
return SrpSession.client2(s.clientData(), alice.email, alice.password)
|
return SrpToken.client2(s.clientData(), alice.email, alice.password)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.then(
|
.then(
|
||||||
function (x) {
|
function (x) {
|
||||||
K = x.K
|
K = x.K
|
||||||
return SrpSession.finish(session.id, x.A, x.M)
|
return session.finish(x.A, x.M)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.done(
|
.done(
|
||||||
|
@ -78,14 +84,5 @@ Account.create(alice)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
test(
|
|
||||||
'teardown',
|
|
||||||
function (t) {
|
|
||||||
dbs.cache.close()
|
|
||||||
dbs.store.close()
|
|
||||||
t.end()
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -56,5 +56,17 @@ module.exports = function (log, inherits, Token, crypto) {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ForgotPasswordToken.prototype.ttl = function () {
|
||||||
|
return Math.max(
|
||||||
|
Math.ceil((LIFETIME - (Date.now() - this.created)) / 1000),
|
||||||
|
0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
ForgotPasswordToken.prototype.failAttempt = function () {
|
||||||
|
this.tries--
|
||||||
|
return this.tries < 1
|
||||||
|
}
|
||||||
|
|
||||||
return ForgotPasswordToken
|
return ForgotPasswordToken
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,6 +81,18 @@ module.exports = function (log, P, uuid, srp, error) {
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SrpToken.prototype.clientData = function () {
|
||||||
|
return {
|
||||||
|
srpToken: this.id,
|
||||||
|
passwordStretching: this.passwordStretching,
|
||||||
|
srp: {
|
||||||
|
type: 'SRP-6a/SHA256/2048/v1',
|
||||||
|
salt: this.s,
|
||||||
|
B: this.B.toString('hex')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SrpToken.client2 = function (session, email, password) {
|
SrpToken.client2 = function (session, email, password) {
|
||||||
return srpGenKey()
|
return srpGenKey()
|
||||||
.then(
|
.then(
|
||||||
|
|
Загрузка…
Ссылка в новой задаче