This commit is contained in:
Danny Coates 2013-10-25 17:49:49 -07:00
Родитель 4ba5938818
Коммит 4d89d86f8c
15 изменённых файлов: 1044 добавлений и 338 удалений

Просмотреть файл

@ -29,14 +29,15 @@ function main() {
memoryMonitor.start()
// databases
var dbs = require('../kv')(config, log)
//var dbs = require('../kv')(config, log)
var db = require('../db/heap')(log)
// TODO: send to the SMTP server directly. In the future this may change
// to another process that we send an http request to.
var mailer = require('../mailer')(config.smtp, log)
// stored objects
var models = require('../models')(log, config, dbs, mailer)
//var models = require('../models')(log, config, dbs, mailer)
// server public key
var serverPublicKey = JSON.parse(fs.readFileSync(config.publicKeyFile))
@ -45,9 +46,9 @@ function main() {
var CC = require('compute-cluster')
var signer = new CC({ module: __dirname + '/signer.js' })
var routes = require('../routes')(log, serverPublicKey, signer, models, config)
var routes = require('../routes')(log, serverPublicKey, signer, db, config)
var Server = require('../server')
var server = Server.create(log, config, routes, models.tokens)
var server = Server.create(log, config, routes, db)
server.start(
function () {

313
db/heap.js Normal file
Просмотреть файл

@ -0,0 +1,313 @@
var inherits = require('util').inherits
var P = require('p-promise')
var Bundle = require('../bundle')
module.exports = function (log) {
var Token = require('../models/token')(log, inherits, Bundle)
var SessionToken = require('../models/session_token')(log, inherits, Token)
var KeyFetchToken = require('../models/key_fetch_token')(log, inherits, Token)
var AccountResetToken = require('../models/account_reset_token')(log, inherits, Token)
var SrpToken = require('../models.srp_session')(log) // TODO
var ForgotPasswordToken = require('../models/forgot_password_token')(log, inherits, Token)
function Heap() {
this.sessionTokens = {}
this.keyFetchTokens = {}
this.accountResetTokens = {}
this.authTokens = {}
this.srpTokens = {}
this.forgotPasswordTokens = {}
this.accounts = {}
this.emailRecords = {}
}
function saveTo(collection) {
return function (object) {
collection[object.id] = object
return object
}
}
// The lazy way
function deleteUid(uid, collection) {
var keys = Object.keys(collection)
for (var i = 0; i < keys.length; i++) {
if (collection[keys[i]].uid === uid) {
delete collection[keys[i]]
}
}
}
Heap.prototype.ping = function () {
return P(true)
}
// CREATE
Heap.prototype.createAccount = function (data) {
this.accounts[data.uid] = data
this.emailRecords[data.email] = data.uid
return P(data)
}
Heap.prototype.createSessionToken = function (authToken) {
return SessionToken.create(authToken)
.then(saveTo(this.sessionTokens))
.then(
function (sessionToken) {
var account = this.account[sessionToken.uid]
account.devices[sessionToken.id] = sessionToken
return sessionToken
}.bind(this)
)
}
Heap.prototype.createKeyFetchToken = function (authToken) {
return KeyFetchToken.create(authToken)
.then(saveTo(this.keyFetchTokens))
}
Heap.prototype.createAccountResetToken = function (token /* authToken|forgotPasswordToken */) {
return AccountResetToken.create(token)
.then(
function (accountResetToken) {
var account = this.accounts[accountResetToken.uid]
if (!account) { return P.reject('Account not found') }
account.accountResetToken = accountResetToken.id
return accountResetToken
}.bind(this)
)
.then(saveTo(this.accountResetTokens))
}
Heap.prototype.createAuthToken = function (srpToken) {
return AuthToken.create(srpToken)
.then(saveTo(this.authTokens))
}
Heap.prototype.createSrpToken = function (emailRecord) {
return SrpToken.create(emailRecord)
.then(saveTo(this.srpTokens))
}
Heap.prototype.createForgotPasswordToken = function (emailRecord) {
return ForgotPasswordToken.create(emailRecord)
.then(
function (forgotPasswordToken) {
var account = this.accounts[forgotPasswordToken.uid]
if (!account) { return P.reject('Account not found') }
account.forgotPasswordToken = forgotPasswordToken.id
return forgotPasswordToken
}.bind(this)
)
.then(saveTo(this.forgotPasswordTokens))
}
// READ
Heap.prototype.accountExists = function (email) {
return P(!!this.emailRecords[email])
}
Heap.prototype.accountDevices = function (uid) {
return this.account(uid)
.then(
function (account) {
return account.devices
}
)
}
Heap.prototype.sessionToken = function (id) {
var sessionToken = this.sessionTokens[id]
if (!sessionToken) { return P.reject('SessionToken not found') }
var account = this.accounts[sessionToken.uid]
if (!account) { return P.reject('Account not found') }
sessionToken.email = account.email
sessionToken.emailCode = account.emailCode
sessionToken.verified = account.verified
return P(sessionToken)
}
Heap.prototype.keyFetchToken = function (id) {
var keyFetchToken = this.keyFetchTokens[id]
if (!keyFetchToken) { return P.reject('KeyFetchToken not found') }
var account = this.accounts[sessionToken.uid]
if (!account) { return P.reject('Account not found') }
keyFetchToken.kA = account.kA
keyFetchToken.wrapKb = account.wrapKb
keyFetchToken.verified = account.verified
return P(keyFetchToken)
}
Heap.prototype.accountResetToken = function (id) {
var accountResetToken = this.accountResetTokens[id]
if (!accountResetToken) { return P.reject('AccountResetToken not found') }
return P(accountResetToken)
}
Heap.prototype.authToken = function (id) {
var authToken = this.authTokens[id]
if (!authToken) { return P.reject('AuthToken not found') }
return P(authToken)
}
Heap.prototype.srpToken = function (id) {
var srpToken = this.srpTokens[id]
if (!srpToken) { return P.reject('SrpToken not found') }
return P(srpToken)
}
Heap.prototype.forgotPasswordToken = function (id) {
var forgotPasswordToken = this.forgotPasswordTokens[id]
if (!forgotPasswordToken) { return P.reject('ForgotPasswordToken not found') }
var account = this.accounts[sessionToken.uid]
if (!account) { return P.reject('Account not found') }
forgotPasswordToken.email = account.email
return P(forgotPasswordToken)
}
Heap.prototype.emailRecord = function (email) {
var uid = this.emailRecords[email]
return this.account(uid)
}
Heap.prototype.account = function (uid) {
var account = this.accounts[sessionToken.uid]
if (!account) { return P.reject('Account not found') }
return P(account)
}
// UPDATE
Heap.prototype.udateForgotPasswordToken = function (forgotPasswordToken) {
return P(true)
}
// DELETE
Heap.prototype.deleteAccount = function (authToken) {
var account = this.accounts[authToken.uid]
if (!account) { return P.reject('Account not found') }
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.forgotPasswordTokens)
delete this.emailRecords[account.email]
delete this.accounts[account.uid]
return P(true)
}
Heap.prototype.deleteSessionToken = function (sessionToken) {
var account = this.accounts[sessionToken.uid]
if (!account) { return P.reject('Account not found') }
delete account.devices[sessionToken.id]
delete this.sessionTokens[sessionToken.id]
return P(true)
}
Heap.prototype.deleteKeyFetchToken = function (keyFetchToken) {
delete this.keyFetchTokens[keyFetchToken.id]
return P(true)
}
Heap.prototype.deleteAccountResetToken = function (accountResetToken) {
delete this.accountResetTokens[accountResetToken.id]
return P(true)
}
Heap.prototype.deleteAuthToken = function (authToken) {
delete this.authTokens[authToken.id]
return P(true)
}
Heap.prototype.deleteSrpToken = function (srpToken) {
delete this.srpTokens[srpToken.id]
return P(true)
}
Heap.prototype.deleteForgotPasswordToken = function (forgotPasswordToken) {
delete this.forgotPasswordTokens[forgotPasswordToken.id]
return P(true)
}
// BATCH
Heap.prototype.resetAccount = function (accountResetToken, data) {
var account = this.accounts[accountResetToken.uid]
if (!account) { return P.reject('Account not found') }
account.srp = data.srp
account.wrapKb = data.wrapKb
account.passwordStretching = data.passwordStretching
account.devices = {}
account.accountResetToken = null
account.forgotPasswordToken = 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.forgotPasswordTokens)
return P(true)
}
Heap.prototype.authFinish = function (srpToken) {
return this.deleteSrpToken(srpToken)
.then(this.createAuthToken.bind(this, srpToken))
}
Heap.prototype.createSession = function (authToken) {
return this.deleteAuthToken(authToken)
.then(
function () {
return P.all([
this.createKeyFetchToken(authToken),
this.createSessionToken(authToken)
])
}
)
.then(
function (tokens) {
return {
keyFetchToken: tokens[0],
sessionToken: tokens[1]
}
}
)
}
Heap.prototype.verifyEmail = function (account) {
account.verified = true
return P(true)
}
Heap.prototype.createPasswordChange = function (authToken) {
return this.deleteAuthToken(authToken)
.then(
function () {
return P.all([
this.createKeyFetchToken(authToken),
this.createAccountResetToken(authToken)
])
}
)
.then(
function (tokens) {
return {
keyFetchToken: tokens[0],
accountResetToken: tokens[1]
}
}
)
}
Heap.prototype.forgotPasswordVerified = function (forgotPasswordToken) {
return this.deleteForgotPasswordToken(forgotPasswordToken)
.then(this.createAccountResetToken.bind(this, forgotPasswordToken))
}
return Heap
}

430
docs/endpoint_data.md Normal file
Просмотреть файл

@ -0,0 +1,430 @@
## /account/create
* input
* email
* srp
* passwordStretching
* reads
* email
* writes
* uid
* email
* emailCode
* verified
* kA
* wrapKb
* srp
* passwordStretching
* sessionTokenList
* keyFetchTokenList
* forgotPasswordTokenList
* srpTokenList
* authTokenList
* accountResetTokenList
* output
* uid
* db-read
* Emails
* db-write
* Emails
* Accounts
## /account/devices
* input
* sessionToken.id
* reads
* uid
* sessionTokenList
* writes
* (none)
* output
* sessionTokenList (x)
* db-read
* SessionTokens
* Accounts
* db-write
* (none)
## /account/keys
* input
* keyFetchToken.id
* reads
* uid
* kA
* wrapKb
* verified
* writes
* delete
* keyFetchToken
* keyFetchTokenList
* output
* kA
* wrapKb
* db-read
* KeyFetchTokens
* db-write
* KeyFetchTokens
* Accounts
## /account/reset
* input
* accountResetToken.id
* srp
* passwordStretching
* wrapKb
* reads
* uid
* sessionTokenList
* keyFetchTokenList
* forgotPasswordTokenList
* srpTokenList
* authTokenList
* accountResetTokenList
* writes
* delete
* all tokens
* srp
* passwordStretching
* wrapKb
* sessionTokenList
* keyFetchTokenList
* forgotPasswordTokenList
* srpTokenList
* authTokenList
* accountResetTokenList
* output
* (none)
* db-read
* AccountResetTokens
* Accounts
* db-write
* SessionTokens
* KeyFetchTokens
* AccountResetTokens
* AuthTokens
* SrpTokens
* ForgotPasswordTokens
* Emails
* Accounts
## /account/destroy
* input
* authToken.id
* reads
* uid
* sessionTokenList
* keyFetchTokenList
* forgotPasswordTokenList
* srpTokenList
* authTokenList
* accountResetTokenList
* writes
* delete
* all
* output
* (none)
* db-read
* AuthTokens
* Accounts
* db-write
* SessionTokens
* KeyFetchTokens
* AccountResetTokens
* AuthTokens
* SrpTokens
* ForgotPasswordTokens
* Emails
* Accounts
## /auth/start
* input
* email
* reads
* uid
* srp
* passwordStretching
* srpToken data
* email
* emailCode
* kA
* wrapKb
* verified
* writes
* srpToken
* srpTokenList
* output
* srpToken.id
* passwordStretching
* srp.B
* srp.salt
* db-read
* Emails
* db-write
* SrpTokens
* Accounts
## /auth/finish
* input
* srpToken.id
* A
* M
* reads
* uid
* srpToken
* N, g, s, v, b, B
* passwordStretching
* authToken data
* email
* emailCode
* kA
* wrapKb
* verified
* writes
* delete
* srpToken
* authToken
* srpTokenList
* authTokenList
* output
* authToken.id
* db-read
* SrpTokens
* db-write
* SrpTokens
* AuthTokens
* Accounts
## /session/create
* input
* authToken.id
* reads
* uid
* verified
* sessionToken data
* email
* emailCode
* keyFetchToken data
* kA
* wrapKb
* writes
* delete
* authToken
* sessionToken
* keyFetchToken
* sessionTokenList
* keyFetchTokenList
* output
* keyFetchToken.id
* sessionToken.id
* db-read
* AuthTokens
* db-write
* AuthTokens
* SessionTokens
* KeyFetchTokens
* Accounts
## /session/destroy
* input
* sessionToken.id
* reads
* uid
* writes
* delete
* sessionToken
* sessionTokenList
* output
* (none)
* db-read
* SessionTokens
* db-write
* SessionTokens
* Accounts
## /recovery_email/status
* input
* sessionToken.id
* reads
* email
* verified
* writes
* (none)
* output
* email
* verified
* db-read
* SessionTokens
* db-write
* (none)
## /recovery_email/resend_code
* input
* sessionToken.id
* reads
* uid
* email
* emailCode
* writes
* (none)
* output
* (none)
* db-read
* SessionTokens
* db-write
* (none)
## /recovery_email/verify_code
* input
* uid
* code
* reads
* emailCode
* srpTokenList
* authTokenList
* keyFetchTokenList
* sessionTokenList
* writes
* verified
* output
* (none)
* db-read
* Accounts
* db-write
* Emails
* SrpTokens
* AuthTokens
* KeyFetchTokens
* SessionTokens
## /certificate/sign
* input
* sessionToken.id
* publicKey
* duration
* reads
* uid
* verified
* writes
* (none)
* output
* signedKey
* db-read
* SessionTokens
* db-write
* (none)
## /password/change/start
* input
* authToken.id
* reads
* uid
* keyFetchToken data
* kA
* wrapKb
* verified
* accountResetToken data
* (none)
* writes
* delete
* authToken
* keyFetchToken
* accountResetToken
* keyFetchTokenList
* accountResetTokenList
* output
* keyFetchToken
* accountResetToken
* db-read
* AuthTokens
* db-write
* AuthTokens
* KeyFetchTokens
* AccountResetTokens
* Accounts
## /password/forgot/send_code
* input
* email
* reads
* uid
* forgotPasswordToken data
* (none)
* writes
* forgotPasswordToken
* forgotPasswordTokenList
* output
* forgotPasswordToken.id
* ttl
* codeLength
* tries
* db-read
* Emails
* db-write
* ForgotPasswordTokens
* Accounts
## /password/forgot/resend_code
* input
* forgotPasswordToken.id
* reads
* uid
* email
* passcode
* ttl
* codeLength
* tries
* writes
* (none)
* output
* forgotPasswordToken.id
* ttl
* codeLength
* tries
* db-read
* ForgotPasswordTokens
* db-write
* (none)
## /password/forgot/verify_code
* input
* forgotPasswordToken.id
* code
* reads
* uid
* passcode
* ttl
* accountResetToken data
* (none)
* writes
* delete
* forgotPasswordToken
* tries
* accountResetToken
* forgotPasswordTokenList
* accountResetTokenList
* output
* accountResetToken.id
* ttl
* tries
* db-read
* ForgotPasswordTokens
* db-write
* ForgotPasswordTokens
* AccountResetTokens
* Accounts

86
docs/schema.md Normal file
Просмотреть файл

@ -0,0 +1,86 @@
## SessionTokens
* id
* uid
* email
* emailCode
* verified
## KeyFetchTokens
* id
* uid
* kA
* wrapKb
* verified
## AccountResetTokens
* id
* uid
## AuthTokens
* id
* uid
* (for sessionToken)
* email
* emailCode
* (for keyFetchToken)
* kA
* wrapKb
* (for both)
* verified
## SrpTokens
* id
* uid
* N
* g
* s
* v
* b
* B
* passwordStretching
* (for authToken)
* email
* emailCode
* kA
* wrapKb
* verified
## ForgotPasswordTokens
* id
* uid
* email
* passcode
* ttl
* codeLength
* tries
## Emails
* email
* uid
* srp
* passwordStretching
* (for srpToken)
* emailCode
* kA
* wrapKb
* verified
## Accounts
* uid
* email
* emailCode
* sessionTokens
* keyFetchTokens
* srpTokens
* authTokens
* accountResetToken
* forgotPasswordToken

Просмотреть файл

@ -11,10 +11,6 @@ module.exports = function (log, inherits, Token, crypto, db) {
}
inherits(AccountResetToken, Token)
AccountResetToken.hydrate = function (object) {
return Token.fill(new AccountResetToken(), object)
}
AccountResetToken.create = function (uid) {
log.trace({ op: 'AccountResetToken.create', uid: uid })
return Token
@ -28,7 +24,7 @@ module.exports = function (log, inherits, Token, crypto, db) {
t.id = key.slice(0, 32).toString('hex')
t._key = key.slice(32, 64).toString('hex')
t.xorKey = key.slice(64, 352).toString('hex')
return t.save()
return t
}
)
}
@ -54,39 +50,6 @@ module.exports = function (log, inherits, Token, crypto, db) {
)
}
AccountResetToken.getCredentials = function (id, cb) {
log.trace({ op: 'AccountResetToken.getCredentials', id: id })
AccountResetToken.get(id)
.done(
function (token) {
cb(null, token)
},
cb
)
}
AccountResetToken.get = function (id) {
log.trace({ op: 'AccountResetToken.get', id: id })
return db
.get(id + '/reset')
.then(AccountResetToken.hydrate)
}
AccountResetToken.del = function (id) {
log.trace({ op: 'AccountResetToken.del', id: id })
return db.delete(id + '/reset')
}
AccountResetToken.prototype.save = function () {
log.trace({ op: 'accountResetToken.save', id: this.id })
var self = this
return db.set(this.id + '/reset', this).then(function () { return self })
}
AccountResetToken.prototype.del = function () {
return AccountResetToken.del(this.id)
}
AccountResetToken.prototype.bundle = function (wrapKb, verifier) {
log.trace({ op: 'accountResetToken.bundle', id: this.id })
return this.xor(

Просмотреть файл

@ -9,11 +9,8 @@ module.exports = function (log, inherits, Token, db, error) {
}
inherits(KeyFetchToken, Token)
KeyFetchToken.hydrate = function (object) {
return Token.fill(new KeyFetchToken(), object)
}
KeyFetchToken.create = function (uid) {
KeyFetchToken.create = function (authToken) {
var uid = authToken && authToken.uid
log.trace({ op: 'KeyFetchToken.create', uid: uid })
return Token
.randomTokenData('account/keys', 3 * 32 + 2 * 32)
@ -27,22 +24,11 @@ module.exports = function (log, inherits, Token, db, error) {
t._key = key.slice(32, 64).toString('hex')
t.hmacKey = key.slice(64, 96).toString('hex')
t.xorKey = key.slice(96, 160).toString('hex')
return t.save()
return t
}
)
}
KeyFetchToken.getCredentials = function (id, cb) {
log.trace({ op: 'KeyFetchToken.getCredentials', id: id })
KeyFetchToken.get(id)
.done(
function (token) {
cb(null, token)
},
cb
)
}
KeyFetchToken.fromHex = function (string) {
log.trace({ op: 'KeyFetchToken.fromHex' })
return Token.
@ -65,28 +51,6 @@ module.exports = function (log, inherits, Token, db, error) {
)
}
KeyFetchToken.get = function (id) {
log.trace({ op: 'KeyFetchToken.get', id: id })
return db
.get(id + '/fetch')
.then(KeyFetchToken.hydrate)
}
KeyFetchToken.del = function (id) {
log.trace({ op: 'KeyFetchToken.del', id: id })
return db.delete(id + '/fetch')
}
KeyFetchToken.prototype.save = function () {
log.trace({ op: 'keyFetchToken.save', id: this.id })
var self = this
return db.set(this.id + '/fetch', this).then(function () { return self })
}
KeyFetchToken.prototype.del = function () {
return KeyFetchToken.del(this.id)
}
KeyFetchToken.prototype.bundle = function (kA, wrapKb) {
log.trace({ op: 'keyFetchToken.bundle', id: this.id })
return this.bundleHexStrings([kA, wrapKb])

Просмотреть файл

@ -2,18 +2,15 @@
* 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, db) {
module.exports = function (log, inherits, Token) {
function SessionToken() {
Token.call(this)
}
inherits(SessionToken, Token)
SessionToken.hydrate = function (object) {
return Token.fill(new SessionToken(), object)
}
SessionToken.create = function (uid) {
SessionToken.create = function (authToken) {
var uid = authToken && authToken.uid
log.trace({ op: 'SessionToken.create', uid: uid })
return Token
.randomTokenData('session', 2 * 32)
@ -25,7 +22,7 @@ module.exports = function (log, inherits, Token, db) {
t.data = data[0].toString('hex')
t.id = key.slice(0, 32).toString('hex')
t._key = key.slice(32, 64).toString('hex')
return t.save()
return t
}
)
}
@ -50,38 +47,5 @@ module.exports = function (log, inherits, Token, db) {
)
}
SessionToken.getCredentials = function (id, cb) {
log.trace({ op: 'SessionToken.getCredentials', id: id })
SessionToken.get(id)
.done(
function (token) {
cb(null, token)
},
cb
)
}
SessionToken.get = function (id) {
log.trace({ op: 'SessionToken.get', id: id })
return db
.get(id + '/session')
.then(SessionToken.hydrate)
}
SessionToken.del = function (id) {
log.trace({ op: 'SessionToken.del', id: id })
return db.delete(id + '/session')
}
SessionToken.prototype.save = function () {
log.trace({ op: 'sessionToken.save', id: this.id })
var self = this
return db.set(this.id + '/session', this).then(function () { return self })
}
SessionToken.prototype.del = function () {
return SessionToken.del(this.id)
}
return SessionToken
}

Просмотреть файл

@ -6,6 +6,18 @@ module.exports = function (log, crypto, uuid, isA, error, Account, RecoveryEmail
const HEX_STRING = /^(?:[a-fA-F0-9]{2})+$/
function sendVerifyCode(email, emailCode) {
//TODO
}
function bundleKeys(keyFetchToken, kA, wrapKb) {
}
function unbundleReset(bundle) {
}
var routes = [
{
method: 'POST',
@ -36,15 +48,35 @@ module.exports = function (log, crypto, uuid, isA, error, Account, RecoveryEmail
handler: function accountCreate(request) {
log.begin('Account.create', request)
var form = request.payload
Account
.create(
{
uid: uuid.v4(),
email: form.email,
srp: form.srp,
kA: crypto.randomBytes(32).toString('hex'),
wrapKb: crypto.randomBytes(32).toString('hex'),
passwordStretching: form.passwordStretching
db.accountExists(form.email)
.then(
function (exists) {
if (exists) {
throw error.accountExists(form.email)
}
}
)
.then(
db.createAccount.bind(
db,
{
uid: uuid.v4(),
email: form.email,
emailCode: crypto.randomBytes(4).toString('hex'),
verified: false,
srp: form.srp,
kA: crypto.randomBytes(32).toString('hex'),
wrapKb: crypto.randomBytes(32).toString('hex'),
passwordStretching: form.passwordStretching,
devices: {},
accountResetToken: null,
forgotPasswordToken: null
}
)
)
.then(
function (account) {
return sendVerifyCode(account.email, account.emailCode)
}
)
.done(
@ -73,13 +105,12 @@ module.exports = function (log, crypto, uuid, isA, error, Account, RecoveryEmail
handler: function (request) {
log.begin('Account.devices', request)
var sessionToken = request.auth.credentials
Account
.get(sessionToken.uid)
db.accountDevices(sessionToken.uid)
.done(
function (account) {
function (devices) {
request.reply(
{
devices: Object.keys(account.sessionTokenIds)
devices: Object.keys(devices)
}
)
},
@ -118,20 +149,18 @@ module.exports = function (log, crypto, uuid, isA, error, Account, RecoveryEmail
log.begin('Account.keys', request)
var reply = request.reply.bind(request)
var keyFetchToken = request.auth.credentials
keyFetchToken
.del()
.then(Account.get.bind(null, keyFetchToken.uid))
db.deleteKeyFetchToken(keyFetchToken)
.then(
function (account) {
if (!account) {
throw error.unknownAccount()
}
if (!account.verified) {
function () {
if (!keyFetchToken.verified) {
throw error.unverifiedAccount()
}
return {
bundle: keyFetchToken.bundle(account.kA, account.wrapKb)
bundle: bundleKeys(
keyFetchToken,
keyFetchToken.kA,
keyFetchToken.wrapKb
)
}
}
)
@ -152,21 +181,12 @@ module.exports = function (log, crypto, uuid, isA, error, Account, RecoveryEmail
handler: function (request) {
log.begin('Account.RecoveryEmailStatus', request)
var sessionToken = request.auth.credentials
Account
.get(sessionToken.uid)
.done(
function (account) {
request.reply(
{
email: account.email,
verified: account.verified
}
)
},
function (err) {
request.reply(err)
}
)
request.reply(
{
email: sessionToken.email,
verified: sessionToken.verified
}
)
},
validate: {
response: {
@ -192,24 +212,13 @@ module.exports = function (log, crypto, uuid, isA, error, Account, RecoveryEmail
handler: function (request) {
log.begin('Account.RecoveryEmailResend', request)
var sessionToken = request.auth.credentials
Account
.get(sessionToken.uid)
sendVerifyCode(sessionToken.email, sessionToken.emailCode)
.done(
function (account) {
return account.primaryRecoveryEmail()
.then(
function (rm) {
return rm.sendVerifyCode()
}
)
.then(
function () {
request.reply({})
},
function (err) {
request.reply(err)
}
)
function () {
request.reply({})
},
function (err) {
request.reply(err)
}
)
}
@ -226,24 +235,13 @@ module.exports = function (log, crypto, uuid, isA, error, Account, RecoveryEmail
log.begin('Account.RecoveryEmailVerify', request)
var uid = request.payload.uid
var code = request.payload.code
RecoveryEmail
.get(uid, code)
db.account(uid)
.then(
function (rm) {
if (! rm) {
function (account) {
if (code !== account.code) {
throw error.invalidVerificationCode()
}
return rm
}
)
.then(
function (rm) {
return rm.verify(code)
}
)
.then(
function (rm) {
return Account.verify(rm)
return db.verifyEmail(account)
}
)
.done(
@ -287,18 +285,11 @@ module.exports = function (log, crypto, uuid, isA, error, Account, RecoveryEmail
var reply = request.reply.bind(request)
var accountResetToken = request.auth.credentials
var payload = request.payload
var unbundle = accountResetToken.unbundle(payload.bundle)
var unbundle = unbundleReset(payload.bundle)
payload.wrapKb = unbundle.wrapKb
payload.srp.verifier = unbundle.verifier
accountResetToken
.del()
.then(Account.get.bind(null, accountResetToken.uid))
.then(
function (account) {
return account.reset(payload)
}
)
db.resetAccount(accountResetToken, payload)
.then(function () { return {} })
.done(reply, reply)
},
@ -316,13 +307,8 @@ module.exports = function (log, crypto, uuid, isA, error, Account, RecoveryEmail
log.begin('Account.destroy', request)
var reply = request.reply.bind(request)
var authToken = request.auth.credentials
authToken.del()
.then(
function () {
return Account.del(authToken.uid)
}
)
db.deleteAccount(authToken)
.then(function () { return {} })
.done(reply, reply)
}
}

Просмотреть файл

@ -6,6 +6,14 @@ module.exports = function (log, isA, error, Account, SrpSession, AuthBundle) {
const HEX_STRING = /^(?:[a-fA-F0-9]{2})+$/
function clientData(srpToken) {
}
function bundleAuth(k, authToken) {
}
var routes = [
{
method: 'POST',
@ -19,8 +27,17 @@ module.exports = function (log, isA, error, Account, SrpSession, AuthBundle) {
handler: function (request) {
log.begin('Auth.start', request)
var reply = request.reply.bind(request)
Account.getByEmail(request.payload.email)
.then(SrpSession.start.bind(null))
db.emailRecord(request.payload.email)
.then(
function (emailRecord) {
return db.createSrpToken(emailRecord)
}
)
.then(
function (srpToken) {
return clientData(srpToken)
}
)
.done(reply, reply)
},
validate: {
@ -53,11 +70,21 @@ module.exports = function (log, isA, error, Account, SrpSession, AuthBundle) {
handler: function (request) {
log.begin('Auth.finish', request)
var reply = request.reply.bind(request)
SrpSession
.finish(request.payload.srpToken, request.payload.A, request.payload.M)
db.srpToken(request.payload.srpToken)
.then(
function (srpSession) {
return AuthBundle.login(srpSession.K, srpSession.uid)
function (srpToken) {
return srpFinish(srpToken, request.payload.A, request.payload.M)
}
)
.then(
function (srpToken) {
return db.authFinish(srpToken)
.then(
function (authToken) {
return bundleAuth(srpToken.K, authToken)
}
)
}
)
.done(reply, reply)

Просмотреть файл

@ -2,7 +2,7 @@
* 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, P, kv) {
module.exports = function (log, P, db) {
var routes = [
{
@ -21,7 +21,7 @@ module.exports = function (log, P, kv) {
config: {
handler: function heartbeat(request) {
log.begin('Defaults.heartbeat', request)
P.all([kv.store.ping(), kv.cache.ping()])
db.ping()
.then(
function () {
request.reply({})

Просмотреть файл

@ -15,16 +15,16 @@ module.exports = function (
log,
serverPublicKey,
signer,
models,
db,
config
) {
var auth = require('./auth')(log, isA, error, models.Account, models.SrpSession, models.AuthBundle)
var defaults = require('./defaults')(log, P, models.dbs)
var defaults = require('./defaults')(log, P, db)
var idp = require('./idp')(log, crypto, error, isA, serverPublicKey, config.bridge)
var account = require('./account')(log, crypto, uuid, isA, error, models.Account, models.RecoveryEmail)
var password = require('./password')(log, isA, error, models.Account, models.tokens)
var session = require('./session')(log, isA, error, models.Account, models.tokens)
var sign = require('./sign')(log, isA, error, signer, models.Account)
var sign = require('./sign')(log, isA, error, signer, config.domain)
var util = require('./util')(log, crypto, error, isA, serverPublicKey, config.bridge)
var raw = require('./rawpassword')(log, isA, error, config, Client)

Просмотреть файл

@ -2,10 +2,26 @@
* 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, isA, error, Account, tokens) {
module.exports = function (log, isA, error, db) {
const HEX_STRING = /^(?:[a-fA-F0-9]{2})+$/
function bundleAccountReset(authToken, keyFetchToken, accountResetToken) {
//TODO
}
function sendRecoveryCode(forgotPasswordToken) {
//TODO
}
function ttl(forgotPasswordToken) {
}
function failAttempt(forgotPasswordToken) {
}
var routes = [
{
method: 'POST',
@ -20,26 +36,11 @@ module.exports = function (log, isA, error, Account, tokens) {
log.begin('Password.changeStart', request)
var reply = request.reply.bind(request)
var authToken = request.auth.credentials
var keyFetchToken = null
var accountResetToken = null
authToken.del()
db.createPasswordChange(authToken)
.then(
function () {
return tokens.KeyFetchToken.create(authToken.uid)
}
)
.then(function (t) { keyFetchToken = t })
.then(tokens.AccountResetToken.create.bind(null, authToken.uid))
.then(function (t) { accountResetToken = t })
.then(Account.get.bind(null, authToken.uid))
.then(
function (account) {
return account.setResetToken(accountResetToken)
}
)
.then(
function () {
return authToken.bundleAccountReset(
return bundleAccountReset(
authToken,
keyFetchToken,
accountResetToken
)
@ -66,22 +67,15 @@ module.exports = function (log, isA, error, Account, tokens) {
handler: function (request) {
log.begin('Password.forgotSend', request)
var email = request.payload.email
var forgotPasswordToken = null
Account.getByEmail(email)
db.emailRecord(email)
.then(
function (account) {
return tokens.ForgotPasswordToken.create(account.uid, account.email)
.then(
function (token) {
forgotPasswordToken = token
return account.setForgotPasswordToken(token)
}
)
.then(
function () {
return forgotPasswordToken.sendRecoveryCode()
}
)
function (emailRecord) {
return db.createForgotPasswordToken(emailRecord)
}
)
.then(
function (forgotPasswordToken) {
return sendRecoveryCode(forgotPasswordToken)
}
)
.done(
@ -89,8 +83,8 @@ module.exports = function (log, isA, error, Account, tokens) {
request.reply(
{
forgotPasswordToken: forgotPasswordToken.data,
ttl: forgotPasswordToken.ttl(),
codeLength: 8,
ttl: ttl(forgotPasswordToken),
codeLength: forgotPasswordToken.passcode.length,
tries: forgotPasswordToken.tries
}
)
@ -128,14 +122,14 @@ module.exports = function (log, isA, error, Account, tokens) {
handler: function (request) {
log.begin('Password.forgotResend', request)
var forgotPasswordToken = request.auth.credentials
forgotPasswordToken.sendRecoveryCode()
sendRecoveryCode(forgotPasswordToken)
.done(
function () {
request.reply(
{
forgotPasswordToken: forgotPasswordToken.data,
ttl: forgotPasswordToken.ttl(),
codeLength: 8,
ttl: forgotPasswordToken.ttl(forgotPasswordToken),
codeLength: ttl(forgotPasswordToken),
tries: forgotPasswordToken.tries
}
)
@ -174,19 +168,10 @@ module.exports = function (log, isA, error, Account, tokens) {
log.begin('Password.forgotVerify', request)
var forgotPasswordToken = request.auth.credentials
var code = +(request.payload.code)
var accountResetToken = null
if (forgotPasswordToken.code === code && forgotPasswordToken.ttl() > 0) {
forgotPasswordToken.del()
.then(tokens.AccountResetToken.create.bind(null, forgotPasswordToken.uid))
.then(function (t) { accountResetToken = t })
.then(Account.get.bind(null, forgotPasswordToken.uid))
.then(
function (account) {
return account.setResetToken(accountResetToken)
}
)
if (forgotPasswordToken.code === code && ttl(forgotPasswordToken) > 0) {
db.forgotPasswordVerified(forgotPasswordToken)
.done(
function () {
function (accountResetToken) {
request.reply(
{
accountResetToken: accountResetToken.data
@ -199,7 +184,7 @@ module.exports = function (log, isA, error, Account, tokens) {
)
}
else {
forgotPasswordToken.failAttempt()
failAttempt(forgotPasswordToken)
.done(
function (t) {
request.reply(

Просмотреть файл

@ -2,10 +2,14 @@
* 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, isA, error, Account, tokens) {
module.exports = function (log, isA, error, db) {
const HEX_STRING = /^(?:[a-fA-F0-9]{2})+$/
function bundleSession(authToken, keyFetchToken, sessionToken) {
// TODO
}
var routes = [
{
method: 'POST',
@ -20,26 +24,14 @@ module.exports = function (log, isA, error, Account, tokens) {
log.begin('Session.create', request)
var reply = request.reply.bind(request)
var authToken = request.auth.credentials
var keyFetchToken = null
var sessionToken = null
authToken.del()
db.createSession(authToken)
.then(
function () {
return tokens.KeyFetchToken.create(authToken.uid)
}
)
.then(function (t) { keyFetchToken = t })
.then(tokens.SessionToken.create.bind(null, authToken.uid))
.then(function (t) { sessionToken = t })
.then(Account.get.bind(null, authToken.uid))
.then(
function (account) {
return account.addSessionToken(sessionToken)
}
)
.then(
function () {
return authToken.bundleSession(keyFetchToken, sessionToken)
function (tokens) {
return bundleSession(
authToken,
tokens.keyFetchToken,
tokens.sessionToken
)
}
)
.then(
@ -65,17 +57,7 @@ module.exports = function (log, isA, error, Account, tokens) {
handler: function (request) {
log.begin('Session.destroy', request)
var sessionToken = request.auth.credentials
sessionToken.del()
.then(
function () {
return Account.get(sessionToken.uid)
}
)
.then(
function (account) {
return account.deleteSessionToken(sessionToken.id)
}
)
db.deleteSessionToken(sessionToken)
.done(
function () {
request.reply({})

Просмотреть файл

@ -2,7 +2,7 @@
* 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, isA, error, signer, Account) {
module.exports = function (log, isA, error, signer, domain) {
const HOUR = 1000 * 60 * 60
@ -32,38 +32,32 @@ module.exports = function (log, isA, error, signer, Account) {
},
handler: function certificateSign(request) {
log.begin('Sign.cert', request)
var uid = request.auth.credentials.uid
Account
.get(uid)
.done(
function (account) {
if (!account.verified) {
return request.reply(error.unverifiedAccount())
}
signer.enqueue(
{
email: Account.principal(uid),
publicKey: JSON.stringify(request.payload.publicKey),
duration: request.payload.duration
},
function (err, result) {
if (err || result.err) {
request.reply(
// XXX TODO: differentiate backlog overflow (503)
// from a generic server-side failure (500)
error.serviceUnavailable(
'Unable to sign certificate',
err || result.err
)
)
}
else {
request.reply(result)
}
}
var sessionToken = request.auth.credentials
if (!sessionToken.verified) {
return request.reply(error.unverifiedAccount())
}
signer.enqueue(
{
email: uid + '@' + domain,
publicKey: JSON.stringify(request.payload.publicKey),
duration: request.payload.duration
},
function (err, result) {
if (err || result.err) {
request.reply(
// XXX TODO: differentiate backlog overflow (503)
// from a generic server-side failure (500)
error.serviceUnavailable(
'Unable to sign certificate',
err || result.err
)
)
}
)
else {
request.reply(result)
}
}
)
}
}
}

Просмотреть файл

@ -4,7 +4,7 @@
module.exports = function (path, url, Hapi, toobusy, error) {
function create(log, config, routes, tokens) {
function create(log, config, routes, db) {
// Hawk needs to calculate request signatures based on public URL,
// not the local URL to which it is bound.
@ -18,6 +18,17 @@ module.exports = function (path, url, Hapi, toobusy, error) {
port: publicURL.port ? publicURL.port : defaultPorts[publicURL.protocol]
}
function makeCredentialFn(dbGetFn) {
return function (id, cb) {
log.trace({ op: 'db.' + dbGetFn.name, id: id })
dbGetFn(id)
.done(
cb.bind(null, null),
cb
)
}
}
var server = Hapi.createServer(
config.bind_to.host,
config.bind_to.port,
@ -26,27 +37,27 @@ module.exports = function (path, url, Hapi, toobusy, error) {
sessionToken: {
scheme: 'hawk',
hawk: hawkOptions,
getCredentialsFunc: tokens.SessionToken.getCredentials
getCredentialsFunc: makeCredentialFn(db.sessionToken)
},
keyFetchToken: {
scheme: 'hawk',
hawk: hawkOptions,
getCredentialsFunc: tokens.KeyFetchToken.getCredentials
getCredentialsFunc: makeCredentialFn(db.keyFetchToken)
},
accountResetToken: {
scheme: 'hawk',
hawk: hawkOptions,
getCredentialsFunc: tokens.AccountResetToken.getCredentials
getCredentialsFunc: makeCredentialFn(db.accountResetToken)
},
authToken: {
scheme: 'hawk',
hawk: hawkOptions,
getCredentialsFunc: tokens.AuthToken.getCredentials
getCredentialsFunc: makeCredentialFn(db.authToken)
},
forgotPasswordToken: {
scheme: 'hawk',
hawk: hawkOptions,
getCredentialsFunc: tokens.ForgotPasswordToken.getCredentials
getCredentialsFunc: makeCredentialFn(db.forgotPasswordToken)
}
},
cors: true,