delete stuff
This commit is contained in:
Родитель
e973ffed85
Коммит
e285cb8ac3
|
@ -40,10 +40,6 @@ function main() {
|
|||
var signer = new CC({ module: __dirname + '/signer.js' })
|
||||
signer.on('error', function () {}) // don't die
|
||||
|
||||
// client_heklper compute-cluster
|
||||
var clientHelper = new CC({ module: __dirname + '/client_helper.js' })
|
||||
clientHelper.on('error', function () {}) // don't die
|
||||
|
||||
var Server = require('../server')
|
||||
var server = null
|
||||
// TODO: send to the SMTP server directly. In the future this may change
|
||||
|
@ -58,11 +54,9 @@ function main() {
|
|||
config.db.backend,
|
||||
log,
|
||||
Token.error,
|
||||
Token.AuthToken,
|
||||
Token.SessionToken,
|
||||
Token.KeyFetchToken,
|
||||
Token.AccountResetToken,
|
||||
Token.SrpToken,
|
||||
Token.PasswordForgotToken,
|
||||
Token.PasswordChangeToken
|
||||
)
|
||||
|
@ -86,7 +80,7 @@ function main() {
|
|||
function (backends) {
|
||||
var db = backends[0]
|
||||
var noncedb = backends[1]
|
||||
var routes = require('../routes')(log, error, serverPublicKey, signer, clientHelper, db, mailer, Token, config)
|
||||
var routes = require('../routes')(log, error, serverPublicKey, signer, db, mailer, config)
|
||||
server = Server.create(log, error, config, routes, db, noncedb, i18n)
|
||||
|
||||
server.start(
|
||||
|
|
109
db/heap.js
109
db/heap.js
|
@ -7,11 +7,9 @@ var P = require('p-promise')
|
|||
module.exports = function (
|
||||
log,
|
||||
error,
|
||||
AuthToken,
|
||||
SessionToken,
|
||||
KeyFetchToken,
|
||||
AccountResetToken,
|
||||
SrpToken,
|
||||
PasswordForgotToken,
|
||||
PasswordChangeToken
|
||||
) {
|
||||
|
@ -20,8 +18,6 @@ module.exports = function (
|
|||
this.sessionTokens = {}
|
||||
this.keyFetchTokens = {}
|
||||
this.accountResetTokens = {}
|
||||
this.authTokens = {}
|
||||
this.srpTokens = {}
|
||||
this.passwordForgotTokens = {}
|
||||
this.passwordChangeTokens = {}
|
||||
this.accounts = {}
|
||||
|
@ -106,18 +102,6 @@ module.exports = function (
|
|||
.then(saveTo(this.accountResetTokens))
|
||||
}
|
||||
|
||||
Heap.prototype.createAuthToken = function (srpToken) {
|
||||
log.trace({ op: 'Heap.createAuthToken', uid: srpToken && srpToken.uid })
|
||||
return AuthToken.create(srpToken)
|
||||
.then(saveTo(this.authTokens))
|
||||
}
|
||||
|
||||
Heap.prototype.createSrpToken = function (emailRecord) {
|
||||
log.trace({ op: 'Heap.createSrpToken', uid: emailRecord && emailRecord.uid })
|
||||
return SrpToken.create(emailRecord)
|
||||
.then(saveTo(this.srpTokens))
|
||||
}
|
||||
|
||||
Heap.prototype.createPasswordForgotToken = function (emailRecord) {
|
||||
log.trace({ op: 'Heap.createPasswordForgotToken', uid: emailRecord && emailRecord.uid })
|
||||
return PasswordForgotToken.create(emailRecord)
|
||||
|
@ -198,23 +182,6 @@ module.exports = function (
|
|||
return P(accountResetToken)
|
||||
}
|
||||
|
||||
Heap.prototype.authToken = function (id) {
|
||||
log.trace({ op: 'Heap.authToken', id: id })
|
||||
var authToken = this.authTokens[id.toString('hex')]
|
||||
if (!authToken) { return P.reject(error.invalidToken()) }
|
||||
var account = this.accounts[authToken.uid.toString('hex')]
|
||||
if (!account) { return P.reject(error.unknownAccount()) }
|
||||
authToken.verified = account.verified
|
||||
return P(authToken)
|
||||
}
|
||||
|
||||
Heap.prototype.srpToken = function (id) {
|
||||
log.trace({ op: 'Heap.srpToken', id: id })
|
||||
var srpToken = this.srpTokens[id.toString('hex')]
|
||||
if (!srpToken) { return P.reject(error.invalidToken()) }
|
||||
return P(srpToken)
|
||||
}
|
||||
|
||||
Heap.prototype.passwordForgotToken = function (id) {
|
||||
log.trace({ op: 'Heap.passwordForgotToken', id: id })
|
||||
var passwordForgotToken = this.passwordForgotTokens[id.toString('hex')]
|
||||
|
@ -261,8 +228,6 @@ module.exports = function (
|
|||
if (!account) { return P.reject(error.unknownAccount()) }
|
||||
deleteUid(account.uid, this.sessionTokens)
|
||||
deleteUid(account.uid, this.keyFetchTokens)
|
||||
deleteUid(account.uid, this.authTokens)
|
||||
deleteUid(account.uid, this.srpTokens)
|
||||
deleteUid(account.uid, this.accountResetTokens)
|
||||
deleteUid(account.uid, this.passwordForgotTokens)
|
||||
delete this.emailRecords[account.email]
|
||||
|
@ -309,30 +274,6 @@ module.exports = function (
|
|||
return P(true)
|
||||
}
|
||||
|
||||
Heap.prototype.deleteAuthToken = function (authToken) {
|
||||
log.trace(
|
||||
{
|
||||
op: 'Heap.deleteAuthToken',
|
||||
id: authToken && authToken.id,
|
||||
uid: authToken && authToken.uid
|
||||
}
|
||||
)
|
||||
delete this.authTokens[authToken.id]
|
||||
return P(true)
|
||||
}
|
||||
|
||||
Heap.prototype.deleteSrpToken = function (srpToken) {
|
||||
log.trace(
|
||||
{
|
||||
op: 'Heap.deleteSrpToken',
|
||||
id: srpToken && srpToken.id,
|
||||
uid: srpToken && srpToken.uid
|
||||
}
|
||||
)
|
||||
delete this.srpTokens[srpToken.id]
|
||||
return P(true)
|
||||
}
|
||||
|
||||
Heap.prototype.deletePasswordForgotToken = function (passwordForgotToken) {
|
||||
log.trace(
|
||||
{
|
||||
|
@ -371,67 +312,17 @@ module.exports = function (
|
|||
account.passwordForgotToken = null
|
||||
deleteUid(account.uid, this.sessionTokens)
|
||||
deleteUid(account.uid, this.keyFetchTokens)
|
||||
deleteUid(account.uid, this.authTokens)
|
||||
deleteUid(account.uid, this.srpTokens)
|
||||
deleteUid(account.uid, this.accountResetTokens)
|
||||
deleteUid(account.uid, this.passwordForgotTokens)
|
||||
return P(true)
|
||||
}
|
||||
|
||||
Heap.prototype.authFinish = function (srpToken) {
|
||||
log.trace({ op: 'Heap.authFinish', uid: srpToken && srpToken.uid })
|
||||
return this.deleteSrpToken(srpToken)
|
||||
.then(this.createAuthToken.bind(this, srpToken))
|
||||
}
|
||||
|
||||
Heap.prototype.createSession = function (authToken) {
|
||||
log.trace({ op: 'Heap.createSession', uid: authToken && authToken.uid })
|
||||
return this.deleteAuthToken(authToken)
|
||||
.then(
|
||||
function () {
|
||||
return P.all([
|
||||
this.createKeyFetchToken(authToken),
|
||||
this.createSessionToken(authToken)
|
||||
])
|
||||
}.bind(this)
|
||||
)
|
||||
.then(
|
||||
function (tokens) {
|
||||
return {
|
||||
keyFetchToken: tokens[0],
|
||||
sessionToken: tokens[1]
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
Heap.prototype.verifyEmail = function (account) {
|
||||
log.trace({ op: 'Heap.verifyEmail', uid: account && account.uid })
|
||||
account.verified = true
|
||||
return P(true)
|
||||
}
|
||||
|
||||
Heap.prototype.createPasswordChange = function (authToken) {
|
||||
log.trace({ op: 'Heap.createPasswordChange', uid: authToken && authToken.uid })
|
||||
return this.deleteAuthToken(authToken)
|
||||
.then(
|
||||
function () {
|
||||
return P.all([
|
||||
this.createKeyFetchToken(authToken),
|
||||
this.createAccountResetToken(authToken)
|
||||
])
|
||||
}.bind(this)
|
||||
)
|
||||
.then(
|
||||
function (tokens) {
|
||||
return {
|
||||
keyFetchToken: tokens[0],
|
||||
accountResetToken: tokens[1]
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
Heap.prototype.forgotPasswordVerified = function (passwordForgotToken) {
|
||||
log.trace({ op: 'Heap.forgotPasswordVerified', uid: passwordForgotToken && passwordForgotToken.uid })
|
||||
return this.deletePasswordForgotToken(passwordForgotToken)
|
||||
|
|
|
@ -6,11 +6,9 @@ module.exports = function (
|
|||
backend,
|
||||
log,
|
||||
error,
|
||||
AuthToken,
|
||||
SessionToken,
|
||||
KeyFetchToken,
|
||||
AccountResetToken,
|
||||
SrpToken,
|
||||
PasswordForgotToken,
|
||||
PasswordChangeToken) {
|
||||
|
||||
|
@ -18,11 +16,9 @@ module.exports = function (
|
|||
return require('./mysql')(
|
||||
log,
|
||||
error,
|
||||
AuthToken,
|
||||
SessionToken,
|
||||
KeyFetchToken,
|
||||
AccountResetToken,
|
||||
SrpToken,
|
||||
PasswordForgotToken,
|
||||
PasswordChangeToken
|
||||
)
|
||||
|
@ -31,11 +27,9 @@ module.exports = function (
|
|||
return require('./heap')(
|
||||
log,
|
||||
error,
|
||||
AuthToken,
|
||||
SessionToken,
|
||||
KeyFetchToken,
|
||||
AccountResetToken,
|
||||
SrpToken,
|
||||
PasswordForgotToken,
|
||||
PasswordChangeToken
|
||||
)
|
||||
|
|
346
db/mysql.js
346
db/mysql.js
|
@ -9,11 +9,9 @@ var schema = require('fs').readFileSync(__dirname + '/schema.sql', { encoding: '
|
|||
module.exports = function (
|
||||
log,
|
||||
error,
|
||||
AuthToken,
|
||||
SessionToken,
|
||||
KeyFetchToken,
|
||||
AccountResetToken,
|
||||
SrpToken,
|
||||
PasswordForgotToken,
|
||||
PasswordChangeToken
|
||||
) {
|
||||
|
@ -235,55 +233,6 @@ module.exports = function (
|
|||
}.bind(this))
|
||||
}
|
||||
|
||||
MySql.prototype.createAuthToken = function (srpToken) {
|
||||
log.trace({ op: 'MySql.createAuthToken', uid: srpToken && srpToken.uid })
|
||||
var sql = 'INSERT INTO authTokens (tokenid, tokendata, uid) VALUES (?, ?, ?)'
|
||||
var con
|
||||
return this.getMasterConnection()
|
||||
.then(function(thisCon) {
|
||||
con = thisCon
|
||||
return AuthToken.create(srpToken)
|
||||
})
|
||||
.then(function(authToken) {
|
||||
var d = P.defer()
|
||||
con.query(
|
||||
sql,
|
||||
[authToken.tokenid, authToken.data, authToken.uid],
|
||||
function (err) {
|
||||
con.release()
|
||||
if (err) return d.reject(err)
|
||||
d.resolve(authToken)
|
||||
}
|
||||
)
|
||||
return d.promise
|
||||
}.bind(this))
|
||||
}
|
||||
|
||||
MySql.prototype.createSrpToken = function (emailRecord) {
|
||||
log.trace({ op: 'MySql.createSrpToken', uid: emailRecord && emailRecord.uid })
|
||||
var sql = 'INSERT INTO srpTokens (tokenid, tokendata, uid) VALUES (?, ?, ?)'
|
||||
|
||||
var con
|
||||
return this.getMasterConnection()
|
||||
.then(function(thisCon) {
|
||||
con = thisCon
|
||||
return SrpToken.create(emailRecord)
|
||||
})
|
||||
.then(function (srpToken) {
|
||||
var d = P.defer()
|
||||
con.query(
|
||||
sql,
|
||||
[srpToken.tokenid, srpToken.data, srpToken.uid],
|
||||
function (err) {
|
||||
con.release()
|
||||
if (err) return d.reject(err)
|
||||
d.resolve(srpToken)
|
||||
}
|
||||
)
|
||||
return d.promise
|
||||
}.bind(this))
|
||||
}
|
||||
|
||||
MySql.prototype.createPasswordForgotToken = function (emailRecord) {
|
||||
log.trace({ op: 'MySql.createPasswordForgotToken', uid: emailRecord && emailRecord.uid })
|
||||
var sql = 'REPLACE INTO passwordForgotTokens (tokenid, tokendata, uid, passcode, created, tries) VALUES (?, ?, ?, ?, ?, ?)'
|
||||
|
@ -464,64 +413,6 @@ module.exports = function (
|
|||
})
|
||||
}
|
||||
|
||||
MySql.prototype.authToken = function (id) {
|
||||
log.trace({ op: 'MySql.authToken', id: id })
|
||||
var sql = 'SELECT t.uid, t.tokendata, a.verified' +
|
||||
' FROM authTokens t, accounts a' +
|
||||
' WHERE t.tokenid = ? AND t.uid = a.uid'
|
||||
return this.getSlaveConnection()
|
||||
.then(function(con) {
|
||||
var d = P.defer()
|
||||
con.query(
|
||||
sql,
|
||||
[id],
|
||||
function (err, results) {
|
||||
con.release()
|
||||
if (err) return d.reject(err)
|
||||
if (!results.length) return d.reject(error.invalidToken())
|
||||
var result = results[0]
|
||||
AuthToken.fromHex(result.tokendata, result)
|
||||
.done(
|
||||
function (authToken) {
|
||||
return d.resolve(authToken)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
return d.promise
|
||||
})
|
||||
}
|
||||
|
||||
MySql.prototype.srpToken = function (id) {
|
||||
log.trace({ op: 'MySql.srpToken', id: id })
|
||||
var sql = 'SELECT t.tokendata, t.uid, a.srp, a.passwordStretching ' +
|
||||
' FROM srpTokens t, accounts a ' +
|
||||
' WHERE t.tokenid = ? AND t.uid = a.uid'
|
||||
return this.getSlaveConnection()
|
||||
.then(function(con) {
|
||||
var d = P.defer()
|
||||
con.query(
|
||||
sql,
|
||||
[id],
|
||||
function (err, results) {
|
||||
con.release()
|
||||
if (err) return d.reject(err)
|
||||
if (!results.length) return d.reject(error.invalidToken())
|
||||
var result = results[0]
|
||||
result.srp = JSON.parse(result.srp)
|
||||
result.passwordStretching = JSON.parse(result.passwordStretching)
|
||||
SrpToken.fromHex(result.tokendata, result)
|
||||
.done(
|
||||
function (srpToken) {
|
||||
return d.resolve(srpToken)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
return d.promise
|
||||
})
|
||||
}
|
||||
|
||||
MySql.prototype.passwordForgotToken = function (id) {
|
||||
log.trace({ op: 'MySql.passwordForgotToken', id: id })
|
||||
var sql = 'SELECT t.tokendata, t.uid, a.email, t.passcode, t.created, t.tries ' +
|
||||
|
@ -760,56 +651,6 @@ module.exports = function (
|
|||
})
|
||||
}
|
||||
|
||||
MySql.prototype.deleteAuthToken = function (authToken) {
|
||||
log.trace(
|
||||
{
|
||||
op: 'MySql.deleteAuthToken',
|
||||
id: authToken && authToken.tokenid,
|
||||
uid: authToken && authToken.uid
|
||||
}
|
||||
)
|
||||
var sql = 'DELETE FROM authTokens WHERE tokenid = ?'
|
||||
return this.getMasterConnection()
|
||||
.then(function(con) {
|
||||
var d = P.defer()
|
||||
con.query(
|
||||
sql,
|
||||
[authToken.tokenid],
|
||||
function (err) {
|
||||
con.release()
|
||||
if (err) return d.reject(err)
|
||||
d.resolve(true)
|
||||
}
|
||||
)
|
||||
return d.promise
|
||||
})
|
||||
}
|
||||
|
||||
MySql.prototype.deleteSrpToken = function (srpToken) {
|
||||
log.trace(
|
||||
{
|
||||
op: 'MySql.deleteSrpToken',
|
||||
id: srpToken && srpToken.tokenid,
|
||||
uid: srpToken && srpToken.uid
|
||||
}
|
||||
)
|
||||
var sql = 'DELETE FROM srpTokens WHERE tokenid = ?'
|
||||
return this.getMasterConnection()
|
||||
.then(function(con) {
|
||||
var d = P.defer()
|
||||
con.query(
|
||||
sql,
|
||||
[srpToken.tokenid],
|
||||
function (err) {
|
||||
con.release()
|
||||
if (err) return d.reject(err)
|
||||
d.resolve(true)
|
||||
}
|
||||
)
|
||||
return d.promise
|
||||
})
|
||||
}
|
||||
|
||||
MySql.prototype.deletePasswordForgotToken = function (passwordForgotToken) {
|
||||
log.trace(
|
||||
{
|
||||
|
@ -905,126 +746,6 @@ module.exports = function (
|
|||
})
|
||||
}
|
||||
|
||||
MySql.prototype.authFinish = function (srpToken) {
|
||||
log.trace({ op: 'MySql.authFinish', uid: srpToken && srpToken.uid })
|
||||
// Order of events:
|
||||
// (1) make an AuthToken
|
||||
// (2) get a connection
|
||||
// (3) start a transaction
|
||||
// (4) delete from srpTokens
|
||||
// (5) insert into authTokens
|
||||
// (6) commit transaction
|
||||
// (7) release connection
|
||||
// (8) resolve with the new authToken
|
||||
var con
|
||||
var authToken
|
||||
return this.getMasterConnection()
|
||||
.then(function(thisCon) {
|
||||
con = thisCon
|
||||
return beginTransaction(con)
|
||||
})
|
||||
.then(function() {
|
||||
return AuthToken.create(srpToken)
|
||||
})
|
||||
.then(function (newAuthToken) {
|
||||
authToken = newAuthToken
|
||||
var d = P.defer()
|
||||
con.query(
|
||||
'DELETE FROM srpTokens WHERE tokenid = ?',
|
||||
[srpToken.tokenid],
|
||||
function(err) {
|
||||
if (err) d.reject(err)
|
||||
d.resolve()
|
||||
}
|
||||
)
|
||||
return d.promise
|
||||
})
|
||||
.then(function() {
|
||||
var d = P.defer()
|
||||
con.query(
|
||||
'INSERT INTO authTokens(tokenid, tokendata, uid) VALUES (?, ?, ?)',
|
||||
[authToken.tokenid, authToken.data, authToken.uid],
|
||||
function(err) {
|
||||
if (err) d.reject(err)
|
||||
d.resolve(authToken)
|
||||
}
|
||||
)
|
||||
return d.promise
|
||||
})
|
||||
.then(function() {
|
||||
return commitTransaction(con).then(function() {
|
||||
con.release()
|
||||
})
|
||||
})
|
||||
.then(function() {
|
||||
return P(authToken)
|
||||
})
|
||||
}
|
||||
|
||||
MySql.prototype.createSession = function (authToken) {
|
||||
log.trace({ op: 'MySql.createSession', uid: authToken && authToken.uid })
|
||||
var con
|
||||
return P.all(
|
||||
[
|
||||
KeyFetchToken.create(authToken),
|
||||
SessionToken.create(authToken)
|
||||
]
|
||||
)
|
||||
.then(function (tokens) {
|
||||
var keyFetchToken = tokens[0]
|
||||
var sessionToken = tokens[1]
|
||||
|
||||
return this.getMasterConnection()
|
||||
.then(function(thisCon) {
|
||||
con = thisCon
|
||||
return beginTransaction(con)
|
||||
})
|
||||
.then(function(thisCon) {
|
||||
var d = P.defer()
|
||||
con.query(
|
||||
'DELETE FROM authTokens WHERE tokenid = ?',
|
||||
[authToken.tokenid],
|
||||
function(err) {
|
||||
if (err) return d.reject(err)
|
||||
d.resolve()
|
||||
}
|
||||
)
|
||||
return d.promise
|
||||
})
|
||||
.then(function() {
|
||||
var d = P.defer()
|
||||
con.query(
|
||||
'INSERT INTO keyfetchTokens (tokenid, tokendata, uid) VALUES (?, ?, ?)',
|
||||
[keyFetchToken.tokenid, keyFetchToken.data, keyFetchToken.uid],
|
||||
function(err) {
|
||||
if (err) return d.reject(err)
|
||||
d.resolve()
|
||||
}
|
||||
)
|
||||
return d.promise
|
||||
})
|
||||
.then(function() {
|
||||
var d = P.defer()
|
||||
con.query(
|
||||
'INSERT INTO sessionTokens (tokenid, tokendata, uid) VALUES (?, ?, ?)',
|
||||
[sessionToken.tokenid, sessionToken.data, sessionToken.uid],
|
||||
function(err) {
|
||||
if (err) return d.reject(err)
|
||||
// now commit and release
|
||||
commitTransaction(con).then(function() {
|
||||
con.release()
|
||||
d.resolve({
|
||||
keyFetchToken: keyFetchToken,
|
||||
sessionToken: sessionToken
|
||||
})
|
||||
})
|
||||
}
|
||||
)
|
||||
return d.promise
|
||||
})
|
||||
}.bind(this))
|
||||
}
|
||||
|
||||
MySql.prototype.verifyEmail = function (account) {
|
||||
log.trace({ op: 'MySql.verifyEmail', uid: account && account.uid })
|
||||
var sql = 'UPDATE accounts SET verified = true WHERE uid = ?'
|
||||
|
@ -1044,73 +765,6 @@ module.exports = function (
|
|||
})
|
||||
}
|
||||
|
||||
MySql.prototype.createPasswordChange = function (authToken) {
|
||||
log.trace({ op: 'MySql.createPasswordChange', uid: authToken && authToken.uid })
|
||||
var con
|
||||
return P.all(
|
||||
[
|
||||
KeyFetchToken.create(authToken),
|
||||
AccountResetToken.create(authToken)
|
||||
]
|
||||
)
|
||||
.then(function (tokens) {
|
||||
var keyFetchToken = tokens[0]
|
||||
var accountResetToken = tokens[1]
|
||||
|
||||
return this.getMasterConnection()
|
||||
.then(function(thisCon) {
|
||||
con = thisCon
|
||||
return beginTransaction(con)
|
||||
})
|
||||
.then(function(thisCon) {
|
||||
var d = P.defer()
|
||||
con.query(
|
||||
'DELETE FROM authTokens WHERE tokenid = ?',
|
||||
[authToken.tokenid],
|
||||
function(err) {
|
||||
if (err) return d.reject(err)
|
||||
d.resolve()
|
||||
}
|
||||
)
|
||||
return d.promise
|
||||
})
|
||||
.then(function() {
|
||||
var d = P.defer()
|
||||
con.query(
|
||||
'INSERT INTO keyfetchTokens (tokenid, tokendata, uid) VALUES (?, ?, ?)',
|
||||
[keyFetchToken.tokenid, keyFetchToken.data, keyFetchToken.uid],
|
||||
function(err) {
|
||||
if (err) return d.reject(err)
|
||||
d.resolve()
|
||||
}
|
||||
)
|
||||
return d.promise
|
||||
})
|
||||
.then(function() {
|
||||
var d = P.defer()
|
||||
con.query(
|
||||
'REPLACE INTO resetTokens (tokenid, tokendata, uid) VALUES (?, ?, ?)',
|
||||
[accountResetToken.tokenid, accountResetToken.data, accountResetToken.uid],
|
||||
function(err) {
|
||||
if (err) return d.reject(err)
|
||||
d.resolve()
|
||||
}
|
||||
)
|
||||
return d.promise
|
||||
})
|
||||
.then(function() {
|
||||
return commitTransaction(con)
|
||||
})
|
||||
.then(function() {
|
||||
con.release()
|
||||
return P({
|
||||
keyFetchToken: keyFetchToken,
|
||||
accountResetToken: accountResetToken
|
||||
})
|
||||
})
|
||||
}.bind(this))
|
||||
}
|
||||
|
||||
MySql.prototype.forgotPasswordVerified = function (passwordForgotToken) {
|
||||
log.trace({ op: 'MySql.forgotPasswordVerified', uid: passwordForgotToken && passwordForgotToken.uid })
|
||||
|
||||
|
|
119
routes/auth.js
119
routes/auth.js
|
@ -1,119 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* 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/. */
|
||||
|
||||
var HEX_STRING = require('./validators').HEX_STRING
|
||||
var HEX_EMAIL = require('./validators').HEX_EMAIL
|
||||
|
||||
module.exports = function (log, isA, error, db, Token) {
|
||||
|
||||
var routes = [
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/auth/start',
|
||||
config: {
|
||||
description:
|
||||
"Begins an SRP login for the supplied email address, " +
|
||||
"returning the temporary srpToken and parameters for " +
|
||||
"key stretching and the SRP protocol for the client.",
|
||||
tags: ["srp", "account"],
|
||||
handler: function (request) {
|
||||
log.begin('Auth.start', request)
|
||||
var reply = request.reply.bind(request)
|
||||
db.emailRecord(Buffer(request.payload.email, 'hex').toString())
|
||||
.then(
|
||||
function (emailRecord) {
|
||||
return db.createSrpToken(emailRecord)
|
||||
}
|
||||
)
|
||||
.then(
|
||||
function (srpToken) {
|
||||
return srpToken.clientData()
|
||||
}
|
||||
)
|
||||
.done(reply, reply)
|
||||
},
|
||||
validate: {
|
||||
payload: {
|
||||
email: isA.String().max(1024).regex(HEX_EMAIL).required()
|
||||
},
|
||||
response: {
|
||||
schema: {
|
||||
srpToken: isA.String().required(),
|
||||
passwordStretching: isA.Object(),
|
||||
srp: isA.Object({
|
||||
type: isA.String().required(),
|
||||
salt: isA.String().regex(HEX_STRING), // salt
|
||||
B: isA.String().regex(HEX_STRING) // server's public key value
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/auth/finish',
|
||||
config: {
|
||||
description:
|
||||
"Finishes the SRP dance, with the client providing " +
|
||||
"proof-of-knownledge of the password and receiving " +
|
||||
"the bundle encrypted with the authToken.",
|
||||
tags: ["srp", "session"],
|
||||
handler: function (request) {
|
||||
log.begin('Auth.finish', request)
|
||||
var reply = request.reply.bind(request)
|
||||
var srpTokenId = Buffer(request.payload.srpToken, 'hex')
|
||||
var srpToken = null;
|
||||
db.srpToken(srpTokenId)
|
||||
.then(
|
||||
function (token) {
|
||||
srpToken = token
|
||||
return srpToken.finish(request.payload.A, request.payload.M)
|
||||
}
|
||||
)
|
||||
.then(
|
||||
function (srpToken) {
|
||||
log.security({ event: 'login-success', uid: srpToken.uid });
|
||||
return srpToken
|
||||
},
|
||||
function (err) {
|
||||
log.security({ event: 'login-failure', err: err, uid: srpToken.uid });
|
||||
throw err
|
||||
}
|
||||
)
|
||||
.then(
|
||||
function (srpToken) {
|
||||
return db.authFinish(srpToken)
|
||||
.then(
|
||||
function (authToken) {
|
||||
return srpToken.bundleAuth(authToken.data)
|
||||
}
|
||||
)
|
||||
.then(
|
||||
function (bundle) {
|
||||
return {bundle: bundle}
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
.done(reply, reply)
|
||||
},
|
||||
validate: {
|
||||
payload: {
|
||||
srpToken: isA.String().regex(HEX_STRING).required(),
|
||||
A: isA.String().regex(HEX_STRING).required(),
|
||||
M: isA.String().regex(HEX_STRING).required()
|
||||
},
|
||||
response: {
|
||||
schema: {
|
||||
bundle: isA.String().regex(HEX_STRING).required()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
return routes
|
||||
}
|
|
@ -14,14 +14,11 @@ module.exports = function (
|
|||
error,
|
||||
serverPublicKey,
|
||||
signer,
|
||||
clientHelper,
|
||||
db,
|
||||
mailer,
|
||||
Token,
|
||||
config
|
||||
) {
|
||||
var isProduction = config.env === 'prod'
|
||||
var auth = require('./auth')(log, isA, error, db, Token)
|
||||
var defaults = require('./defaults')(log, P, db)
|
||||
var idp = require('./idp')(log, serverPublicKey)
|
||||
var account = require('./account')(log, crypto, P, uuid, isA, error, db, mailer, isProduction)
|
||||
|
@ -29,16 +26,13 @@ module.exports = function (
|
|||
var session = require('./session')(log, isA, error, db)
|
||||
var sign = require('./sign')(log, isA, error, signer, config.domain)
|
||||
var util = require('./util')(log, crypto, isA, config)
|
||||
var raw = require('./rawpassword')(log, isA, error, clientHelper, crypto, db, isProduction)
|
||||
|
||||
var v1Routes = [].concat(
|
||||
auth,
|
||||
account,
|
||||
password,
|
||||
session,
|
||||
sign,
|
||||
util,
|
||||
raw
|
||||
util
|
||||
)
|
||||
v1Routes.forEach(function(route) {
|
||||
route.path = "/v1" + route.path
|
||||
|
|
|
@ -1,193 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* 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/. */
|
||||
|
||||
var HEX_STRING = require('./validators').HEX_STRING
|
||||
var HEX_EMAIL = require('./validators').HEX_EMAIL
|
||||
|
||||
module.exports = function (log, isA, error, clientHelper, crypto, db, isProduction) {
|
||||
|
||||
function enqueueWithHelper(message, cb) {
|
||||
clientHelper.enqueue(message, function(err, result) {
|
||||
if (err) {
|
||||
log.error({ op: 'clientHelper.enqueue', err: err, result: result })
|
||||
return cb(error.serviceUnavailable())
|
||||
}
|
||||
if (result && result.err) {
|
||||
return cb(result.err)
|
||||
}
|
||||
return cb(null, result)
|
||||
})
|
||||
}
|
||||
|
||||
var routes = [
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/raw_password/session/create',
|
||||
config: {
|
||||
description: 'Create a session from an email and password',
|
||||
tags: ['raw', 'session'],
|
||||
handler: function (request) {
|
||||
log.begin('RawPassword.sessionCreate', request)
|
||||
enqueueWithHelper(
|
||||
{
|
||||
action: 'session/create',
|
||||
email: Buffer(request.payload.email, 'hex').toString('utf8'),
|
||||
password: request.payload.password
|
||||
},
|
||||
function (err, result) {
|
||||
if (err) {
|
||||
return request.reply(error.wrap(err))
|
||||
}
|
||||
return request.reply(result)
|
||||
}
|
||||
)
|
||||
},
|
||||
validate: {
|
||||
payload: {
|
||||
email: isA.String().max(1024).regex(HEX_EMAIL).required(),
|
||||
password: isA.String().required()
|
||||
},
|
||||
response: {
|
||||
schema: {
|
||||
uid: isA.String().required(),
|
||||
verified: isA.Boolean().required(),
|
||||
sessionToken: isA.String().regex(HEX_STRING).required()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/raw_password/account/create',
|
||||
config: {
|
||||
description: 'Creates an account associated with an email address',
|
||||
tags: ['raw', 'account'],
|
||||
handler: function accountCreate(request) {
|
||||
log.begin('RawPassword.accountCreate', request)
|
||||
enqueueWithHelper(
|
||||
{
|
||||
action: 'account/create',
|
||||
email: Buffer(request.payload.email, 'hex').toString('utf8'),
|
||||
password: request.payload.password,
|
||||
options: {
|
||||
preVerified: request.payload.preVerified || false,
|
||||
service: request.payload.service,
|
||||
lang: request.app.preferredLang
|
||||
}
|
||||
},
|
||||
function (err, result) {
|
||||
if (err) {
|
||||
return request.reply(error.wrap(err))
|
||||
}
|
||||
return request.reply(result)
|
||||
}
|
||||
)
|
||||
},
|
||||
validate: {
|
||||
payload: {
|
||||
email: isA.String().max(1024).regex(HEX_EMAIL).required(),
|
||||
password: isA.String().required(),
|
||||
preVerified: isA.Boolean(),
|
||||
service: isA.String().max(16).alphanum().optional()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/raw_password/password/change',
|
||||
config: {
|
||||
description: 'Creates an account associated with an email address',
|
||||
tags: ['raw', 'account'],
|
||||
handler: function passwordChange(request) {
|
||||
log.begin('RawPassword.passwordChange', request)
|
||||
enqueueWithHelper(
|
||||
{
|
||||
action: 'password/change',
|
||||
email: Buffer(request.payload.email, 'hex').toString('utf8'),
|
||||
oldPassword: request.payload.oldPassword,
|
||||
newPassword: request.payload.newPassword
|
||||
},
|
||||
function (err, result) {
|
||||
if (err) {
|
||||
err = error.wrap(err)
|
||||
log.security({ event: 'pwd-change-failure', err: err })
|
||||
return request.reply(err);
|
||||
}
|
||||
log.security({ event: 'pwd-change-success' })
|
||||
return request.reply(result)
|
||||
}
|
||||
)
|
||||
},
|
||||
validate: {
|
||||
payload: {
|
||||
email: isA.String().max(1024).regex(HEX_EMAIL).required(),
|
||||
oldPassword: isA.String().required(),
|
||||
newPassword: isA.String().required()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/raw_password/password/reset',
|
||||
config: {
|
||||
description: 'Resets the password associated with an account',
|
||||
auth: {
|
||||
strategy: 'accountResetToken'
|
||||
},
|
||||
tags: ['raw', 'account'],
|
||||
handler: function passwordReset(request) {
|
||||
log.begin('RawPassword.passwordReset', request)
|
||||
var accountResetToken = request.auth.credentials
|
||||
var form = request.payload
|
||||
|
||||
db.account(accountResetToken.uid)
|
||||
.then(
|
||||
function (account) {
|
||||
enqueueWithHelper(
|
||||
{
|
||||
action: 'password/reset',
|
||||
email: account.email,
|
||||
newPassword: form.newPassword,
|
||||
passwordStretching: account.passwordStretching
|
||||
},
|
||||
function (err, result) {
|
||||
if (err) {
|
||||
err = error.wrap(err)
|
||||
log.security({ event: 'pwd-reset-failure', err: err })
|
||||
return request.reply(err)
|
||||
}
|
||||
log.security({ event: 'pwd-reset-success' })
|
||||
result.wrapKb = crypto.randomBytes(32)
|
||||
db.resetAccount(accountResetToken, result)
|
||||
.done(
|
||||
function () {
|
||||
return request.reply({})
|
||||
},
|
||||
function (err) {
|
||||
return request.reply(error.wrap(err))
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
},
|
||||
validate: {
|
||||
payload: {
|
||||
newPassword: isA.String().required()
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
]
|
||||
|
||||
if (isProduction) {
|
||||
delete routes[1].config.validate.payload.preVerified
|
||||
}
|
||||
|
||||
return routes
|
||||
}
|
|
@ -5,42 +5,6 @@
|
|||
module.exports = function (log, isA, error, db) {
|
||||
|
||||
var routes = [
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/session/create',
|
||||
config: {
|
||||
description: "Creates a new session",
|
||||
tags: ["session"],
|
||||
auth: {
|
||||
strategy: 'authToken'
|
||||
},
|
||||
handler: function (request) {
|
||||
log.begin('Session.create', request)
|
||||
var reply = request.reply.bind(request)
|
||||
var authToken = request.auth.credentials
|
||||
log.security({ event: 'session-create' })
|
||||
db.createSession(authToken)
|
||||
.then(
|
||||
function (tokens) {
|
||||
return authToken.bundleSession(
|
||||
tokens.keyFetchToken.data,
|
||||
tokens.sessionToken.data
|
||||
)
|
||||
}
|
||||
)
|
||||
.then(
|
||||
function (bundle) {
|
||||
return {
|
||||
uid: authToken.uid.toString('hex'),
|
||||
verified: authToken.verified,
|
||||
bundle: bundle
|
||||
}
|
||||
}
|
||||
)
|
||||
.done(reply, reply)
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/session/destroy',
|
||||
|
|
|
@ -71,11 +71,6 @@ module.exports = function (path, url, Hapi, toobusy) {
|
|||
hawk: hawkOptions,
|
||||
getCredentialsFunc: makeCredentialFn(db.accountResetToken.bind(db))
|
||||
},
|
||||
authToken: {
|
||||
scheme: 'hawk',
|
||||
hawk: hawkOptions,
|
||||
getCredentialsFunc: makeCredentialFn(db.authToken.bind(db))
|
||||
},
|
||||
passwordForgotToken: {
|
||||
scheme: 'hawk',
|
||||
hawk: hawkOptions,
|
||||
|
|
|
@ -1,148 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* 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/. */
|
||||
|
||||
var test = require('../ptaptest')
|
||||
var crypto = require('crypto')
|
||||
var log = { trace: function() {} }
|
||||
|
||||
var tokens = require('../../tokens')(log)
|
||||
var AuthToken = tokens.AuthToken
|
||||
|
||||
var ACCOUNT = {
|
||||
uid: 'xxx'
|
||||
}
|
||||
|
||||
|
||||
test(
|
||||
're-creation from tokendata works',
|
||||
function (t) {
|
||||
var token = null;
|
||||
return AuthToken.create(ACCOUNT)
|
||||
.then(
|
||||
function (x) {
|
||||
token = x
|
||||
}
|
||||
)
|
||||
.then(
|
||||
function () {
|
||||
return AuthToken.fromHex(token.data, ACCOUNT)
|
||||
}
|
||||
)
|
||||
.then(
|
||||
function (token2) {
|
||||
t.deepEqual(token.data, token2.data)
|
||||
t.deepEqual(token.id, token2.id)
|
||||
t.deepEqual(token.authKey, token2.authKey)
|
||||
t.deepEqual(token.bundleKey, token2.bundleKey)
|
||||
t.deepEqual(token.uid, token2.uid)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
test(
|
||||
'bundle / unbundle of session data works',
|
||||
function (t) {
|
||||
var token = null;
|
||||
var keyFetchToken = crypto.randomBytes(32)
|
||||
var sessionToken = crypto.randomBytes(32)
|
||||
return AuthToken.create(ACCOUNT)
|
||||
.then(
|
||||
function (x) {
|
||||
token = x
|
||||
return x.bundleSession(keyFetchToken, sessionToken)
|
||||
}
|
||||
)
|
||||
.then(
|
||||
function (b) {
|
||||
return token.unbundleSession(b)
|
||||
}
|
||||
)
|
||||
.then(
|
||||
function (ub) {
|
||||
t.deepEqual(ub.keyFetchToken, keyFetchToken)
|
||||
t.deepEqual(ub.sessionToken, sessionToken)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
test(
|
||||
'bundle / unbundle of account reset data works',
|
||||
function (t) {
|
||||
var token = null;
|
||||
var keyFetchToken = crypto.randomBytes(32)
|
||||
var resetToken = crypto.randomBytes(32)
|
||||
return AuthToken.create(ACCOUNT)
|
||||
.then(
|
||||
function (x) {
|
||||
token = x
|
||||
return x.bundleAccountReset(keyFetchToken, resetToken)
|
||||
}
|
||||
)
|
||||
.then(
|
||||
function (b) {
|
||||
return token.unbundleAccountReset(b)
|
||||
}
|
||||
)
|
||||
.then(
|
||||
function (ub) {
|
||||
t.deepEqual(ub.keyFetchToken, keyFetchToken)
|
||||
t.deepEqual(ub.accountResetToken, resetToken)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'authToken key derivations are test-vector compliant',
|
||||
function (t) {
|
||||
var token = null;
|
||||
var tokendata = '606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f'
|
||||
return AuthToken.fromHex(tokendata, ACCOUNT)
|
||||
.then(
|
||||
function (x) {
|
||||
token = x
|
||||
t.equal(token.data.toString('hex'), tokendata)
|
||||
t.equal(token.id.toString('hex'), '9a39818e3bbe613238c9d7ff013a18411ed2c66c3565c3c4de03feefecb7d212')
|
||||
t.equal(token.authKey.toString('hex'), '4a17cbdd54ee17db426fcd7baddff587231d7eadb408c091ce19ca915b715985')
|
||||
t.equal(token.bundleKey.toString('hex'), '9d93978e662bfc6e8cc203fa4628ef5a7bf1ddfd7ee54e97ec5c033257b4fca9')
|
||||
}
|
||||
)
|
||||
.then(
|
||||
function () {
|
||||
var keyFetchToken = Buffer('808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f', 'hex')
|
||||
var sessionToken = Buffer('a0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf', 'hex')
|
||||
return token.bundleSession(keyFetchToken, sessionToken)
|
||||
}
|
||||
)
|
||||
.then(
|
||||
function (bundle) {
|
||||
t.equal(bundle,
|
||||
'04a347b2c75b2f418cc37162dea57c1ee408f9109f820234' +
|
||||
'7768a841cf8ad3dc324f1adf6b2f710fa4ea823f4ccb70c4' +
|
||||
'bf46b4eb6b0a99b0017ecafbf95073eb7973ddbb184b601a' +
|
||||
'c4df09704028ebfc754dd50e7d8eebfa52ce3fd868c69852')
|
||||
}
|
||||
)
|
||||
.then(
|
||||
function () {
|
||||
var keyFetchToken = Buffer('808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f', 'hex')
|
||||
var accountResetToken = Buffer('c0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedf', 'hex')
|
||||
return token.bundleAccountReset(keyFetchToken, accountResetToken)
|
||||
}
|
||||
)
|
||||
.then(
|
||||
function (bundle) {
|
||||
t.equal(bundle,
|
||||
'bd643fdd047f7ecd5743d91d980cad6011155fd8559fea1d' +
|
||||
'438f12d2c66270f820be421ad000d69800a4a03980862f7e' +
|
||||
'3fbd4eb5c0f77a94c0c2e7f2be97d21d804fc4bc30923cc0' +
|
||||
'd6c07ffea954848e0076b94f7deee71fa34db5c106d91980')
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
|
@ -12,11 +12,9 @@ var DB = require('../../db')(
|
|||
config,
|
||||
log,
|
||||
Token.error,
|
||||
Token.AuthToken,
|
||||
Token.SessionToken,
|
||||
Token.KeyFetchToken,
|
||||
Token.AccountResetToken,
|
||||
Token.SrpToken,
|
||||
Token.PasswordForgotToken
|
||||
)
|
||||
|
||||
|
@ -82,62 +80,6 @@ DB.connect()
|
|||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'srp token handling',
|
||||
function (t) {
|
||||
return db.emailRecord(ACCOUNT.email)
|
||||
.then(function(emailRecord) {
|
||||
return db.createSrpToken(emailRecord)
|
||||
})
|
||||
.then(function(srpToken) {
|
||||
t.deepEqual(srpToken.uid, ACCOUNT.uid, 'srpToken.uid is the same as the ACCOUNT.uid')
|
||||
t.equal(srpToken.v.toString('hex'), ACCOUNT.srp.verifier)
|
||||
t.equal(srpToken.s, ACCOUNT.srp.salt, 'srpToken.s == ACCOUNT.srp.salt')
|
||||
t.ok(srpToken.b, 'srpToken.b is true')
|
||||
return srpToken
|
||||
})
|
||||
.then(function(srpToken) {
|
||||
return db.srpToken(srpToken.tokenid)
|
||||
})
|
||||
.then(function(srpToken) {
|
||||
t.deepEqual(srpToken.uid, ACCOUNT.uid)
|
||||
t.equal(srpToken.v.toString('hex'), ACCOUNT.srp.verifier)
|
||||
t.equal(srpToken.s, ACCOUNT.srp.salt)
|
||||
t.ok(srpToken.b)
|
||||
return srpToken
|
||||
})
|
||||
.then(function(srpToken) {
|
||||
return db.deleteSrpToken(srpToken.tokenid)
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'auth token handling',
|
||||
function (t) {
|
||||
var tokenid;
|
||||
return db.emailRecord(ACCOUNT.email)
|
||||
.then(function(emailRecord) {
|
||||
return db.createAuthToken({ uid: emailRecord.uid })
|
||||
})
|
||||
.then(function(authToken) {
|
||||
t.deepEqual(authToken.uid, ACCOUNT.uid)
|
||||
tokenid = authToken.tokenid
|
||||
})
|
||||
.then(function() {
|
||||
return db.authToken(tokenid)
|
||||
})
|
||||
.then(function(authToken) {
|
||||
t.deepEqual(authToken.tokenid, tokenid, 'token id matches')
|
||||
t.deepEqual(authToken.uid, ACCOUNT.uid)
|
||||
return authToken
|
||||
})
|
||||
.then(function(authToken) {
|
||||
return db.deleteAuthToken(authToken)
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'session token handling',
|
||||
function (t) {
|
||||
|
@ -276,88 +218,6 @@ DB.connect()
|
|||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'db.authFinish',
|
||||
function (t) {
|
||||
return db.emailRecord(ACCOUNT.email)
|
||||
.then(function(emailRecord) {
|
||||
return db.createSrpToken(emailRecord)
|
||||
})
|
||||
.then(function(srpToken) {
|
||||
return db.authFinish(srpToken)
|
||||
})
|
||||
.then(function(authToken) {
|
||||
t.deepEqual(authToken.uid, ACCOUNT.uid)
|
||||
})
|
||||
}
|
||||
)
|
||||
/*
|
||||
test(
|
||||
'db.createSession',
|
||||
function (t) {
|
||||
var tokens1;
|
||||
return db.emailRecord(ACCOUNT.email)
|
||||
.then(function(emailRecord) {
|
||||
return db.createAuthToken(emailRecord)
|
||||
})
|
||||
.then(function(authToken) {
|
||||
return db.createSession(authToken)
|
||||
})
|
||||
.then(function(tokens) {
|
||||
t.deepEqual(tokens.keyFetchToken.uid, ACCOUNT.uid, 'keyFetchToken uid and account uid should be the same')
|
||||
t.deepEqual(tokens.sessionToken.uid, ACCOUNT.uid, 'sessionToken uid and account uid should be the same')
|
||||
tokens1 = tokens
|
||||
})
|
||||
.then(function() {
|
||||
return db.keyFetchToken(tokens1.keyFetchToken.tokenid)
|
||||
})
|
||||
.then(function(keyFetchToken) {
|
||||
t.deepEqual(keyFetchToken.uid, ACCOUNT.uid, 'keyFetchToken uid and account uid should still be the same')
|
||||
return db.deleteKeyFetchToken(tokens1.keyFetchToken)
|
||||
})
|
||||
.then(function() {
|
||||
return db.sessionToken(tokens1.sessionToken.tokenid)
|
||||
})
|
||||
.then(function(sessionToken) {
|
||||
t.deepEqual(sessionToken.uid, ACCOUNT.uid, 'sessionToken uid and account uid should still be the same')
|
||||
return db.deleteSessionToken(tokens1.sessionToken)
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'db.createPasswordChange',
|
||||
function (t) {
|
||||
var tokens1;
|
||||
return db.emailRecord(ACCOUNT.email)
|
||||
.then(function(emailRecord) {
|
||||
return db.createAuthToken(emailRecord)
|
||||
})
|
||||
.then(function(authToken) {
|
||||
return db.createPasswordChange(authToken)
|
||||
})
|
||||
.then(function(tokens) {
|
||||
t.deepEqual(tokens.keyFetchToken.uid, ACCOUNT.uid, 'keyFetchToken.uid is the same as the original ACCOUNT.uid')
|
||||
t.deepEqual(tokens.accountResetToken.uid, ACCOUNT.uid, 'accountResetToken.uid is the same as the original ACCOUNT.uid')
|
||||
tokens1 = tokens
|
||||
})
|
||||
.then(function() {
|
||||
return db.keyFetchToken(tokens1.keyFetchToken.tokenid)
|
||||
})
|
||||
.then(function(keyFetchToken) {
|
||||
t.deepEqual(keyFetchToken.uid, ACCOUNT.uid)
|
||||
return db.deleteKeyFetchToken(tokens1.keyFetchToken)
|
||||
})
|
||||
.then(function() {
|
||||
return db.accountResetToken(tokens1.accountResetToken.tokenid)
|
||||
})
|
||||
.then(function(accountResetToken) {
|
||||
t.deepEqual(accountResetToken.uid, ACCOUNT.uid)
|
||||
return db.deleteAccountResetToken(tokens1.accountResetToken)
|
||||
})
|
||||
}
|
||||
)
|
||||
*/
|
||||
test(
|
||||
'db.forgotPasswordVerified',
|
||||
function (t) {
|
||||
|
|
|
@ -1,183 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* 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/. */
|
||||
|
||||
var test = require('../ptaptest')
|
||||
var crypto = require('crypto')
|
||||
var Client = require('../../client')
|
||||
var config = require('../../config').root()
|
||||
var TestServer = require('../test_server')
|
||||
|
||||
function uniqueID() {
|
||||
return crypto.randomBytes(10).toString('hex');
|
||||
}
|
||||
|
||||
TestServer.start(config.publicUrl)
|
||||
.then(function main(server) {
|
||||
|
||||
// Randomly-generated account names for testing.
|
||||
// This makes it easy to run the tests against an existing server
|
||||
// which may already have some accounts in its db.
|
||||
|
||||
var email1 = uniqueID() + "@example.com"
|
||||
|
||||
test(
|
||||
'(reduced security) Create account',
|
||||
function (t) {
|
||||
var clientApi = new Client.Api(config.publicUrl)
|
||||
var email = Buffer(email1).toString('hex')
|
||||
var password = 'allyourbasearebelongtous'
|
||||
return clientApi.rawPasswordAccountCreate(email, password, {preVerified: true})
|
||||
.then(
|
||||
function (result) {
|
||||
var client = null
|
||||
t.equal(typeof(result.uid), 'string')
|
||||
return Client.login(config.publicUrl, email1, password)
|
||||
.then(
|
||||
function (x) {
|
||||
client = x
|
||||
return client.keys()
|
||||
}
|
||||
)
|
||||
.then(
|
||||
function (keys) {
|
||||
t.ok(Buffer.isBuffer(keys.kA), 'kA exists')
|
||||
t.ok(Buffer.isBuffer(keys.wrapKb), 'wrapKb exists')
|
||||
t.ok(Buffer.isBuffer(keys.kB), 'kB exists')
|
||||
t.equal(client.kB.length, 32, 'kB exists, has the right length')
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
.then(
|
||||
function () {
|
||||
return server.assertLogs(t, {
|
||||
'account-create-success': 1,
|
||||
'session-create': 1
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'(reduced security) Login with email and password',
|
||||
function (t) {
|
||||
var clientApi = new Client.Api(config.publicUrl)
|
||||
var email = Buffer(email1).toString('hex')
|
||||
var password = 'allyourbasearebelongtous'
|
||||
return clientApi.rawPasswordSessionCreate(email, password)
|
||||
.then(
|
||||
function (result) {
|
||||
t.ok(result.uid, 'uid exists')
|
||||
t.equal(result.verified, true, 'email verified')
|
||||
t.equal(typeof(result.sessionToken), 'string', 'sessionToken exists')
|
||||
}
|
||||
)
|
||||
.then(
|
||||
function () {
|
||||
return server.assertLogs(t, {
|
||||
'account-create-success': 0,
|
||||
'session-create': 1
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'(reduced security) Login with email and wrong password',
|
||||
function (t) {
|
||||
var clientApi = new Client.Api(config.publicUrl)
|
||||
var email = Buffer(email1).toString('hex')
|
||||
var password = 'xxx'
|
||||
return clientApi.rawPasswordSessionCreate(email, password)
|
||||
.then(
|
||||
function (result) {
|
||||
t.fail('login succeeded')
|
||||
},
|
||||
function (err) {
|
||||
t.equal(err.errno, 103)
|
||||
}
|
||||
)
|
||||
.then(
|
||||
function () {
|
||||
return server.assertLogs(t, {
|
||||
'login-failure': 1,
|
||||
'login-success': 0,
|
||||
'session-create': 0
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'(reduced security) Login with unknown email',
|
||||
function (t) {
|
||||
var clientApi = new Client.Api(config.publicUrl)
|
||||
var email = Buffer('x@y.me').toString('hex')
|
||||
var password = 'allyourbasearebelongtous'
|
||||
return clientApi.rawPasswordSessionCreate(email, password)
|
||||
.then(
|
||||
function (result) {
|
||||
t.fail('login succeeded')
|
||||
},
|
||||
function (err) {
|
||||
t.equal(err.errno, 102)
|
||||
}
|
||||
)
|
||||
.then(
|
||||
function () {
|
||||
return server.assertLogs(t, {
|
||||
// the error here is "account does not exist"
|
||||
// which doesn't trigger security logging
|
||||
'login-failure': 0,
|
||||
'login-success': 0,
|
||||
'session-create': 0
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'(reduced security) Change password',
|
||||
function (t) {
|
||||
var clientApi = new Client.Api(config.publicUrl)
|
||||
var email = Buffer(email1).toString('hex')
|
||||
var password = 'allyourbasearebelongtous'
|
||||
var newPassword = 'wow'
|
||||
return clientApi.rawPasswordPasswordChange(email, password, newPassword)
|
||||
.then(
|
||||
function (result) {
|
||||
t.equal(JSON.stringify(result), '{}', 'password changed')
|
||||
return clientApi.rawPasswordSessionCreate(email, newPassword)
|
||||
}
|
||||
)
|
||||
.then(
|
||||
function (result) {
|
||||
t.ok(result.uid, 'uid exists')
|
||||
t.equal(result.verified, true, 'email verified')
|
||||
t.equal(typeof(result.sessionToken), 'string', 'sessionToken exists')
|
||||
}
|
||||
)
|
||||
.then(
|
||||
function () {
|
||||
return server.assertLogs(t, {
|
||||
'pwd-reset-success': 1,
|
||||
'pwd-reset-failure': 0
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'teardown',
|
||||
function (t) {
|
||||
server.stop()
|
||||
t.end()
|
||||
}
|
||||
)
|
||||
})
|
|
@ -1,128 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* 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/. */
|
||||
|
||||
var test = require('../ptaptest')
|
||||
var crypto = require('crypto')
|
||||
var srp = require('srp')
|
||||
var log = { trace: function() {} }
|
||||
|
||||
var Token = require('../../tokens')(log)
|
||||
var DB = require('../../db/heap')(
|
||||
log,
|
||||
Token.error,
|
||||
Token.AuthToken,
|
||||
Token.SessionToken,
|
||||
Token.KeyFetchToken,
|
||||
Token.AccountResetToken,
|
||||
Token.SrpToken,
|
||||
Token.PasswordForgotToken
|
||||
)
|
||||
var db = new DB()
|
||||
|
||||
var SrpToken = Token.SrpToken
|
||||
|
||||
var alice = {
|
||||
uid: 'xxx',
|
||||
email: Buffer('somebödy@example.com').toString('hex'),
|
||||
password: 'awesomeSauce',
|
||||
srp: {
|
||||
verifier: null,
|
||||
salt: 'BAD1'
|
||||
},
|
||||
kA: 'BAD3',
|
||||
wrapKb: 'BAD4'
|
||||
}
|
||||
|
||||
alice.srp.verifier = srp.computeVerifier(
|
||||
srp.params[2048],
|
||||
Buffer(alice.srp.salt, 'hex'),
|
||||
Buffer(alice.email),
|
||||
Buffer(alice.password)
|
||||
).toString('hex')
|
||||
|
||||
|
||||
db.createAccount(alice)
|
||||
.then(
|
||||
function (a) {
|
||||
|
||||
test(
|
||||
'create login session works',
|
||||
function (t) {
|
||||
return SrpToken.create(a)
|
||||
.then(
|
||||
function (s) {
|
||||
t.equal(s.uid, a.uid)
|
||||
t.equal(s.s, a.srp.salt)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
test(
|
||||
'finish login session works',
|
||||
function (t) {
|
||||
var session = null
|
||||
var K = null
|
||||
return SrpToken.create(a)
|
||||
.then(
|
||||
function (s) {
|
||||
session = s
|
||||
var a = crypto.randomBytes(32)
|
||||
var clientData = s.clientData()
|
||||
var srpClient = new srp.Client(
|
||||
srp.params[2048],
|
||||
Buffer(clientData.srp.salt, 'hex'),
|
||||
Buffer(alice.email),
|
||||
Buffer(alice.password),
|
||||
a
|
||||
)
|
||||
srpClient.setB(Buffer(clientData.srp.B, 'hex'))
|
||||
return {
|
||||
A: srpClient.computeA().toString('hex'),
|
||||
M: srpClient.computeM1().toString('hex'),
|
||||
K: srpClient.computeK().toString('hex'),
|
||||
}
|
||||
}
|
||||
)
|
||||
.then(
|
||||
function (x) {
|
||||
K = x.K
|
||||
return session.finish(x.A, x.M)
|
||||
}
|
||||
)
|
||||
.then(
|
||||
function (s) {
|
||||
t.equal(s.K.toString('hex'), K)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
.done(
|
||||
function () {
|
||||
|
||||
test(
|
||||
'authToken encryption is test-vector compliant',
|
||||
function (t) {
|
||||
var srpK = 'e68fd0112bfa31dcffc8e9c96a1cbadb4c3145978ff35c73e5bf8d30bbc7499a'
|
||||
var authToken = Buffer('606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f', 'hex')
|
||||
var bundle = '253957f10e861c7c0a12bb0193d384d9579db544666d50bd3252d6576c768a68' +
|
||||
'a98c87f5769ab4ccca3df863faeb217eb16ddc29d712b30112b446324ee806d6'
|
||||
return SrpToken.create(alice)
|
||||
.then(
|
||||
function (token) {
|
||||
token.K = Buffer(srpK, 'hex')
|
||||
return token.bundleAuth(authToken)
|
||||
}
|
||||
)
|
||||
.then(
|
||||
function (b) {
|
||||
t.equal(b, bundle)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
|
@ -1,62 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* 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/. */
|
||||
|
||||
module.exports = function (log, inherits, Token, error) {
|
||||
|
||||
function AuthToken(keys, details) {
|
||||
Token.call(this, keys, details)
|
||||
this.verified = !!details.verified
|
||||
}
|
||||
inherits(AuthToken, Token)
|
||||
|
||||
AuthToken.tokenTypeID = 'authToken'
|
||||
|
||||
AuthToken.create = function (details) {
|
||||
log.trace({ op: 'AuthToken.create', uid: details && details.uid })
|
||||
return Token.createNewToken(AuthToken, details || {})
|
||||
}
|
||||
|
||||
AuthToken.fromHex = function (string, details) {
|
||||
log.trace({ op: 'AuthToken.fromHex' })
|
||||
return Token.createTokenFromHexData(AuthToken, string, details || {})
|
||||
}
|
||||
|
||||
AuthToken.prototype.bundleSession = function (keyFetchToken, sessionToken) {
|
||||
log.trace({ op: 'authToken.bundleSession', id: this.id })
|
||||
return this.bundle('session/create', Buffer.concat([keyFetchToken, sessionToken]))
|
||||
}
|
||||
|
||||
AuthToken.prototype.unbundleSession = function (bundle) {
|
||||
log.trace({ op: 'authToken.unbundleSession', id: this.id })
|
||||
return this.unbundle('session/create', bundle)
|
||||
.then(
|
||||
function (plaintext) {
|
||||
return {
|
||||
keyFetchToken: plaintext.slice(0, 32),
|
||||
sessionToken: plaintext.slice(32, 64)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
AuthToken.prototype.bundleAccountReset = function (keyFetchToken, resetToken) {
|
||||
log.trace({ op: 'authToken.bundleAccountReset', id: this.id })
|
||||
return this.bundle('password/change', Buffer.concat([keyFetchToken, resetToken]))
|
||||
}
|
||||
|
||||
AuthToken.prototype.unbundleAccountReset = function (bundle) {
|
||||
log.trace({ op: 'authToken.unbundleAccountReset', id: this.id })
|
||||
return this.unbundle('password/change', bundle)
|
||||
.then(
|
||||
function (plaintext) {
|
||||
return {
|
||||
keyFetchToken: plaintext.slice(0, 32),
|
||||
accountResetToken: plaintext.slice(32, 64)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
return AuthToken
|
||||
}
|
|
@ -6,8 +6,6 @@ var crypto = require('crypto')
|
|||
var inherits = require('util').inherits
|
||||
|
||||
var P = require('p-promise')
|
||||
var srp = require('srp')
|
||||
var uuid = require('uuid')
|
||||
var hkdf = require('../hkdf')
|
||||
|
||||
var error = require('./error')
|
||||
|
@ -25,7 +23,6 @@ module.exports = function (log) {
|
|||
crypto
|
||||
)
|
||||
var SessionToken = require('./session_token')(log, inherits, Token)
|
||||
var AuthToken = require('./auth_token')(log, inherits, Token, error)
|
||||
var PasswordForgotToken = require('./password_forgot_token')(
|
||||
log,
|
||||
inherits,
|
||||
|
@ -33,16 +30,6 @@ module.exports = function (log) {
|
|||
Token,
|
||||
crypto
|
||||
)
|
||||
var SrpToken = require('./srp_token')(
|
||||
log,
|
||||
inherits,
|
||||
P,
|
||||
uuid,
|
||||
srp,
|
||||
Bundle,
|
||||
Token,
|
||||
error
|
||||
)
|
||||
|
||||
var PasswordChangeToken = require('./password_change_token')(
|
||||
log,
|
||||
|
@ -55,9 +42,7 @@ module.exports = function (log) {
|
|||
Token.AccountResetToken = AccountResetToken
|
||||
Token.KeyFetchToken = KeyFetchToken
|
||||
Token.SessionToken = SessionToken
|
||||
Token.AuthToken = AuthToken
|
||||
Token.PasswordForgotToken = PasswordForgotToken
|
||||
Token.SrpToken = SrpToken
|
||||
Token.PasswordChangeToken = PasswordChangeToken
|
||||
|
||||
return Token
|
||||
|
|
|
@ -1,85 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* 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/. */
|
||||
|
||||
module.exports = function (log, inherits, P, uuid, srp, Bundle, Token, error) {
|
||||
|
||||
function SrpToken(keys, details) {
|
||||
if (!details.srp) { details.srp = {} }
|
||||
Token.call(this, keys, details)
|
||||
this.b = this.bundleKey
|
||||
this.v = details.srp.verifier ? Buffer(details.srp.verifier, 'hex') : null
|
||||
this.s = details.srp.salt ? details.srp.salt : null
|
||||
this.passwordStretching = details.passwordStretching || null
|
||||
this.srpServer = new srp.Server(srp.params[2048], this.v, this.b)
|
||||
this.K = null
|
||||
}
|
||||
inherits(SrpToken, Token)
|
||||
|
||||
SrpToken.tokenTypeID = 'srpToken'
|
||||
|
||||
SrpToken.create = function (details) {
|
||||
log.trace({ op: 'SrpToken.create', uid: details && details.uid })
|
||||
return Token.createNewToken(SrpToken, details || {})
|
||||
}
|
||||
|
||||
SrpToken.fromHex = function (string, details) {
|
||||
log.trace({ op: 'SrpToken.create', uid: details && details.uid })
|
||||
return Token.createTokenFromHexData(SrpToken, string, details || {})
|
||||
}
|
||||
|
||||
// Get the data to be sent back to the client in the first message.
|
||||
//
|
||||
SrpToken.prototype.clientData = function () {
|
||||
return {
|
||||
srpToken: this.id.toString('hex'),
|
||||
passwordStretching: this.passwordStretching,
|
||||
srp: {
|
||||
type: 'SRP-6a/SHA256/2048/v1',
|
||||
salt: this.s,
|
||||
B: this.srpServer.computeB().toString('hex')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Complete the SRP dance, verifying the correct credentials and
|
||||
// deriving the value of the shared secret.
|
||||
//
|
||||
SrpToken.prototype.finish = function (A, M1) {
|
||||
A = Buffer(A, 'hex')
|
||||
this.srpServer.setA(A)
|
||||
try {
|
||||
this.srpServer.checkM1(Buffer(M1, 'hex'))
|
||||
}
|
||||
catch (e) {
|
||||
throw error.incorrectPassword()
|
||||
}
|
||||
this.K = this.srpServer.computeK()
|
||||
return this
|
||||
}
|
||||
|
||||
SrpToken.prototype.bundleAuth = function (authToken) {
|
||||
log.trace({ op: 'srpToken.bundleAuth', id: this.id, auth: authToken })
|
||||
if (!this.K) {
|
||||
return P.reject('Shared secret missing; SRP handshake was not completed')
|
||||
}
|
||||
return Bundle.bundle(this.K, 'auth/finish', authToken)
|
||||
}
|
||||
|
||||
SrpToken.prototype.unbundleAuth = function (bundle) {
|
||||
log.trace({ op: 'srpToken.unbundleAuth', id: this.id })
|
||||
if (!this.K) {
|
||||
return P.reject('Shared secret missing; SRP handshake was not completed')
|
||||
}
|
||||
return Bundle.unbundle(this.K, 'auth/finish', bundle)
|
||||
.then(
|
||||
function (plaintext) {
|
||||
return {
|
||||
authToken: plaintext,
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
return SrpToken
|
||||
}
|
Загрузка…
Ссылка в новой задаче