test(all): switch tap runner for mocha

This commit is contained in:
Sean McArthur 2016-11-08 12:04:46 -08:00
Родитель 6aa8ef2f91
Коммит 6c815d0416
74 изменённых файлов: 9625 добавлений и 9694 удалений

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

@ -58,6 +58,9 @@ function AppError(options, extra, headers) {
this.message = options.message || DEFAULTS.message
this.isBoom = true
this.stack = options.stack
if (!this.stack) {
Error.captureStackTrace(this, AppError)
}
this.errno = options.errno || DEFAULTS.errno
this.output = {
statusCode: options.code || DEFAULTS.code,

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

@ -262,19 +262,5 @@ module.exports = function (level, name, options) {
}
)
Object.keys(console).forEach(
function (key) {
console[key] = function () { // eslint-disable-line no-console
var json = { op: 'console', message: util.format.apply(null, arguments) }
if(log[key]) {
log[key](json)
}
else {
log.warn(json)
}
}
}
)
return log
}

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

@ -13,9 +13,9 @@
"start": "NODE_ENV=dev scripts/start-local.sh 2>&1",
"start-mysql": "NODE_ENV=dev scripts/start-local-mysql.sh 2>&1",
"test-quick": "npm run tq",
"test-e2e": "NODE_ENV=dev tap test/e2e 2>/dev/null",
"tq": "NODE_ENV=dev tap test/local 2>/dev/null && NODE_ENV=dev CORS_ORIGIN=https://bar scripts/test-remote-quick.js",
"test-remote": "MAILER_HOST=restmail.net MAILER_PORT=80 CORS_ORIGIN=http://baz tap --timeout=300 --tap test/remote",
"test-e2e": "NODE_ENV=dev mocha test/e2e",
"tq": "NODE_ENV=dev mocha test/local 2>/dev/null && NODE_ENV=dev CORS_ORIGIN=https://bar scripts/test-remote-quick.js",
"test-remote": "MAILER_HOST=restmail.net MAILER_PORT=80 CORS_ORIGIN=http://baz mocha --timeout=300000 test/remote",
"prepush": "grunt quicklint"
},
"repository": {
@ -77,16 +77,18 @@
"grunt-nsp": "2.3.1",
"hawk": "2.3.1",
"husky": "0.11.7",
"insist": "1.0.0",
"jws": "3.1.3",
"leftpad": "0.0.0",
"load-grunt-tasks": "3.5.2",
"mailparser": "0.6.1",
"mocha": "3.1.2",
"nock": "8.0.0",
"nyc": "8.4.0",
"proxyquire": "1.7.10",
"simplesmtp": "0.3.35",
"sinon": "1.17.5",
"sjcl": "1.0.6",
"tap": "7.1.2",
"ws": "1.1.1"
}
}

28
scripts/mocha-coverage.js Executable file
Просмотреть файл

@ -0,0 +1,28 @@
#!/usr/bin/env node
/* 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/. */
'use strict'
const path = require('path')
const spawn = require('child_process').spawn
const MOCHA_BIN = path.join(path.dirname(__dirname), 'node_modules', '.bin', 'mocha')
const NYC_BIN = path.join(path.dirname(__dirname), 'node_modules', '.bin', 'nyc')
let bin = NYC_BIN
let argv = ['--cache', MOCHA_BIN]
if (process.env.NO_COVERAGE) {
bin = MOCHA_BIN
argv = []
}
const p = spawn(bin, argv.concat(process.argv.slice(2)), { stdio: 'inherit', env: process.env })
// exit this process with the same exit code as the test process
p.on('close', function (code) {
process.exit(code)
})

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

@ -1,21 +0,0 @@
#!/usr/bin/env node
/* 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 path = require('path'),
spawn = require('child_process').spawn
var COVERAGE_ARGS = ['--coverage', '--cov']
if (process.env.NO_COVERAGE) {
COVERAGE_ARGS = []
}
var p = spawn(path.join(path.dirname(__dirname), 'node_modules', '.bin', 'tap'),
process.argv.slice(2).concat(COVERAGE_ARGS), { stdio: 'inherit', env: process.env })
// exit this process with the same exit code as the test process
p.on('close', function (code) {
process.exit(code)
})

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

@ -10,5 +10,5 @@ fi
./scripts/gen_keys.js
./scripts/gen_vapid_keys.js
./scripts/check-i18n.js
./scripts/tap-coverage.js $glob 2>/dev/null
./scripts/mocha-coverage.js -R dot $glob
grunt eslint copyright

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

@ -12,13 +12,13 @@ var TestServer = require('../test/test_server')
TestServer.start(config, false)
.then(
function (server) {
var tap = spawn(
path.join(path.dirname(__dirname), 'node_modules/.bin/tap'),
var cp = spawn(
path.join(path.dirname(__dirname), 'node_modules/.bin/mocha'),
['test/remote'],
{ stdio: 'inherit' }
)
tap.on('close', function(code) {
cp.on('close', function(code) {
server.stop()
})
}

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

@ -2,5 +2,8 @@ plugins:
- fxa
extends: ../.eslintrc
env:
mocha: true
rules:
fxa/async-crypto-random: 0

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

@ -2,10 +2,11 @@
* 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 tap = require('tap')
'use strict'
const assert = require('insist')
var proxyquire = require('proxyquire')
var test = tap.test
var P = require('../../lib/promise')
var config = require('../../config').getProperties()
var spyLog = require('../mocks').spyLog
@ -13,53 +14,58 @@ var mockUid = new Buffer('foo')
var PushManager = require('../push_helper').PushManager
var pushManager = new PushManager({
server: 'wss://push.services.mozilla.com/',
channelId: '9500b5e6-9954-40d5-8ac1-3920832e781e'
})
describe('e2e/push', () => {
test(
'pushToAllDevices sends notifications using a real push server',
function (t) {
pushManager.getSubscription().then(function (subscription) { // eslint-disable-line no-unreachable
var mockDbResult = {
devices: function (/* uid */) {
return P.resolve([
{
'id': '0f7aa00356e5416e82b3bef7bc409eef',
'isCurrentDevice': true,
'lastAccessTime': 1449235471335,
'name': 'My Phone',
'type': 'mobile',
'pushCallback': subscription.endpoint,
'pushPublicKey': 'BBXOKjUb84pzws1wionFpfCBjDuCh4-s_1b52WA46K5wYL2gCWEOmFKWn_NkS5nmJwTBuO8qxxdjAIDtNeklvQc',
'pushAuthKey': 'GSsIiaD2Mr83iPqwFNK4rw'
}
])
},
updateDevice: function () {
return P.resolve()
}
}
let pushManager
before(() => {
pushManager = new PushManager({
server: 'wss://push.services.mozilla.com/',
channelId: '9500b5e6-9954-40d5-8ac1-3920832e781e'
})
})
var thisSpyLog = spyLog({
info: function (log) {
if (log.name === 'push.account_verify.success') {
t.end()
it(
'pushToAllDevices sends notifications using a real push server',
() => {
return pushManager.getSubscription().then(function (subscription) { // eslint-disable-line no-unreachable
var mockDbResult = {
devices: function (/* uid */) {
return P.resolve([
{
'id': '0f7aa00356e5416e82b3bef7bc409eef',
'isCurrentDevice': true,
'lastAccessTime': 1449235471335,
'name': 'My Phone',
'type': 'mobile',
'pushCallback': subscription.endpoint,
'pushPublicKey': 'BBXOKjUb84pzws1wionFpfCBjDuCh4-s_1b52WA46K5wYL2gCWEOmFKWn_NkS5nmJwTBuO8qxxdjAIDtNeklvQc',
'pushAuthKey': 'GSsIiaD2Mr83iPqwFNK4rw'
}
])
},
updateDevice: function () {
return P.resolve()
}
}
})
var push = proxyquire('../../lib/push', {})(thisSpyLog, mockDbResult, config)
var options = {
data: new Buffer('foodata')
}
push.pushToAllDevices(mockUid, 'accountVerify', options).then(function() {
if (thisSpyLog.error.callCount !== 0) {
throw new Error('No errors should have been logged')
let count = 0
var thisSpyLog = spyLog({
info: function (log) {
if (log.name === 'push.account_verify.success') {
count++
}
}
})
var push = proxyquire('../../lib/push', {})(thisSpyLog, mockDbResult, config)
var options = {
data: new Buffer('foodata')
}
return push.pushToAllDevices(mockUid, 'accountVerify', options).then(function() {
assert.equal(thisSpyLog.error.callCount, 0, 'No errors should have been logged')
assert.equal(count, 1)
})
})
})
}
)
}
)
})

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

@ -2,58 +2,60 @@
* 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 log = { trace: function() {} }
'use strict'
var tokens = require('../../lib/tokens')(log)
var AccountResetToken = tokens.AccountResetToken
const assert = require('insist')
var ACCOUNT = {
const log = { trace() {} }
const tokens = require('../../lib/tokens')(log)
const AccountResetToken = tokens.AccountResetToken
const ACCOUNT = {
uid: 'xxx'
}
describe('account reset tokens', () => {
test(
're-creation from tokenData works',
function (t) {
var token = null
return AccountResetToken.create(ACCOUNT)
.then(
function (x) {
token = x
}
)
.then(
function () {
return AccountResetToken.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)
}
)
}
)
it(
'should re-create from tokenData',
() => {
let token = null
return AccountResetToken.create(ACCOUNT)
.then(
(x) => {
token = x
}
)
.then(
() => AccountResetToken.fromHex(token.data, ACCOUNT)
)
.then(
(token2) => {
assert.deepEqual(token.data, token2.data)
assert.deepEqual(token.id, token2.id)
assert.deepEqual(token.authKey, token2.authKey)
assert.deepEqual(token.bundleKey, token2.bundleKey)
assert.deepEqual(token.uid, token2.uid)
}
)
}
)
test(
'accountResetToken key derivations are test-vector compliant',
function (t) {
var token = null
var tokenData = 'c0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedf'
return AccountResetToken.fromHex(tokenData, ACCOUNT)
.then(
function (x) {
token = x
t.equal(token.data.toString('hex'), tokenData)
t.equal(token.id.toString('hex'), '46ec557e56e531a058620e9344ca9c75afac0d0bcbdd6f8c3c2f36055d9540cf')
t.equal(token.authKey.toString('hex'), '716ebc28f5122ef48670a48209190a1605263c3188dfe45256265929d1c45e48')
t.equal(token.bundleKey.toString('hex'), 'aa5906d2318c6e54ecebfa52f10df4c036165c230cc78ee859f546c66ea3c126')
}
)
}
)
it(
'should have test-vector compliant key derivations',
() => {
let token = null
const tokenData = 'c0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedf'
return AccountResetToken.fromHex(tokenData, ACCOUNT)
.then(
(x) => {
token = x
assert.equal(token.data.toString('hex'), tokenData)
assert.equal(token.id.toString('hex'), '46ec557e56e531a058620e9344ca9c75afac0d0bcbdd6f8c3c2f36055d9540cf')
assert.equal(token.authKey.toString('hex'), '716ebc28f5122ef48670a48209190a1605263c3188dfe45256265929d1c45e48')
assert.equal(token.bundleKey.toString('hex'), 'aa5906d2318c6e54ecebfa52f10df4c036165c230cc78ee859f546c66ea3c126')
}
)
}
)
})

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -2,26 +2,27 @@
* 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/. */
const test = require('tap').test
const assert = require('insist')
const base32 = require('../../lib/crypto/base32')
test('base32 takes 1 integer argument, returns a function', (t) => {
t.equal(typeof base32, 'function')
t.equal(base32.length, 1)
const gen = base32(10)
t.equal(typeof gen, 'function')
t.equal(gen.length, 0)
t.end()
})
describe('base32', () => {
it('takes 1 integer argument, returns a function', () => {
assert.equal(typeof base32, 'function')
assert.equal(base32.length, 1)
const gen = base32(10)
assert.equal(typeof gen, 'function')
assert.equal(gen.length, 0)
})
test('base32 output', (t) => {
const gen = base32(10)
return gen().then(code => {
t.equal(code.length, 10, 'matches length')
t.ok(/^[0-9A-Z]+$/.test(code), 'no lowercase letters')
t.equal(code.indexOf('I'), -1, 'no I')
t.equal(code.indexOf('L'), -1, 'no L')
t.equal(code.indexOf('O'), -1, 'no O')
t.equal(code.indexOf('U'), -1, 'no U')
it('should have correct output', () => {
const gen = base32(10)
return gen().then(code => {
assert.equal(code.length, 10, 'matches length')
assert.ok(/^[0-9A-Z]+$/.test(code), 'no lowercase letters')
assert.equal(code.indexOf('I'), -1, 'no I')
assert.equal(code.indexOf('L'), -1, 'no L')
assert.equal(code.indexOf('O'), -1, 'no O')
assert.equal(code.indexOf('U'), -1, 'no U')
})
})
})

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

@ -2,9 +2,10 @@
* 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/. */
const assert = require('insist')
var EventEmitter = require('events').EventEmitter
var sinon = require('sinon')
var test = require('../ptaptest')
var spyLog = require('../mocks').spyLog
var error = require('../../lib/error')
var P = require('../../lib/promise')
@ -22,302 +23,304 @@ function mockedBounces(log, db) {
return bounces(log, error)(mockBounceQueue, db)
}
test(
'unknown message types are silently ignored',
function (t) {
var mockLog = spyLog()
var mockDB = {}
return mockedBounces(mockLog, mockDB).handleBounce(mockMessage({
junk: 'message'
})).then(function () {
t.equal(mockLog.messages.length, 0)
})
}
)
describe('bounce messages', () => {
it(
'should ignore unknown message types',
() => {
var mockLog = spyLog()
var mockDB = {}
return mockedBounces(mockLog, mockDB).handleBounce(mockMessage({
junk: 'message'
})).then(function () {
assert.equal(mockLog.messages.length, 0)
})
}
)
test(
'multiple recipients are all handled in turn',
function (t) {
var mockLog = spyLog()
var mockDB = {
emailRecord: sinon.spy(function (email) {
return P.resolve({
uid: '123456',
email: email,
emailVerified: false
it(
'should handle multiple recipients in turn',
() => {
var mockLog = spyLog()
var mockDB = {
emailRecord: sinon.spy(function (email) {
return P.resolve({
uid: '123456',
email: email,
emailVerified: false
})
}),
deleteAccount: sinon.spy(function (record) {
return P.resolve({ })
})
}),
deleteAccount: sinon.spy(function (record) {
return P.resolve({ })
})
}
var mockMsg = mockMessage({
bounce: {
bounceType: 'Permanent',
bouncedRecipients: [
{ emailAddress: 'test@example.com' },
{ emailAddress: 'foobar@example.com'}
]
}
})
return mockedBounces(mockLog, mockDB).handleBounce(mockMsg).then(function () {
t.equal(mockDB.emailRecord.callCount, 2)
t.equal(mockDB.deleteAccount.callCount, 2)
t.equal(mockDB.emailRecord.args[0][0], 'test@example.com')
t.equal(mockDB.emailRecord.args[1][0], 'foobar@example.com')
t.equal(mockLog.messages.length, 6, 'messages logged')
t.equal(mockLog.messages[1].level, 'increment')
t.equal(mockLog.messages[1].args[0], 'account.email_bounced')
t.equal(mockLog.messages[5].level, 'info')
t.equal(mockLog.messages[5].args[0].op, 'accountDeleted')
t.equal(mockLog.messages[5].args[0].email, 'foobar@example.com')
t.equal(mockMsg.del.callCount, 1)
})
}
)
test(
'abuse complaints are treated like bounces',
function (t) {
var mockLog = spyLog()
var mockDB = {
emailRecord: sinon.spy(function (email) {
return P.resolve({
uid: '123456',
email: email,
emailVerified: false
})
}),
deleteAccount: sinon.spy(function (record) {
return P.resolve({ })
})
}
return mockedBounces(mockLog, mockDB).handleBounce(mockMessage({
complaint: {
complaintFeedbackType: 'abuse',
complainedRecipients: [
{ emailAddress: 'test@example.com' },
{ emailAddress: 'foobar@example.com'}
]
}
})).then(function () {
t.equal(mockDB.emailRecord.callCount, 2)
t.equal(mockDB.deleteAccount.callCount, 2)
t.equal(mockDB.emailRecord.args[0][0], 'test@example.com')
t.equal(mockDB.emailRecord.args[1][0], 'foobar@example.com')
t.equal(mockLog.messages.length, 6)
})
}
)
test(
'verified accounts are not deleted on bounce',
function (t) {
var mockLog = spyLog()
var mockDB = {
emailRecord: sinon.spy(function (email) {
return P.resolve({
uid: '123456',
email: email,
emailVerified: (email === 'verified@example.com')
})
}),
deleteAccount: sinon.spy(function (record) {
return P.resolve({ })
})
}
return mockedBounces(mockLog, mockDB).handleBounce(mockMessage({
bounce: {
bounceType: 'Permanent',
// docs: http://docs.aws.amazon.com/ses/latest/DeveloperGuide/notification-contents.html#bounced-recipients
bouncedRecipients: [
{ emailAddress: 'test@example.com', action: 'failed', status: '5.0.0', diagnosticCode: 'smtp; 550 user unknown' },
{ emailAddress: 'verified@example.com', status: '4.0.0' }
]
}
})).then(function () {
t.equal(mockDB.emailRecord.callCount, 2)
t.equal(mockDB.emailRecord.args[0][0], 'test@example.com')
t.equal(mockDB.emailRecord.args[1][0], 'verified@example.com')
t.equal(mockDB.deleteAccount.callCount, 1)
t.equal(mockDB.deleteAccount.args[0][0].email, 'test@example.com')
t.equal(mockLog.messages.length, 6)
t.equal(mockLog.messages[0].args[0].op, 'handleBounce')
t.equal(mockLog.messages[0].args[0].email, 'test@example.com')
t.equal(mockLog.messages[0].args[0].status, '5.0.0')
t.equal(mockLog.messages[0].args[0].action, 'failed')
t.equal(mockLog.messages[0].args[0].diagnosticCode, 'smtp; 550 user unknown')
t.equal(mockLog.messages[1].args[0], 'account.email_bounced')
t.equal(mockLog.messages[2].args[0].op, 'accountDeleted')
t.equal(mockLog.messages[2].args[0].email, 'test@example.com')
t.equal(mockLog.messages[3].args[0].op, 'handleBounce')
t.equal(mockLog.messages[3].args[0].email, 'verified@example.com')
t.equal(mockLog.messages[3].args[0].status, '4.0.0')
t.notOk(mockLog.messages[3].args[0].diagnosticCode)
t.equal(mockLog.messages[4].args[0], 'account.email_bounced')
t.equal(mockLog.messages[5].level, 'increment')
t.equal(mockLog.messages[5].args[0], 'account.email_bounced.already_verified')
})
}
)
test(
'errors when looking up the email record are logged',
function (t) {
var mockLog = spyLog()
var mockDB = {
emailRecord: sinon.spy(function (email) {
return P.reject(new error({}))
})
}
var mockMsg = mockMessage({
bounce: {
bounceType: 'Permanent',
bouncedRecipients: [
{ emailAddress: 'test@example.com' },
]
}
})
return mockedBounces(mockLog, mockDB).handleBounce(mockMsg).then(function () {
t.equal(mockDB.emailRecord.callCount, 1)
t.equal(mockDB.emailRecord.args[0][0], 'test@example.com')
t.equal(mockLog.messages.length, 3)
t.equal(mockLog.messages[0].args[0].op, 'handleBounce')
t.equal(mockLog.messages[0].args[0].email, 'test@example.com')
t.equal(mockLog.messages[1].args[0], 'account.email_bounced')
t.equal(mockLog.messages[2].args[0].op, 'databaseError')
t.equal(mockLog.messages[2].args[0].email, 'test@example.com')
t.equal(mockMsg.del.callCount, 1)
})
}
)
test(
'errors when deleting the email record are logged',
function (t) {
var mockLog = spyLog()
var mockDB = {
emailRecord: sinon.spy(function (email) {
return P.resolve({
uid: '123456',
email: email,
emailVerified: false
})
}),
deleteAccount: sinon.spy(function (record) {
return P.reject(new error.unknownAccount('test@example.com'))
})
}
var mockMsg = mockMessage({
bounce: {
bounceType: 'Permanent',
bouncedRecipients: [
{ emailAddress: 'test@example.com' },
]
}
})
return mockedBounces(mockLog, mockDB).handleBounce(mockMsg).then(function () {
t.equal(mockDB.emailRecord.callCount, 1)
t.equal(mockDB.emailRecord.args[0][0], 'test@example.com')
t.equal(mockDB.deleteAccount.callCount, 1)
t.equal(mockDB.deleteAccount.args[0][0].email, 'test@example.com')
t.equal(mockLog.messages.length, 3)
t.equal(mockLog.messages[0].args[0].op, 'handleBounce')
t.equal(mockLog.messages[0].args[0].email, 'test@example.com')
t.equal(mockLog.messages[1].args[0], 'account.email_bounced')
t.equal(mockLog.messages[2].args[0].op, 'databaseError')
t.equal(mockLog.messages[2].args[0].email, 'test@example.com')
t.equal(mockLog.messages[2].args[0].err.errno, error.ERRNO.ACCOUNT_UNKNOWN)
t.equal(mockMsg.del.callCount, 1)
})
}
)
test(
'quoted email addresses are normalized for lookup',
function (t) {
var mockLog = spyLog()
var mockDB = {
emailRecord: sinon.spy(function (email) {
// Lookup only succeeds when using original, unquoted email addr.
if (email !== 'test.@example.com') {
return P.reject(new error.unknownAccount(email))
var mockMsg = mockMessage({
bounce: {
bounceType: 'Permanent',
bouncedRecipients: [
{ emailAddress: 'test@example.com' },
{ emailAddress: 'foobar@example.com'}
]
}
return P.resolve({
uid: '123456',
email: email,
emailVerified: false
})
}),
deleteAccount: sinon.spy(function (record) {
return P.resolve({ })
})
return mockedBounces(mockLog, mockDB).handleBounce(mockMsg).then(function () {
assert.equal(mockDB.emailRecord.callCount, 2)
assert.equal(mockDB.deleteAccount.callCount, 2)
assert.equal(mockDB.emailRecord.args[0][0], 'test@example.com')
assert.equal(mockDB.emailRecord.args[1][0], 'foobar@example.com')
assert.equal(mockLog.messages.length, 6, 'messages logged')
assert.equal(mockLog.messages[1].level, 'increment')
assert.equal(mockLog.messages[1].args[0], 'account.email_bounced')
assert.equal(mockLog.messages[5].level, 'info')
assert.equal(mockLog.messages[5].args[0].op, 'accountDeleted')
assert.equal(mockLog.messages[5].args[0].email, 'foobar@example.com')
assert.equal(mockMsg.del.callCount, 1)
})
}
return mockedBounces(mockLog, mockDB).handleBounce(mockMessage({
bounce: {
bounceType: 'Permanent',
bouncedRecipients: [
// Bounce message has email addr in quoted form, since some
// mail agents normalize it in this way.
{ emailAddress: '"test."@example.com' },
]
}
})).then(function () {
t.equal(mockDB.emailRecord.callCount, 2)
t.equal(mockDB.emailRecord.args[0][0], '"test."@example.com')
t.equal(mockDB.emailRecord.args[1][0], 'test.@example.com')
t.equal(mockDB.deleteAccount.callCount, 1)
t.equal(mockDB.deleteAccount.args[0][0].email, 'test.@example.com')
})
}
)
)
test(
'can log email template name and bounceType',
function (t) {
var mockLog = spyLog()
var mockDB = {
emailRecord: sinon.spy(function (email) {
return P.resolve({
uid: '123456',
email: email,
emailVerified: false
it(
'should treat abuse complaints like bounces',
() => {
var mockLog = spyLog()
var mockDB = {
emailRecord: sinon.spy(function (email) {
return P.resolve({
uid: '123456',
email: email,
emailVerified: false
})
}),
deleteAccount: sinon.spy(function (record) {
return P.resolve({ })
})
}),
deleteAccount: sinon.spy(function () {
return P.resolve({ })
}
return mockedBounces(mockLog, mockDB).handleBounce(mockMessage({
complaint: {
complaintFeedbackType: 'abuse',
complainedRecipients: [
{ emailAddress: 'test@example.com' },
{ emailAddress: 'foobar@example.com'}
]
}
})).then(function () {
assert.equal(mockDB.emailRecord.callCount, 2)
assert.equal(mockDB.deleteAccount.callCount, 2)
assert.equal(mockDB.emailRecord.args[0][0], 'test@example.com')
assert.equal(mockDB.emailRecord.args[1][0], 'foobar@example.com')
assert.equal(mockLog.messages.length, 6)
})
}
var mockMsg = mockMessage({
bounce: {
bounceType: 'Permanent',
bounceSubType: 'General',
bouncedRecipients: [
{emailAddress: 'test@example.com'}
]
},
mail: {
headers: [
{
name: 'X-Template-Name',
value: 'verifyLoginEmail'
)
it(
'should not delete verified accounts on bounce',
() => {
var mockLog = spyLog()
var mockDB = {
emailRecord: sinon.spy(function (email) {
return P.resolve({
uid: '123456',
email: email,
emailVerified: (email === 'verified@example.com')
})
}),
deleteAccount: sinon.spy(function (record) {
return P.resolve({ })
})
}
return mockedBounces(mockLog, mockDB).handleBounce(mockMessage({
bounce: {
bounceType: 'Permanent',
// docs: http://docs.aws.amazon.com/ses/latest/DeveloperGuide/notification-contents.html#bounced-recipients
bouncedRecipients: [
{ emailAddress: 'test@example.com', action: 'failed', status: '5.0.0', diagnosticCode: 'smtp; 550 user unknown' },
{ emailAddress: 'verified@example.com', status: '4.0.0' }
]
}
})).then(function () {
assert.equal(mockDB.emailRecord.callCount, 2)
assert.equal(mockDB.emailRecord.args[0][0], 'test@example.com')
assert.equal(mockDB.emailRecord.args[1][0], 'verified@example.com')
assert.equal(mockDB.deleteAccount.callCount, 1)
assert.equal(mockDB.deleteAccount.args[0][0].email, 'test@example.com')
assert.equal(mockLog.messages.length, 6)
assert.equal(mockLog.messages[0].args[0].op, 'handleBounce')
assert.equal(mockLog.messages[0].args[0].email, 'test@example.com')
assert.equal(mockLog.messages[0].args[0].status, '5.0.0')
assert.equal(mockLog.messages[0].args[0].action, 'failed')
assert.equal(mockLog.messages[0].args[0].diagnosticCode, 'smtp; 550 user unknown')
assert.equal(mockLog.messages[1].args[0], 'account.email_bounced')
assert.equal(mockLog.messages[2].args[0].op, 'accountDeleted')
assert.equal(mockLog.messages[2].args[0].email, 'test@example.com')
assert.equal(mockLog.messages[3].args[0].op, 'handleBounce')
assert.equal(mockLog.messages[3].args[0].email, 'verified@example.com')
assert.equal(mockLog.messages[3].args[0].status, '4.0.0')
assert(!mockLog.messages[3].args[0].diagnosticCode)
assert.equal(mockLog.messages[4].args[0], 'account.email_bounced')
assert.equal(mockLog.messages[5].level, 'increment')
assert.equal(mockLog.messages[5].args[0], 'account.email_bounced.already_verified')
})
}
)
it(
'should log errors when looking up the email record',
() => {
var mockLog = spyLog()
var mockDB = {
emailRecord: sinon.spy(function (email) {
return P.reject(new error({}))
})
}
var mockMsg = mockMessage({
bounce: {
bounceType: 'Permanent',
bouncedRecipients: [
{ emailAddress: 'test@example.com' },
]
}
})
return mockedBounces(mockLog, mockDB).handleBounce(mockMsg).then(function () {
assert.equal(mockDB.emailRecord.callCount, 1)
assert.equal(mockDB.emailRecord.args[0][0], 'test@example.com')
assert.equal(mockLog.messages.length, 3)
assert.equal(mockLog.messages[0].args[0].op, 'handleBounce')
assert.equal(mockLog.messages[0].args[0].email, 'test@example.com')
assert.equal(mockLog.messages[1].args[0], 'account.email_bounced')
assert.equal(mockLog.messages[2].args[0].op, 'databaseError')
assert.equal(mockLog.messages[2].args[0].email, 'test@example.com')
assert.equal(mockMsg.del.callCount, 1)
})
}
)
it(
'should log errors when deleting the email record',
() => {
var mockLog = spyLog()
var mockDB = {
emailRecord: sinon.spy(function (email) {
return P.resolve({
uid: '123456',
email: email,
emailVerified: false
})
}),
deleteAccount: sinon.spy(function (record) {
return P.reject(new error.unknownAccount('test@example.com'))
})
}
var mockMsg = mockMessage({
bounce: {
bounceType: 'Permanent',
bouncedRecipients: [
{ emailAddress: 'test@example.com' },
]
}
})
return mockedBounces(mockLog, mockDB).handleBounce(mockMsg).then(function () {
assert.equal(mockDB.emailRecord.callCount, 1)
assert.equal(mockDB.emailRecord.args[0][0], 'test@example.com')
assert.equal(mockDB.deleteAccount.callCount, 1)
assert.equal(mockDB.deleteAccount.args[0][0].email, 'test@example.com')
assert.equal(mockLog.messages.length, 3)
assert.equal(mockLog.messages[0].args[0].op, 'handleBounce')
assert.equal(mockLog.messages[0].args[0].email, 'test@example.com')
assert.equal(mockLog.messages[1].args[0], 'account.email_bounced')
assert.equal(mockLog.messages[2].args[0].op, 'databaseError')
assert.equal(mockLog.messages[2].args[0].email, 'test@example.com')
assert.equal(mockLog.messages[2].args[0].err.errno, error.ERRNO.ACCOUNT_UNKNOWN)
assert.equal(mockMsg.del.callCount, 1)
})
}
)
it(
'should normalize quoted email addresses for lookup',
() => {
var mockLog = spyLog()
var mockDB = {
emailRecord: sinon.spy(function (email) {
// Lookup only succeeds when using original, unquoted email addr.
if (email !== 'test.@example.com') {
return P.reject(new error.unknownAccount(email))
}
]
return P.resolve({
uid: '123456',
email: email,
emailVerified: false
})
}),
deleteAccount: sinon.spy(function (record) {
return P.resolve({ })
})
}
})
return mockedBounces(mockLog, mockDB).handleBounce(mockMessage({
bounce: {
bounceType: 'Permanent',
bouncedRecipients: [
// Bounce message has email addr in quoted form, since some
// mail agents normalize it in this way.
{ emailAddress: '"test."@example.com' },
]
}
})).then(function () {
assert.equal(mockDB.emailRecord.callCount, 2)
assert.equal(mockDB.emailRecord.args[0][0], '"test."@example.com')
assert.equal(mockDB.emailRecord.args[1][0], 'test.@example.com')
assert.equal(mockDB.deleteAccount.callCount, 1)
assert.equal(mockDB.deleteAccount.args[0][0].email, 'test.@example.com')
})
}
)
return mockedBounces(mockLog, mockDB).handleBounce(mockMsg).then(function () {
t.equal(mockDB.emailRecord.callCount, 1)
t.equal(mockDB.emailRecord.args[0][0], 'test@example.com')
t.equal(mockDB.deleteAccount.callCount, 1)
t.equal(mockDB.deleteAccount.args[0][0].email, 'test@example.com')
t.equal(mockLog.messages.length, 3)
t.equal(mockLog.messages[0].args[0].op, 'handleBounce')
t.equal(mockLog.messages[0].args[0].email, 'test@example.com')
t.equal(mockLog.messages[0].args[0].template, 'verifyLoginEmail')
t.equal(mockLog.messages[0].args[0].bounceType, 'Permanent')
t.equal(mockLog.messages[0].args[0].bounceSubType, 'General')
t.equal(mockLog.messages[1].args[0], 'account.email_bounced')
})
}
)
it(
'should log email template name and bounceType',
() => {
var mockLog = spyLog()
var mockDB = {
emailRecord: sinon.spy(function (email) {
return P.resolve({
uid: '123456',
email: email,
emailVerified: false
})
}),
deleteAccount: sinon.spy(function () {
return P.resolve({ })
})
}
var mockMsg = mockMessage({
bounce: {
bounceType: 'Permanent',
bounceSubType: 'General',
bouncedRecipients: [
{emailAddress: 'test@example.com'}
]
},
mail: {
headers: [
{
name: 'X-Template-Name',
value: 'verifyLoginEmail'
}
]
}
})
return mockedBounces(mockLog, mockDB).handleBounce(mockMsg).then(function () {
assert.equal(mockDB.emailRecord.callCount, 1)
assert.equal(mockDB.emailRecord.args[0][0], 'test@example.com')
assert.equal(mockDB.deleteAccount.callCount, 1)
assert.equal(mockDB.deleteAccount.args[0][0].email, 'test@example.com')
assert.equal(mockLog.messages.length, 3)
assert.equal(mockLog.messages[0].args[0].op, 'handleBounce')
assert.equal(mockLog.messages[0].args[0].email, 'test@example.com')
assert.equal(mockLog.messages[0].args[0].template, 'verifyLoginEmail')
assert.equal(mockLog.messages[0].args[0].bounceType, 'Permanent')
assert.equal(mockLog.messages[0].args[0].bounceSubType, 'General')
assert.equal(mockLog.messages[1].args[0], 'account.email_bounced')
})
}
)
})

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

@ -2,100 +2,105 @@
* 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('tap').test
var butil = require('../../lib/crypto/butil')
'use strict'
test(
'buffersAreEqual returns false if lengths are different',
function (t) {
t.equal(butil.buffersAreEqual(Buffer(2), Buffer(4)), false)
t.end()
}
)
const assert = require('assert')
const butil = require('../../lib/crypto/butil')
test(
'buffersAreEqual returns true if buffers have same bytes',
function (t) {
var b1 = Buffer('abcd', 'hex')
var b2 = Buffer('abcd', 'hex')
t.equal(butil.buffersAreEqual(b1, b2), true)
t.end()
}
)
describe('butil', () => {
test(
'xorBuffers throws an Error if lengths are different',
function (t) {
try {
butil.xorBuffers(Buffer(2), Buffer(4))
}
catch (e) {
return t.end()
}
t.fail('did not throw')
}
)
describe('.buffersAreEqual', () => {
test(
'xorBuffers works',
function (t) {
var b1 = Buffer('e5', 'hex')
var b2 = Buffer('5e', 'hex')
t.deepEqual(butil.xorBuffers(b1, b2), Buffer('bb', 'hex'))
t.end()
}
)
it(
'returns false if lengths are different',
() => {
assert.equal(butil.buffersAreEqual(Buffer(2), Buffer(4)), false)
}
)
test(
'bufferize works',
function (t) {
var argument = { foo: 'bar', baz: 'f00d' }
var result = butil.bufferize(argument)
t.notEqual(result, argument)
t.equal(Object.keys(result).length, Object.keys(argument).length)
t.equal(typeof result.foo, 'string')
t.equal(result.foo, argument.foo)
t.ok(Buffer.isBuffer(result.baz))
t.equal(result.baz.length, 2)
t.equal(result.baz[0], 0xf0)
t.equal(result.baz[1], 0x0d)
t.end()
}
)
it(
'returns true if buffers have same bytes',
() => {
const b1 = Buffer('abcd', 'hex')
const b2 = Buffer('abcd', 'hex')
assert.equal(butil.buffersAreEqual(b1, b2), true)
}
)
test(
'bufferize works in-place',
function (t) {
var argument = { foo: 'beef', bar: 'baz' }
var result = butil.bufferize(argument, { inplace: true })
t.equal(result, argument)
t.equal(Object.keys(result).length, 2)
t.ok(Buffer.isBuffer(result.foo))
t.equal(result.foo.length, 2)
t.equal(result.foo[0], 0xbe)
t.equal(result.foo[1], 0xef)
t.equal(typeof result.bar, 'string')
t.equal(result.bar, 'baz')
t.end()
}
)
})
test(
'bufferize ignores exceptions',
function (t) {
var argument = { foo: 'bar', baz: 'f00d', qux: 'beef' }
var result = butil.bufferize(argument, { ignore: [ 'baz' ] })
t.notEqual(argument, result)
t.equal(Object.keys(result).length, Object.keys(argument).length)
t.equal(typeof result.foo, 'string')
t.equal(result.foo, argument.foo)
t.equal(typeof result.baz, 'string')
t.equal(result.baz, argument.baz)
t.ok(Buffer.isBuffer(result.qux))
t.equal(result.qux.length, 2)
t.equal(result.qux[0], 0xbe)
t.equal(result.qux[1], 0xef)
t.end()
}
)
describe('.xorBuffers', () => {
it(
'throws an Error if lengths are different',
() => {
assert.throws(() => {
butil.xorBuffers(Buffer(2), Buffer(4))
})
}
)
it(
'should return a Buffer with bits ORed',
() => {
const b1 = Buffer('e5', 'hex')
const b2 = Buffer('5e', 'hex')
assert.deepEqual(butil.xorBuffers(b1, b2), Buffer('bb', 'hex'))
}
)
})
describe('.bufferize', () => {
it(
'should bufferize hex-looking values',
() => {
const argument = { foo: 'bar', baz: 'f00d' }
const result = butil.bufferize(argument)
assert.notEqual(result, argument)
assert.equal(Object.keys(result).length, Object.keys(argument).length)
assert.equal(typeof result.foo, 'string')
assert.equal(result.foo, argument.foo)
assert.ok(Buffer.isBuffer(result.baz))
assert.equal(result.baz.length, 2)
assert.equal(result.baz[0], 0xf0)
assert.equal(result.baz[1], 0x0d)
}
)
it(
'should convert in-place',
() => {
const argument = { foo: 'beef', bar: 'baz' }
const result = butil.bufferize(argument, { inplace: true })
assert.equal(result, argument)
assert.equal(Object.keys(result).length, 2)
assert.ok(Buffer.isBuffer(result.foo))
assert.equal(result.foo.length, 2)
assert.equal(result.foo[0], 0xbe)
assert.equal(result.foo[1], 0xef)
assert.equal(typeof result.bar, 'string')
assert.equal(result.bar, 'baz')
}
)
it(
'should ignore exceptions',
() => {
const argument = { foo: 'bar', baz: 'f00d', qux: 'beef' }
const result = butil.bufferize(argument, { ignore: [ 'baz' ] })
assert.notEqual(argument, result)
assert.equal(Object.keys(result).length, Object.keys(argument).length)
assert.equal(typeof result.foo, 'string')
assert.equal(result.foo, argument.foo)
assert.equal(typeof result.baz, 'string')
assert.equal(result.baz, argument.baz)
assert.ok(Buffer.isBuffer(result.qux))
assert.equal(result.qux.length, 2)
assert.equal(result.qux[0], 0xbe)
assert.equal(result.qux[1], 0xef)
}
)
})
})

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

@ -2,12 +2,12 @@
* 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')
const assert = require('insist')
const log = {
trace: () => {},
activityEvent: () => {},
flowEvent: () => {},
error: console.error, // eslint-disable-line no-console
error() {}
}
const mocks = require('../mocks')
var error = require('../../lib/error.js')
@ -27,434 +27,372 @@ var customsServer = nock(CUSTOMS_URL_REAL)
'Content-Type': 'application/json'
})
test(
"can create a customs object with url as 'none'",
function (t) {
t.plan(7)
describe('Customs', () => {
it(
"can create a customs object with url as 'none'",
() => {
customsNoUrl = new Customs('none')
customsNoUrl = new Customs('none')
assert.ok(customsNoUrl, 'got a customs object with a none url')
t.ok(customsNoUrl, 'got a customs object with a none url')
var request = newRequest()
var ip = request.app.clientAddress
var email = newEmail()
var action = newAction()
var request = newRequest()
var ip = request.app.clientAddress
var email = newEmail()
var action = newAction()
return customsNoUrl.check(request, email, action)
.then(function(result) {
t.equal(result, undefined, 'Nothing is returned when /check succeeds')
t.pass('Passed /check (no url)')
}, function(error) {
t.fail('We should have failed open (no url provided) for /check')
})
.then(function() {
return customsNoUrl.flag(ip, { email: email, uid: '12345' })
})
.then(function(result) {
t.equal(result, undefined, 'Nothing is returned when /failedLoginAttempt succeeds')
t.pass('Passed /failedLoginAttempt')
}, function(error) {
t.fail('We should have failed open for /failedLoginAttempt')
})
.then(function() {
return customsNoUrl.reset(email)
})
.then(function(result) {
t.equal(result, undefined, 'Nothing is returned when /passwordReset succeeds')
t.pass('Passed /passwordReset')
}, function(error) {
t.fail('We should have failed open (no url provided) for /failedLoginAttempt')
})
}
)
test(
'can create a customs object with a url',
function (t) {
t.plan(29)
customsWithUrl = new Customs(CUSTOMS_URL_REAL)
t.ok(customsWithUrl, 'got a customs object with a valid url')
var request = newRequest()
var ip = request.app.clientAddress
var email = newEmail()
var action = newAction()
// Mock a check that does not get blocked.
customsServer.post('/check', function (body) {
t.deepEqual(body, {
ip: ip,
email: email,
action: action,
headers: request.headers,
query: request.query,
payload: request.payload,
}, 'first call to /check had expected request params')
return true
}).reply(200, {
block: false,
retryAfter: 0
})
return customsWithUrl.check(request, email, action)
.then(function(result) {
t.equal(result, undefined, 'Nothing is returned when /check succeeds')
t.pass('Passed /check (with url)')
}, function(error) {
t.fail('We should not have failed here for /check : err=' + error)
})
.then(function() {
// Mock a report of a failed login attempt
customsServer.post('/failedLoginAttempt', function (body) {
t.deepEqual(body, {
ip: ip,
email: email,
errno: error.ERRNO.UNEXPECTED_ERROR
}, 'first call to /failedLoginAttempt had expected request params')
return true
}).reply(200, {})
return customsWithUrl.flag(ip, { email: email, uid: '12345' })
})
.then(function(result) {
t.equal(result, undefined, 'Nothing is returned when /failedLoginAttempt succeeds')
t.pass('Passed /failedLoginAttempt')
}, function(error) {
t.fail('We should not have failed here for /failedLoginAttempt : err=' + error)
})
.then(function() {
// Mock a report of a password reset.
customsServer.post('/passwordReset', function (body) {
t.deepEqual(body, {
email: email,
}, 'first call to /passwordReset had expected request params')
return true
}).reply(200, {})
return customsWithUrl.reset(email)
})
.then(function(result) {
t.equal(result, undefined, 'Nothing is returned when /passwordReset succeeds')
t.pass('Passed /passwordReset')
}, function(error) {
t.fail('We should not have failed here for /passwordReset : err=' + error)
})
.then(function() {
// Mock a check that does get blocked, with a retryAfter.
customsServer.post('/check', function (body) {
t.deepEqual(body, {
ip: ip,
email: email,
action: action,
headers: request.headers,
query: request.query,
payload: request.payload,
}, 'second call to /check had expected request params')
return true
}).reply(200, {
block: true,
retryAfter: 10001
return customsNoUrl.check(request, email, action)
.then(function(result) {
assert.equal(result, undefined, 'Nothing is returned when /check succeeds')
})
return customsWithUrl.check(request, email, action)
.then(function() {
return customsNoUrl.flag(ip, { email: email, uid: '12345' })
})
.then(function(result) {
assert.equal(result, undefined, 'Nothing is returned when /failedLoginAttempt succeeds')
})
.then(function() {
return customsNoUrl.reset(email)
})
.then(function(result) {
assert.equal(result, undefined, 'Nothing is returned when /passwordReset succeeds')
})
}
)
it(
'can create a customs object with a url',
() => {
customsWithUrl = new Customs(CUSTOMS_URL_REAL)
assert.ok(customsWithUrl, 'got a customs object with a valid url')
var request = newRequest()
var ip = request.app.clientAddress
var email = newEmail()
var action = newAction()
// Mock a check that does not get blocked.
customsServer.post('/check', function (body) {
assert.deepEqual(body, {
ip: ip,
email: email,
action: action,
headers: request.headers,
query: request.query,
payload: request.payload,
}, 'first call to /check had expected request params')
return true
}).reply(200, {
block: false,
retryAfter: 0
})
.then(function(result) {
t.fail('This should have failed the check since it should be blocked')
}, function(err) {
t.pass('Since we faked a block, we should have arrived here')
t.equal(err.errno, error.ERRNO.THROTTLED, 'Error number is correct')
t.equal(err.message, 'Client has sent too many requests', 'Error message is correct')
t.ok(err.isBoom, 'The error causes a boom')
t.equal(err.output.statusCode, 429, 'Status Code is correct')
t.equal(err.output.payload.retryAfter, 10001, 'retryAfter is correct')
t.equal(err.output.headers['retry-after'], 10001, 'retryAfter header is correct')
})
.then(function() {
// Mock a report of a failed login attempt that does trigger lockout.
customsServer.post('/failedLoginAttempt', function (body) {
t.deepEqual(body, {
ip: ip,
return customsWithUrl.check(request, email, action)
.then(function(result) {
assert.equal(result, undefined, 'Nothing is returned when /check succeeds')
})
.then(function() {
// Mock a report of a failed login attempt
customsServer.post('/failedLoginAttempt', function (body) {
assert.deepEqual(body, {
ip: ip,
email: email,
errno: error.ERRNO.UNEXPECTED_ERROR
}, 'first call to /failedLoginAttempt had expected request params')
return true
}).reply(200, {})
return customsWithUrl.flag(ip, { email: email, uid: '12345' })
})
.then(function(result) {
assert.equal(result, undefined, 'Nothing is returned when /failedLoginAttempt succeeds')
})
.then(function() {
// Mock a report of a password reset.
customsServer.post('/passwordReset', function (body) {
assert.deepEqual(body, {
email: email,
}, 'first call to /passwordReset had expected request params')
return true
}).reply(200, {})
return customsWithUrl.reset(email)
})
.then(function(result) {
assert.equal(result, undefined, 'Nothing is returned when /passwordReset succeeds')
})
.then(function() {
// Mock a check that does get blocked, with a retryAfter.
customsServer.post('/check', function (body) {
assert.deepEqual(body, {
ip: ip,
email: email,
action: action,
headers: request.headers,
query: request.query,
payload: request.payload,
}, 'second call to /check had expected request params')
return true
}).reply(200, {
block: true,
retryAfter: 10001
})
return customsWithUrl.check(request, email, action)
})
.then(function(result) {
assert(false, 'This should have failed the check since it should be blocked')
}, function(err) {
assert.equal(err.errno, error.ERRNO.THROTTLED, 'Error number is correct')
assert.equal(err.message, 'Client has sent too many requests', 'Error message is correct')
assert.ok(err.isBoom, 'The error causes a boom')
assert.equal(err.output.statusCode, 429, 'Status Code is correct')
assert.equal(err.output.payload.retryAfter, 10001, 'retryAfter is correct')
assert.equal(err.output.headers['retry-after'], 10001, 'retryAfter header is correct')
})
.then(function() {
// Mock a report of a failed login attempt that does trigger lockout.
customsServer.post('/failedLoginAttempt', function (body) {
assert.deepEqual(body, {
ip: ip,
email: email,
errno: error.ERRNO.INCORRECT_PASSWORD
}, 'second call to /failedLoginAttempt had expected request params')
return true
}).reply(200, { })
return customsWithUrl.flag(ip, {
email: email,
errno: error.ERRNO.INCORRECT_PASSWORD
}, 'second call to /failedLoginAttempt had expected request params')
return true
}).reply(200, { })
return customsWithUrl.flag(ip, {
})
})
.then(function(result) {
assert.equal(result, undefined, 'Nothing is returned when /failedLoginAttempt succeeds')
})
.then(function() {
// Mock a check that does get blocked, with no retryAfter.
request.headers['user-agent'] = 'test passing through headers'
request.payload['foo'] = 'bar'
customsServer.post('/check', function (body) {
assert.deepEqual(body, {
ip: ip,
email: email,
action: action,
headers: request.headers,
query: request.query,
payload: request.payload,
}, 'third call to /check had expected request params')
return true
}).reply(200, {
block: true
})
return customsWithUrl.check(request, email, action)
})
.then(function(result) {
assert(false, 'This should have failed the check since it should be blocked')
}, function(err) {
assert.equal(err.errno, error.ERRNO.REQUEST_BLOCKED, 'Error number is correct')
assert.equal(err.message, 'The request was blocked for security reasons', 'Error message is correct')
assert.ok(err.isBoom, 'The error causes a boom')
assert.equal(err.output.statusCode, 400, 'Status Code is correct')
assert(!err.output.payload.retryAfter, 'retryAfter field is not present')
assert(!err.output.headers['retry-after'], 'retryAfter header is not present')
})
}
)
it(
'can create a customs object with non-existant customs service',
() => {
customsInvalidUrl = new Customs(CUSTOMS_URL_MISSING)
assert.ok(customsInvalidUrl, 'got a customs object with a non-existant service url')
var request = newRequest()
var ip = request.app.clientAddress
var email = newEmail()
var action = newAction()
return customsInvalidUrl.check(request, email, action)
.then(function(result) {
assert.equal(result, undefined, 'Nothing is returned when /check succeeds even when service is non-existant')
})
.then(function() {
return customsInvalidUrl.flag(ip, { email: email, uid: '12345' })
})
.then(function(result) {
assert.equal(result, undefined, 'Nothing is returned when /failedLoginAttempt succeeds')
})
.then(function() {
return customsInvalidUrl.reset(email)
})
.then(function(result) {
assert.equal(result, undefined, 'Nothing is returned when /passwordReset succeeds')
})
}
)
it(
'can rate limit checkAccountStatus /check',
() => {
customsWithUrl = new Customs(CUSTOMS_URL_REAL)
assert.ok(customsWithUrl, 'can rate limit checkAccountStatus /check')
var request = newRequest()
var ip = request.app.clientAddress
var email = newEmail()
var action = 'accountStatusCheck'
function checkRequestBody (body) {
assert.deepEqual(body, {
ip: ip,
email: email,
errno: error.ERRNO.INCORRECT_PASSWORD
action: action,
headers: request.headers,
query: request.query,
payload: request.payload,
}, 'call to /check had expected request params')
return true
}
customsServer
.post('/check', checkRequestBody).reply(200, '{"block":false,"retryAfter":0}')
.post('/check', checkRequestBody).reply(200, '{"block":false,"retryAfter":0}')
.post('/check', checkRequestBody).reply(200, '{"block":false,"retryAfter":0}')
.post('/check', checkRequestBody).reply(200, '{"block":false,"retryAfter":0}')
.post('/check', checkRequestBody).reply(200, '{"block":false,"retryAfter":0}')
.post('/check', checkRequestBody).reply(200, '{"block":true,"retryAfter":10001}')
return customsWithUrl.check(request, email, action)
.then(function(result) {
assert.equal(result, undefined, 'Nothing is returned when /check succeeds - 1')
return customsWithUrl.check(request, email, action)
})
})
.then(function(result) {
t.equal(result, undefined, 'Nothing is returned when /failedLoginAttempt succeeds')
t.pass('Passed /failedLoginAttempt')
}, function(error) {
t.fail('We should not have failed here for /failedLoginAttempt : err=' + error)
})
.then(function() {
// Mock a check that does get blocked, with no retryAfter.
request.headers['user-agent'] = 'test passing through headers'
request.payload['foo'] = 'bar'
customsServer.post('/check', function (body) {
t.deepEqual(body, {
ip: ip,
email: email,
action: action,
headers: request.headers,
query: request.query,
payload: request.payload,
}, 'third call to /check had expected request params')
return true
}).reply(200, {
block: true
.then(function(result) {
assert.equal(result, undefined, 'Nothing is returned when /check succeeds - 2')
return customsWithUrl.check(request, email, action)
})
.then(function(result) {
assert.equal(result, undefined, 'Nothing is returned when /check succeeds - 3')
return customsWithUrl.check(request, email, action)
})
.then(function(result) {
assert.equal(result, undefined, 'Nothing is returned when /check succeeds - 4')
return customsWithUrl.check(request, email, action)
})
.then(function() {
// request is blocked
return customsWithUrl.check(request, email, action)
})
.then(function() {
assert(false, 'This should have failed the check since it should be blocked')
}, function(error) {
assert.equal(error.errno, 114, 'Error number is correct')
assert.equal(error.message, 'Client has sent too many requests', 'Error message is correct')
assert.ok(error.isBoom, 'The error causes a boom')
assert.equal(error.output.statusCode, 429, 'Status Code is correct')
assert.equal(error.output.payload.retryAfter, 10001, 'retryAfter is correct')
assert.equal(error.output.payload.retryAfterLocalized, 'in 3 hours', 'retryAfterLocalized is correct')
assert.equal(error.output.headers['retry-after'], 10001, 'retryAfter header is correct')
})
return customsWithUrl.check(request, email, action)
})
.then(function(result) {
t.fail('This should have failed the check since it should be blocked')
}, function(err) {
t.pass('Since we faked a block, we should have arrived here')
t.equal(err.errno, error.ERRNO.REQUEST_BLOCKED, 'Error number is correct')
t.equal(err.message, 'The request was blocked for security reasons', 'Error message is correct')
t.ok(err.isBoom, 'The error causes a boom')
t.equal(err.output.statusCode, 400, 'Status Code is correct')
t.notOk(err.output.payload.retryAfter, 'retryAfter field is not present')
t.notOk(err.output.headers['retry-after'], 'retryAfter header is not present')
})
}
)
test(
'can create a customs object with non-existant customs service',
function (t) {
t.plan(7)
customsInvalidUrl = new Customs(CUSTOMS_URL_MISSING)
t.ok(customsInvalidUrl, 'got a customs object with a non-existant service url')
var request = newRequest()
var ip = request.app.clientAddress
var email = newEmail()
var action = newAction()
return customsInvalidUrl.check(request, email, action)
.then(function(result) {
t.equal(result, undefined, 'Nothing is returned when /check succeeds even when service is non-existant')
t.pass('Passed /check (no url)')
}, function(error) {
t.fail('We should have failed open (non-existant service url provided) for /check')
})
.then(function() {
return customsInvalidUrl.flag(ip, { email: email, uid: '12345' })
})
.then(function(result) {
t.equal(result, undefined, 'Nothing is returned when /failedLoginAttempt succeeds')
t.pass('Passed /failedLoginAttempt')
}, function(error) {
t.fail('We should have failed open (no url provided) for /failedLoginAttempt')
})
.then(function() {
return customsInvalidUrl.reset(email)
})
.then(function(result) {
t.equal(result, undefined, 'Nothing is returned when /passwordReset succeeds')
t.pass('Passed /passwordReset')
}, function(error) {
t.fail('We should have failed open (no url provided) for /failedLoginAttempt')
})
}
)
test(
'can rate limit checkAccountStatus /check',
function (t) {
t.plan(19)
customsWithUrl = new Customs(CUSTOMS_URL_REAL)
t.ok(customsWithUrl, 'can rate limit checkAccountStatus /check')
var request = newRequest()
var ip = request.app.clientAddress
var email = newEmail()
var action = 'accountStatusCheck'
function checkRequestBody (body) {
t.deepEqual(body, {
ip: ip,
email: email,
action: action,
headers: request.headers,
query: request.query,
payload: request.payload,
}, 'call to /check had expected request params')
return true
}
)
customsServer
.post('/check', checkRequestBody).reply(200, '{"block":false,"retryAfter":0}')
.post('/check', checkRequestBody).reply(200, '{"block":false,"retryAfter":0}')
.post('/check', checkRequestBody).reply(200, '{"block":false,"retryAfter":0}')
.post('/check', checkRequestBody).reply(200, '{"block":false,"retryAfter":0}')
.post('/check', checkRequestBody).reply(200, '{"block":false,"retryAfter":0}')
.post('/check', checkRequestBody).reply(200, '{"block":true,"retryAfter":10001}')
it(
'can rate limit devicesNotify /checkAuthenticated',
() => {
customsWithUrl = new Customs(CUSTOMS_URL_REAL)
return customsWithUrl.check(request, email, action)
.then(function(result) {
t.equal(result, undefined, 'Nothing is returned when /check succeeds - 1')
return customsWithUrl.check(request, email, action)
}, function(error) {
t.fail('We should not have failed here for /check : err=' + error)
})
.then(function(result) {
t.equal(result, undefined, 'Nothing is returned when /check succeeds - 2')
return customsWithUrl.check(request, email, action)
}, function(error) {
t.fail('We should not have failed here for /check : err=' + error)
})
.then(function(result) {
t.equal(result, undefined, 'Nothing is returned when /check succeeds - 3')
return customsWithUrl.check(request, email, action)
}, function(error) {
t.fail('We should not have failed here for /check : err=' + error)
})
.then(function(result) {
t.equal(result, undefined, 'Nothing is returned when /check succeeds - 4')
return customsWithUrl.check(request, email, action)
}, function(error) {
t.fail('We should not have failed here for /check : err=' + error)
})
.then(function() {
// request is blocked
return customsWithUrl.check(request, email, action)
})
.then(function() {
t.fail('This should have failed the check since it should be blocked')
}, function(error) {
t.pass('Since we faked a block, we should have arrived here')
t.equal(error.errno, 114, 'Error number is correct')
t.equal(error.message, 'Client has sent too many requests', 'Error message is correct')
t.ok(error.isBoom, 'The error causes a boom')
t.equal(error.output.statusCode, 429, 'Status Code is correct')
t.equal(error.output.payload.retryAfter, 10001, 'retryAfter is correct')
t.equal(error.output.payload.retryAfterLocalized, 'in 3 hours', 'retryAfterLocalized is correct')
t.equal(error.output.headers['retry-after'], 10001, 'retryAfter header is correct')
})
}
)
assert.ok(customsWithUrl, 'can rate limit /checkAuthenticated')
test(
'can rate limit devicesNotify /checkAuthenticated',
function (t) {
t.plan(18)
var request = newRequest()
var action = 'devicesNotify'
var ip = request.app.clientAddress
var uid = 'foo'
customsWithUrl = new Customs(CUSTOMS_URL_REAL)
function checkRequestBody (body) {
assert.deepEqual(body, {
action: action,
ip: ip,
uid: uid,
}, 'call to /checkAuthenticated had expected request params')
return true
}
t.ok(customsWithUrl, 'can rate limit /checkAuthenticated')
customsServer
.post('/checkAuthenticated', checkRequestBody).reply(200, '{"block":false,"retryAfter":0}')
.post('/checkAuthenticated', checkRequestBody).reply(200, '{"block":false,"retryAfter":0}')
.post('/checkAuthenticated', checkRequestBody).reply(200, '{"block":false,"retryAfter":0}')
.post('/checkAuthenticated', checkRequestBody).reply(200, '{"block":false,"retryAfter":0}')
.post('/checkAuthenticated', checkRequestBody).reply(200, '{"block":false,"retryAfter":0}')
.post('/checkAuthenticated', checkRequestBody).reply(200, '{"block":true,"retryAfter":10001}')
var request = newRequest()
var action = 'devicesNotify'
var ip = request.app.clientAddress
var uid = 'foo'
function checkRequestBody (body) {
t.deepEqual(body, {
action: action,
ip: ip,
uid: uid,
}, 'call to /checkAuthenticated had expected request params')
return true
return customsWithUrl.checkAuthenticated(action, ip, uid)
.then(function(result) {
assert.equal(result, undefined, 'Nothing is returned when /checkAuthenticated succeeds - 1')
return customsWithUrl.checkAuthenticated(action, ip, uid)
})
.then(function(result) {
assert.equal(result, undefined, 'Nothing is returned when /checkAuthenticated succeeds - 2')
return customsWithUrl.checkAuthenticated(action, ip, uid)
})
.then(function(result) {
assert.equal(result, undefined, 'Nothing is returned when /checkAuthenticated succeeds - 3')
return customsWithUrl.checkAuthenticated(action, ip, uid)
})
.then(function(result) {
assert.equal(result, undefined, 'Nothing is returned when /checkAuthenticated succeeds - 4')
return customsWithUrl.checkAuthenticated(action, ip, uid)
})
.then(function() {
// request is blocked
return customsWithUrl.checkAuthenticated(action, ip, uid)
})
.then(function() {
assert(false, 'This should have failed the check since it should be blocked')
}, function(error) {
assert.equal(error.errno, 114, 'Error number is correct')
assert.equal(error.message, 'Client has sent too many requests', 'Error message is correct')
assert.ok(error.isBoom, 'The error causes a boom')
assert.equal(error.output.statusCode, 429, 'Status Code is correct')
assert.equal(error.output.payload.retryAfter, 10001, 'retryAfter is correct')
assert.equal(error.output.headers['retry-after'], 10001, 'retryAfter header is correct')
})
}
)
customsServer
.post('/checkAuthenticated', checkRequestBody).reply(200, '{"block":false,"retryAfter":0}')
.post('/checkAuthenticated', checkRequestBody).reply(200, '{"block":false,"retryAfter":0}')
.post('/checkAuthenticated', checkRequestBody).reply(200, '{"block":false,"retryAfter":0}')
.post('/checkAuthenticated', checkRequestBody).reply(200, '{"block":false,"retryAfter":0}')
.post('/checkAuthenticated', checkRequestBody).reply(200, '{"block":false,"retryAfter":0}')
.post('/checkAuthenticated', checkRequestBody).reply(200, '{"block":true,"retryAfter":10001}')
it(
'can scrub customs request object',
() => {
customsWithUrl = new Customs(CUSTOMS_URL_REAL)
return customsWithUrl.checkAuthenticated(action, ip, uid)
.then(function(result) {
t.equal(result, undefined, 'Nothing is returned when /checkAuthenticated succeeds - 1')
return customsWithUrl.checkAuthenticated(action, ip, uid)
}, function(error) {
t.fail('We should not have failed here for /checkAuthenticated : err=' + error)
assert.ok(customsWithUrl, 'got a customs object with a valid url')
var request = newRequest()
request.payload.authPW = 'asdfasdfadsf'
var ip = request.app.clientAddress
var email = newEmail()
var action = newAction()
customsServer.post('/check', function (body) {
assert.deepEqual(body, {
ip: ip,
email: email,
action: action,
headers: request.headers,
query: request.query,
payload: {}
}, 'should not have authPW in payload')
return true
}).reply(200, {
block: false,
retryAfter: 0
})
.then(function(result) {
t.equal(result, undefined, 'Nothing is returned when /checkAuthenticated succeeds - 2')
return customsWithUrl.checkAuthenticated(action, ip, uid)
}, function(error) {
t.fail('We should not have failed here for /checkAuthenticated : err=' + error)
})
.then(function(result) {
t.equal(result, undefined, 'Nothing is returned when /checkAuthenticated succeeds - 3')
return customsWithUrl.checkAuthenticated(action, ip, uid)
}, function(error) {
t.fail('We should not have failed here for /checkAuthenticated : err=' + error)
})
.then(function(result) {
t.equal(result, undefined, 'Nothing is returned when /checkAuthenticated succeeds - 4')
return customsWithUrl.checkAuthenticated(action, ip, uid)
}, function(error) {
t.fail('We should not have failed here for /checkAuthenticated : err=' + error)
})
.then(function() {
// request is blocked
return customsWithUrl.checkAuthenticated(action, ip, uid)
})
.then(function() {
t.fail('This should have failed the check since it should be blocked')
}, function(error) {
t.pass('Since we faked a block, we should have arrived here')
t.equal(error.errno, 114, 'Error number is correct')
t.equal(error.message, 'Client has sent too many requests', 'Error message is correct')
t.ok(error.isBoom, 'The error causes a boom')
t.equal(error.output.statusCode, 429, 'Status Code is correct')
t.equal(error.output.payload.retryAfter, 10001, 'retryAfter is correct')
t.equal(error.output.headers['retry-after'], 10001, 'retryAfter header is correct')
})
}
)
test(
'can scrub customs request object',
function (t) {
t.plan(3)
return customsWithUrl.check(request, email, action)
.then(function (result) {
assert.equal(result, undefined, 'nothing is returned when /check succeeds - 1')
})
}
)
customsWithUrl = new Customs(CUSTOMS_URL_REAL)
t.ok(customsWithUrl, 'got a customs object with a valid url')
var request = newRequest()
request.payload.authPW = 'asdfasdfadsf'
var ip = request.app.clientAddress
var email = newEmail()
var action = newAction()
customsServer.post('/check', function (body) {
t.deepEqual(body, {
ip: ip,
email: email,
action: action,
headers: request.headers,
query: request.query,
payload: {}
}, 'should not have authPW in payload')
return true
}).reply(200, {
block: false,
retryAfter: 0
})
return customsWithUrl.check(request, email, action)
.then(function (result) {
t.equal(result, undefined, 'nothing is returned when /check succeeds - 1')
}, function (error) {
t.fail('should not have failed here for /check : err=' + error)
})
}
)
})
function newEmail() {
return Math.random().toString().substr(2) + '@example.com'
@ -489,3 +427,4 @@ function newAction() {
return EMAIL_ACTIONS[Math.floor(Math.random() * EMAIL_ACTIONS.length)]
}

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

@ -4,20 +4,20 @@
'use strict'
const assert = require('insist')
var uuid = require('uuid')
var crypto = require('crypto')
var test = require('../ptaptest')
var mocks = require('../mocks')
var modulePath = '../../lib/devices'
test('require', function (t) {
t.plan(3)
t.equal(typeof require(modulePath), 'function', 'require returns function')
t.equal(require(modulePath).length, 3, 'returned function expects three arguments')
describe('devices', () => {
it('should be an exported function', () => {
assert.equal(typeof require(modulePath), 'function', 'require returns function')
assert.equal(require(modulePath).length, 3, 'returned function expects three arguments')
})
t.test('instantiate', function (t) {
t.plan(8)
describe('instance', () => {
var log = mocks.spyLog()
var deviceCreatedAt = Date.now()
var deviceId = crypto.randomBytes(16)
@ -33,17 +33,20 @@ test('require', function (t) {
var push = mocks.mockPush()
var devices = require(modulePath)(log, db, push)
t.equal(typeof devices, 'object', 'devices is object')
t.equal(Object.keys(devices).length, 2, 'devices has two properties')
it('should instantiate', () => {
t.equal(typeof devices.upsert, 'function', 'devices has upsert method')
t.equal(devices.upsert.length, 3, 'devices.upsert expects three arguments')
assert.equal(typeof devices, 'object', 'devices is object')
assert.equal(Object.keys(devices).length, 2, 'devices has two properties')
t.equal(typeof devices.synthesizeName, 'function', 'devices has synthesizeName method')
t.equal(devices.synthesizeName.length, 1, 'devices.synthesizeName expects 1 argument')
assert.equal(typeof devices.upsert, 'function', 'devices has upsert method')
assert.equal(devices.upsert.length, 3, 'devices.upsert expects three arguments')
t.test('devices.upsert', function (t) {
t.plan(3)
assert.equal(typeof devices.synthesizeName, 'function', 'devices has synthesizeName method')
assert.equal(devices.synthesizeName.length, 1, 'devices.synthesizeName expects 1 argument')
})
describe('.upsert', () => {
const request = mocks.mockRequest({
log: log
})
@ -52,44 +55,44 @@ test('require', function (t) {
uid: uuid.v4('binary')
}
t.test('create', function (t) {
it('should create', () => {
return devices.upsert(request, sessionToken, device)
.then(function (result) {
t.deepEqual(result, {
assert.deepEqual(result, {
id: deviceId,
name: device.name,
type: device.type,
createdAt: deviceCreatedAt
}, 'result was correct')
t.equal(db.updateDevice.callCount, 0, 'db.updateDevice was not called')
assert.equal(db.updateDevice.callCount, 0, 'db.updateDevice was not called')
t.equal(db.createDevice.callCount, 1, 'db.createDevice was called once')
assert.equal(db.createDevice.callCount, 1, 'db.createDevice was called once')
var args = db.createDevice.args[0]
t.equal(args.length, 3, 'db.createDevice was passed three arguments')
t.deepEqual(args[0], sessionToken.uid, 'first argument was uid')
t.deepEqual(args[1], sessionToken.tokenId, 'second argument was sessionTokenId')
t.equal(args[2], device, 'third argument was device')
assert.equal(args.length, 3, 'db.createDevice was passed three arguments')
assert.deepEqual(args[0], sessionToken.uid, 'first argument was uid')
assert.deepEqual(args[1], sessionToken.tokenId, 'second argument was sessionTokenId')
assert.equal(args[2], device, 'third argument was device')
t.equal(log.activityEvent.callCount, 1, 'log.activityEvent was called once')
assert.equal(log.activityEvent.callCount, 1, 'log.activityEvent was called once')
args = log.activityEvent.args[0]
t.equal(args.length, 3, 'log.activityEvent was passed three arguments')
t.equal(args[0], 'device.created', 'first argument was event name')
t.equal(args[1], request, 'second argument was request object')
t.deepEqual(args[2], {
assert.equal(args.length, 3, 'log.activityEvent was passed three arguments')
assert.equal(args[0], 'device.created', 'first argument was event name')
assert.equal(args[1], request, 'second argument was request object')
assert.deepEqual(args[2], {
uid: sessionToken.uid.toString('hex'),
device_id: deviceId.toString('hex'),
is_placeholder: false
}, 'third argument contained uid, device_id and is_placeholder')
t.equal(log.info.callCount, 0, 'log.info was not called')
assert.equal(log.info.callCount, 0, 'log.info was not called')
t.equal(log.notifyAttachedServices.callCount, 1, 'log.notifyAttachedServices was called once')
assert.equal(log.notifyAttachedServices.callCount, 1, 'log.notifyAttachedServices was called once')
args = log.notifyAttachedServices.args[0]
t.equal(args.length, 3, 'log.notifyAttachedServices was passed three arguments')
t.equal(args[0], 'device:create', 'first argument was event name')
t.equal(args[1], request, 'second argument was request object')
t.deepEqual(args[2], {
assert.equal(args.length, 3, 'log.notifyAttachedServices was passed three arguments')
assert.equal(args[0], 'device:create', 'first argument was event name')
assert.equal(args[1], request, 'second argument was request object')
assert.deepEqual(args[2], {
uid: sessionToken.uid,
id: deviceId,
type: device.type,
@ -97,12 +100,12 @@ test('require', function (t) {
isPlaceholder: false
}, 'third argument was event data')
t.equal(push.notifyDeviceConnected.callCount, 1, 'push.notifyDeviceConnected was called once')
assert.equal(push.notifyDeviceConnected.callCount, 1, 'push.notifyDeviceConnected was called once')
args = push.notifyDeviceConnected.args[0]
t.equal(args.length, 3, 'push.notifyDeviceConnected was passed three arguments')
t.equal(args[0], sessionToken.uid, 'first argument was uid')
t.equal(args[1], device.name, 'second arguent was device name')
t.equal(args[2], deviceId.toString('hex'), 'third argument was device id')
assert.equal(args.length, 3, 'push.notifyDeviceConnected was passed three arguments')
assert.equal(args[0], sessionToken.uid, 'first argument was uid')
assert.equal(args[1], device.name, 'second arguent was device name')
assert.equal(args[2], deviceId.toString('hex'), 'third argument was device id')
})
.then(function () {
db.createDevice.reset()
@ -112,27 +115,27 @@ test('require', function (t) {
})
})
t.test('create placeholder', function (t) {
it('should create placeholders', () => {
return devices.upsert(request, sessionToken, {})
.then(function (result) {
t.equal(db.updateDevice.callCount, 0, 'db.updateDevice was not called')
t.equal(db.createDevice.callCount, 1, 'db.createDevice was called once')
assert.equal(db.updateDevice.callCount, 0, 'db.updateDevice was not called')
assert.equal(db.createDevice.callCount, 1, 'db.createDevice was called once')
t.equal(log.activityEvent.callCount, 1, 'log.activityEvent was called once')
t.equal(log.activityEvent.args[0][2].is_placeholder, true, 'is_placeholder was correct')
assert.equal(log.activityEvent.callCount, 1, 'log.activityEvent was called once')
assert.equal(log.activityEvent.args[0][2].is_placeholder, true, 'is_placeholder was correct')
t.equal(log.info.callCount, 1, 'log.info was called once')
t.equal(log.info.args[0].length, 1, 'log.info was passed one argument')
t.deepEqual(log.info.args[0][0], {
assert.equal(log.info.callCount, 1, 'log.info was called once')
assert.equal(log.info.args[0].length, 1, 'log.info was passed one argument')
assert.deepEqual(log.info.args[0][0], {
op: 'device:createPlaceholder',
uid: sessionToken.uid,
id: result.id
}, 'argument was event data')
t.equal(log.notifyAttachedServices.callCount, 1, 'log.notifyAttachedServices was called once')
t.equal(log.notifyAttachedServices.args[0][2].isPlaceholder, true, 'isPlaceholder was correct')
assert.equal(log.notifyAttachedServices.callCount, 1, 'log.notifyAttachedServices was called once')
assert.equal(log.notifyAttachedServices.args[0][2].isPlaceholder, true, 'isPlaceholder was correct')
t.equal(push.notifyDeviceConnected.callCount, 1, 'push.notifyDeviceConnected was called once')
assert.equal(push.notifyDeviceConnected.callCount, 1, 'push.notifyDeviceConnected was called once')
})
.then(function () {
db.createDevice.reset()
@ -143,7 +146,7 @@ test('require', function (t) {
})
})
t.test('update', function (t) {
it('should update', () => {
var deviceInfo = {
id: deviceId,
name: device.name,
@ -151,37 +154,37 @@ test('require', function (t) {
}
return devices.upsert(request, sessionToken, deviceInfo)
.then(function (result) {
t.equal(result, deviceInfo, 'result was correct')
assert.equal(result, deviceInfo, 'result was correct')
t.equal(db.createDevice.callCount, 0, 'db.createDevice was not called')
assert.equal(db.createDevice.callCount, 0, 'db.createDevice was not called')
t.equal(db.updateDevice.callCount, 1, 'db.updateDevice was called once')
assert.equal(db.updateDevice.callCount, 1, 'db.updateDevice was called once')
var args = db.updateDevice.args[0]
t.equal(args.length, 3, 'db.createDevice was passed three arguments')
t.deepEqual(args[0], sessionToken.uid, 'first argument was uid')
t.deepEqual(args[1], sessionToken.tokenId, 'second argument was sessionTokenId')
t.deepEqual(args[2], {
assert.equal(args.length, 3, 'db.createDevice was passed three arguments')
assert.deepEqual(args[0], sessionToken.uid, 'first argument was uid')
assert.deepEqual(args[1], sessionToken.tokenId, 'second argument was sessionTokenId')
assert.deepEqual(args[2], {
id: deviceId,
name: device.name,
type: device.type
}, 'device info was unmodified')
t.equal(log.activityEvent.callCount, 1, 'log.activityEvent was called once')
assert.equal(log.activityEvent.callCount, 1, 'log.activityEvent was called once')
args = log.activityEvent.args[0]
t.equal(args.length, 3, 'log.activityEvent was passed three arguments')
t.equal(args[0], 'device.updated', 'first argument was event name')
t.equal(args[1], request, 'second argument was request object')
t.deepEqual(args[2], {
assert.equal(args.length, 3, 'log.activityEvent was passed three arguments')
assert.equal(args[0], 'device.updated', 'first argument was event name')
assert.equal(args[1], request, 'second argument was request object')
assert.deepEqual(args[2], {
uid: sessionToken.uid.toString('hex'),
device_id: deviceId.toString('hex'),
is_placeholder: false
}, 'third argument contained uid and device_id')
t.equal(log.info.callCount, 0, 'log.info was not called')
assert.equal(log.info.callCount, 0, 'log.info was not called')
t.equal(log.notifyAttachedServices.callCount, 0, 'log.notifyAttachedServices was not called')
assert.equal(log.notifyAttachedServices.callCount, 0, 'log.notifyAttachedServices was not called')
t.equal(push.notifyDeviceConnected.callCount, 0, 'push.notifyDeviceConnected was not called')
assert.equal(push.notifyDeviceConnected.callCount, 0, 'push.notifyDeviceConnected was not called')
})
.then(function () {
db.createDevice.reset()
@ -191,53 +194,50 @@ test('require', function (t) {
})
})
t.test('devices.synthesizeName', function (t) {
t.equal(devices.synthesizeName({
it('should synthesizeName', () => {
assert.equal(devices.synthesizeName({
uaBrowser: 'foo',
uaBrowserVersion: 'bar',
uaOS: 'baz',
uaOSVersion: 'qux'
}), 'foo bar, baz qux', 'result is correct when all ua properties are set')
t.equal(devices.synthesizeName({
assert.equal(devices.synthesizeName({
uaBrowserVersion: 'foo',
uaOS: 'bar',
uaOSVersion: 'baz'
}), 'bar baz', 'result is correct when uaBrowser property is missing')
t.equal(devices.synthesizeName({
assert.equal(devices.synthesizeName({
uaBrowser: 'foo',
uaOS: 'bar',
uaOSVersion: 'baz'
}), 'foo, bar baz', 'result is correct when uaBrowserVersion property is missing')
t.equal(devices.synthesizeName({
assert.equal(devices.synthesizeName({
uaBrowser: 'foo',
uaBrowserVersion: 'bar',
uaOSVersion: 'baz'
}), 'foo bar', 'result is correct when uaOS property is missing')
t.equal(devices.synthesizeName({
assert.equal(devices.synthesizeName({
uaBrowser: 'foo',
uaBrowserVersion: 'bar',
uaOS: 'baz'
}), 'foo bar, baz', 'result is correct when uaOSVersion property is missing')
t.equal(devices.synthesizeName({
assert.equal(devices.synthesizeName({
uaBrowser: 'wibble',
uaBrowserVersion: 'blee'
}), 'wibble blee', 'result is correct when both uaOS properties are missing')
t.equal(devices.synthesizeName({
assert.equal(devices.synthesizeName({
uaOS: 'foo'
}), 'foo', 'result is correct when only uaOS property is present')
t.equal(devices.synthesizeName({
assert.equal(devices.synthesizeName({
uaOSVersion: 'foo'
}), '', 'result defaults to the empty string')
t.end()
})
})
})

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

@ -2,97 +2,95 @@
* 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')
const assert = require('insist')
var messages = require('joi/lib/language')
var AppError = require('../../lib/error')
test(
'tightly-coupled joi message hack is okay',
function (t) {
t.equal(typeof messages.errors.any.required, 'string')
t.not(messages.errors.any.required, '')
t.end()
}
)
describe('AppErrors', () => {
test(
'exported functions exist',
function (t) {
t.equal(typeof AppError, 'function')
t.equal(AppError.length, 3)
t.equal(typeof AppError.translate, 'function')
t.equal(AppError.translate.length, 1)
t.equal(typeof AppError.invalidRequestParameter, 'function')
t.equal(AppError.invalidRequestParameter.length, 1)
t.equal(typeof AppError.missingRequestParameter, 'function')
t.equal(AppError.missingRequestParameter.length, 1)
t.end()
}
)
it(
'tightly-coupled joi message hack is okay',
() => {
assert.equal(typeof messages.errors.any.required, 'string')
assert.notEqual(messages.errors.any.required, '')
}
)
test(
'error.translate with missing required parameters',
function (t) {
var result = AppError.translate({
output: {
payload: {
message: 'foo' + messages.errors.any.required,
validation: {
keys: [ 'bar', 'baz' ]
it(
'exported functions exist',
() => {
assert.equal(typeof AppError, 'function')
assert.equal(AppError.length, 3)
assert.equal(typeof AppError.translate, 'function')
assert.equal(AppError.translate.length, 1)
assert.equal(typeof AppError.invalidRequestParameter, 'function')
assert.equal(AppError.invalidRequestParameter.length, 1)
assert.equal(typeof AppError.missingRequestParameter, 'function')
assert.equal(AppError.missingRequestParameter.length, 1)
}
)
it(
'should translate with missing required parameters',
() => {
var result = AppError.translate({
output: {
payload: {
message: 'foo' + messages.errors.any.required,
validation: {
keys: [ 'bar', 'baz' ]
}
}
}
}
})
t.ok(result instanceof AppError, 'instanceof AppError')
t.equal(result.errno, 108)
t.equal(result.message, 'Missing parameter in request body: bar')
t.equal(result.output.statusCode, 400)
t.equal(result.output.payload.error, 'Bad Request')
t.equal(result.output.payload.errno, result.errno)
t.equal(result.output.payload.message, result.message)
t.equal(result.output.payload.param, 'bar')
t.end()
}
)
})
assert.ok(result instanceof AppError, 'instanceof AppError')
assert.equal(result.errno, 108)
assert.equal(result.message, 'Missing parameter in request body: bar')
assert.equal(result.output.statusCode, 400)
assert.equal(result.output.payload.error, 'Bad Request')
assert.equal(result.output.payload.errno, result.errno)
assert.equal(result.output.payload.message, result.message)
assert.equal(result.output.payload.param, 'bar')
}
)
test(
'error.translate with invalid parameter',
function (t) {
var result = AppError.translate({
output: {
payload: {
validation: 'foo'
it(
'should translate with invalid parameter',
() => {
var result = AppError.translate({
output: {
payload: {
validation: 'foo'
}
}
}
})
t.ok(result instanceof AppError, 'instanceof AppError')
t.equal(result.errno, 107)
t.equal(result.message, 'Invalid parameter in request body')
t.equal(result.output.statusCode, 400)
t.equal(result.output.payload.error, 'Bad Request')
t.equal(result.output.payload.errno, result.errno)
t.equal(result.output.payload.message, result.message)
t.equal(result.output.payload.validation, 'foo')
t.end()
}
)
})
assert.ok(result instanceof AppError, 'instanceof AppError')
assert.equal(result.errno, 107)
assert.equal(result.message, 'Invalid parameter in request body')
assert.equal(result.output.statusCode, 400)
assert.equal(result.output.payload.error, 'Bad Request')
assert.equal(result.output.payload.errno, result.errno)
assert.equal(result.output.payload.message, result.message)
assert.equal(result.output.payload.validation, 'foo')
}
)
test(
'tooManyRequests',
function (t) {
var result = AppError.tooManyRequests(900, 'in 15 minutes')
t.ok(result instanceof AppError, 'instanceof AppError')
t.equal(result.errno, 114)
t.equal(result.message, 'Client has sent too many requests')
t.equal(result.output.statusCode, 429)
t.equal(result.output.payload.error, 'Too Many Requests')
t.equal(result.output.payload.retryAfter, 900)
t.equal(result.output.payload.retryAfterLocalized, 'in 15 minutes')
it(
'tooManyRequests',
() => {
var result = AppError.tooManyRequests(900, 'in 15 minutes')
assert.ok(result instanceof AppError, 'instanceof AppError')
assert.equal(result.errno, 114)
assert.equal(result.message, 'Client has sent too many requests')
assert.equal(result.output.statusCode, 429)
assert.equal(result.output.payload.error, 'Too Many Requests')
assert.equal(result.output.payload.retryAfter, 900)
assert.equal(result.output.payload.retryAfterLocalized, 'in 15 minutes')
result = AppError.tooManyRequests(900)
t.equal(result.output.payload.retryAfter, 900)
t.notOk(result.output.payload.retryAfterLocalized)
result = AppError.tooManyRequests(900)
assert.equal(result.output.payload.retryAfter, 900)
assert(!result.output.payload.retryAfterLocalized)
t.end()
}
)
}
)
})

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

@ -4,7 +4,7 @@
'use strict'
const test = require('../ptaptest')
const assert = require('insist')
const sinon = require('sinon')
const proxyquire = require('proxyquire')
@ -27,255 +27,246 @@ const features = proxyquire('../../lib/features', {
crypto: crypto
})(config)
test(
'interface is correct',
t => {
t.equal(typeof features, 'object', 'object type should be exported')
t.equal(Object.keys(features).length, 4, 'object should have four properties')
t.equal(typeof features.isSampledUser, 'function', 'isSampledUser should be function')
t.equal(typeof features.isLastAccessTimeEnabledForUser, 'function', 'isLastAccessTimeEnabledForUser should be function')
t.equal(typeof features.isSigninConfirmationEnabledForUser, 'function', 'isSigninConfirmationEnabledForUser should be function')
t.equal(typeof features.isSigninUnblockEnabledForUser, 'function', 'isSigninUnblockEnabledForUser should be function')
describe('features', () => {
it(
'interface is correct',
() => {
assert.equal(typeof features, 'object', 'object type should be exported')
assert.equal(Object.keys(features).length, 4, 'object should have four properties')
assert.equal(typeof features.isSampledUser, 'function', 'isSampledUser should be function')
assert.equal(typeof features.isLastAccessTimeEnabledForUser, 'function', 'isLastAccessTimeEnabledForUser should be function')
assert.equal(typeof features.isSigninConfirmationEnabledForUser, 'function', 'isSigninConfirmationEnabledForUser should be function')
assert.equal(typeof features.isSigninUnblockEnabledForUser, 'function', 'isSigninUnblockEnabledForUser should be function')
t.equal(crypto.createHash.callCount, 1, 'crypto.createHash should have been called once on require')
let args = crypto.createHash.args[0]
t.equal(args.length, 1, 'crypto.createHash should have been passed one argument')
t.equal(args[0], 'sha1', 'crypto.createHash algorithm should have been sha1')
assert.equal(crypto.createHash.callCount, 1, 'crypto.createHash should have been called once on require')
let args = crypto.createHash.args[0]
assert.equal(args.length, 1, 'crypto.createHash should have been passed one argument')
assert.equal(args[0], 'sha1', 'crypto.createHash algorithm should have been sha1')
t.equal(hash.update.callCount, 2, 'hash.update should have been called twice on require')
args = hash.update.args[0]
t.equal(args.length, 1, 'hash.update should have been passed one argument first time')
t.equal(typeof args[0], 'string', 'hash.update data should have been a string first time')
args = hash.update.args[1]
t.equal(args.length, 1, 'hash.update should have been passed one argument second time')
t.equal(typeof args[0], 'string', 'hash.update data should have been a string second time')
assert.equal(hash.update.callCount, 2, 'hash.update should have been called twice on require')
args = hash.update.args[0]
assert.equal(args.length, 1, 'hash.update should have been passed one argument first time')
assert.equal(typeof args[0], 'string', 'hash.update data should have been a string first time')
args = hash.update.args[1]
assert.equal(args.length, 1, 'hash.update should have been passed one argument second time')
assert.equal(typeof args[0], 'string', 'hash.update data should have been a string second time')
t.equal(hash.digest.callCount, 1, 'hash.digest should have been called once on require')
args = hash.digest.args[0]
t.equal(args.length, 1, 'hash.digest should have been passed one argument')
t.equal(args[0], 'hex', 'hash.digest ecnoding should have been hex')
assert.equal(hash.digest.callCount, 1, 'hash.digest should have been called once on require')
args = hash.digest.args[0]
assert.equal(args.length, 1, 'hash.digest should have been passed one argument')
assert.equal(args[0], 'hex', 'hash.digest ecnoding should have been hex')
crypto.createHash.reset()
hash.update.reset()
hash.digest.reset()
crypto.createHash.reset()
hash.update.reset()
hash.digest.reset()
}
)
t.end()
}
)
it(
'isSampledUser',
() => {
let uid = Buffer.alloc(32, 0xff)
let sampleRate = 1
hashResult = Array(40).fill('f').join('')
test(
'isSampledUser',
t => {
let uid = Buffer.alloc(32, 0xff)
let sampleRate = 1
hashResult = Array(40).fill('f').join('')
assert.equal(features.isSampledUser(sampleRate, uid, 'foo'), true, 'should always return true if sample rate is 1')
t.equal(features.isSampledUser(sampleRate, uid, 'foo'), true, 'should always return true if sample rate is 1')
assert.equal(crypto.createHash.callCount, 0, 'crypto.createHash should not have been called')
assert.equal(hash.update.callCount, 0, 'hash.update should not have been called')
assert.equal(hash.digest.callCount, 0, 'hash.digest should not have been called')
t.equal(crypto.createHash.callCount, 0, 'crypto.createHash should not have been called')
t.equal(hash.update.callCount, 0, 'hash.update should not have been called')
t.equal(hash.digest.callCount, 0, 'hash.digest should not have been called')
sampleRate = 0
hashResult = Array(40).fill('0').join('')
sampleRate = 0
hashResult = Array(40).fill('0').join('')
assert.equal(features.isSampledUser(sampleRate, uid, 'foo'), false, 'should always return false if sample rate is 0')
t.equal(features.isSampledUser(sampleRate, uid, 'foo'), false, 'should always return false if sample rate is 0')
assert.equal(crypto.createHash.callCount, 0, 'crypto.createHash should not have been called')
assert.equal(hash.update.callCount, 0, 'hash.update should not have been called')
assert.equal(hash.digest.callCount, 0, 'hash.digest should not have been called')
t.equal(crypto.createHash.callCount, 0, 'crypto.createHash should not have been called')
t.equal(hash.update.callCount, 0, 'hash.update should not have been called')
t.equal(hash.digest.callCount, 0, 'hash.digest should not have been called')
sampleRate = 0.05
// First 27 characters are ignored, last 13 are 0.04 * 0xfffffffffffff
hashResult = '0000000000000000000000000000a3d70a3d70a6'
sampleRate = 0.05
// First 27 characters are ignored, last 13 are 0.04 * 0xfffffffffffff
hashResult = '0000000000000000000000000000a3d70a3d70a6'
assert.equal(features.isSampledUser(sampleRate, uid, 'foo'), true, 'should return true if sample rate is greater than the extracted cohort value')
t.equal(features.isSampledUser(sampleRate, uid, 'foo'), true, 'should return true if sample rate is greater than the extracted cohort value')
assert.equal(crypto.createHash.callCount, 1, 'crypto.createHash should have been called once')
let args = crypto.createHash.args[0]
assert.equal(args.length, 1, 'crypto.createHash should have been passed one argument')
assert.equal(args[0], 'sha1', 'crypto.createHash algorithm should have been sha1')
t.equal(crypto.createHash.callCount, 1, 'crypto.createHash should have been called once')
let args = crypto.createHash.args[0]
t.equal(args.length, 1, 'crypto.createHash should have been passed one argument')
t.equal(args[0], 'sha1', 'crypto.createHash algorithm should have been sha1')
assert.equal(hash.update.callCount, 2, 'hash.update should have been called twice')
args = hash.update.args[0]
assert.equal(args.length, 1, 'hash.update should have been passed one argument first time')
assert.equal(args[0], uid.toString('hex'), 'hash.update data should have been stringified uid first time')
args = hash.update.args[1]
assert.equal(args.length, 1, 'hash.update should have been passed one argument second time')
assert.equal(args[0], 'foo', 'hash.update data should have been key second time')
t.equal(hash.update.callCount, 2, 'hash.update should have been called twice')
args = hash.update.args[0]
t.equal(args.length, 1, 'hash.update should have been passed one argument first time')
t.equal(args[0], uid.toString('hex'), 'hash.update data should have been stringified uid first time')
args = hash.update.args[1]
t.equal(args.length, 1, 'hash.update should have been passed one argument second time')
t.equal(args[0], 'foo', 'hash.update data should have been key second time')
assert.equal(hash.digest.callCount, 1, 'hash.digest should have been called once')
args = hash.digest.args[0]
assert.equal(args.length, 1, 'hash.digest should have been passed one argument')
assert.equal(args[0], 'hex', 'hash.digest ecnoding should have been hex')
t.equal(hash.digest.callCount, 1, 'hash.digest should have been called once')
args = hash.digest.args[0]
t.equal(args.length, 1, 'hash.digest should have been passed one argument')
t.equal(args[0], 'hex', 'hash.digest ecnoding should have been hex')
crypto.createHash.reset()
hash.update.reset()
hash.digest.reset()
crypto.createHash.reset()
hash.update.reset()
hash.digest.reset()
sampleRate = 0.04
sampleRate = 0.04
assert.equal(features.isSampledUser(sampleRate, uid, 'bar'), false, 'should return false if sample rate is equal to the extracted cohort value')
t.equal(features.isSampledUser(sampleRate, uid, 'bar'), false, 'should return false if sample rate is equal to the extracted cohort value')
assert.equal(crypto.createHash.callCount, 1, 'crypto.createHash should have been called once')
assert.equal(hash.update.callCount, 2, 'hash.update should have been called twice')
assert.equal(hash.update.args[0][0], uid.toString('hex'), 'hash.update data should have been stringified uid first time')
assert.equal(hash.update.args[1][0], 'bar', 'hash.update data should have been key second time')
assert.equal(hash.digest.callCount, 1, 'hash.digest should have been called once')
t.equal(crypto.createHash.callCount, 1, 'crypto.createHash should have been called once')
t.equal(hash.update.callCount, 2, 'hash.update should have been called twice')
t.equal(hash.update.args[0][0], uid.toString('hex'), 'hash.update data should have been stringified uid first time')
t.equal(hash.update.args[1][0], 'bar', 'hash.update data should have been key second time')
t.equal(hash.digest.callCount, 1, 'hash.digest should have been called once')
crypto.createHash.reset()
hash.update.reset()
hash.digest.reset()
crypto.createHash.reset()
hash.update.reset()
hash.digest.reset()
sampleRate = 0.03
sampleRate = 0.03
assert.equal(features.isSampledUser(sampleRate, uid, 'foo'), false, 'should return false if sample rate is less than the extracted cohort value')
t.equal(features.isSampledUser(sampleRate, uid, 'foo'), false, 'should return false if sample rate is less than the extracted cohort value')
crypto.createHash.reset()
hash.update.reset()
hash.digest.reset()
crypto.createHash.reset()
hash.update.reset()
hash.digest.reset()
uid = Array(64).fill('7').join('')
sampleRate = 0.03
// First 27 characters are ignored, last 13 are 0.02 * 0xfffffffffffff
hashResult = '000000000000000000000000000051eb851eb852'
uid = Array(64).fill('7').join('')
sampleRate = 0.03
// First 27 characters are ignored, last 13 are 0.02 * 0xfffffffffffff
hashResult = '000000000000000000000000000051eb851eb852'
assert.equal(features.isSampledUser(sampleRate, uid, 'wibble'), true, 'should return true if sample rate is greater than the extracted cohort value')
t.equal(features.isSampledUser(sampleRate, uid, 'wibble'), true, 'should return true if sample rate is greater than the extracted cohort value')
assert.equal(hash.update.callCount, 2, 'hash.update should have been called twice')
assert.equal(hash.update.args[0][0], uid, 'hash.update data should have been stringified uid first time')
assert.equal(hash.update.args[1][0], 'wibble', 'hash.update data should have been key second time')
t.equal(hash.update.callCount, 2, 'hash.update should have been called twice')
t.equal(hash.update.args[0][0], uid, 'hash.update data should have been stringified uid first time')
t.equal(hash.update.args[1][0], 'wibble', 'hash.update data should have been key second time')
crypto.createHash.reset()
hash.update.reset()
hash.digest.reset()
}
)
crypto.createHash.reset()
hash.update.reset()
hash.digest.reset()
it(
'isLastAccessTimeEnabledForUser',
() => {
const uid = 'foo'
const email = 'bar@mozilla.com'
// First 27 characters are ignored, last 13 are 0.02 * 0xfffffffffffff
hashResult = '000000000000000000000000000051eb851eb852'
t.end()
}
)
config.lastAccessTimeUpdates.enabled = true
config.lastAccessTimeUpdates.sampleRate = 0
config.lastAccessTimeUpdates.enabledEmailAddresses = /.+@mozilla\.com$/
assert.equal(features.isLastAccessTimeEnabledForUser(uid, email), true, 'should return true when email address matches')
test(
'isLastAccessTimeEnabledForUser',
t => {
const uid = 'foo'
const email = 'bar@mozilla.com'
// First 27 characters are ignored, last 13 are 0.02 * 0xfffffffffffff
hashResult = '000000000000000000000000000051eb851eb852'
config.lastAccessTimeUpdates.enabledEmailAddresses = /.+@mozilla\.org$/
assert.equal(features.isLastAccessTimeEnabledForUser(uid, email), false, 'should return false when email address does not match')
config.lastAccessTimeUpdates.enabled = true
config.lastAccessTimeUpdates.sampleRate = 0
config.lastAccessTimeUpdates.enabledEmailAddresses = /.+@mozilla\.com$/
t.equal(features.isLastAccessTimeEnabledForUser(uid, email), true, 'should return true when email address matches')
config.lastAccessTimeUpdates.sampleRate = 0.03
assert.equal(features.isLastAccessTimeEnabledForUser(uid, email), true, 'should return true when sample rate matches')
config.lastAccessTimeUpdates.enabledEmailAddresses = /.+@mozilla\.org$/
t.equal(features.isLastAccessTimeEnabledForUser(uid, email), false, 'should return false when email address does not match')
config.lastAccessTimeUpdates.sampleRate = 0.02
assert.equal(features.isLastAccessTimeEnabledForUser(uid, email), false, 'should return false when sample rate does not match')
config.lastAccessTimeUpdates.sampleRate = 0.03
t.equal(features.isLastAccessTimeEnabledForUser(uid, email), true, 'should return true when sample rate matches')
config.lastAccessTimeUpdates.enabled = false
config.lastAccessTimeUpdates.sampleRate = 0.03
config.lastAccessTimeUpdates.enabledEmailAddresses = /.+@mozilla\.com$/
assert.equal(features.isLastAccessTimeEnabledForUser(uid, email), false, 'should return false when feature is disabled')
}
)
config.lastAccessTimeUpdates.sampleRate = 0.02
t.equal(features.isLastAccessTimeEnabledForUser(uid, email), false, 'should return false when sample rate does not match')
config.lastAccessTimeUpdates.enabled = false
config.lastAccessTimeUpdates.sampleRate = 0.03
config.lastAccessTimeUpdates.enabledEmailAddresses = /.+@mozilla\.com$/
t.equal(features.isLastAccessTimeEnabledForUser(uid, email), false, 'should return false when feature is disabled')
t.end()
}
)
test(
'isSigninConfirmationEnabledForUser',
t => {
const uid = 'wibble'
const email = 'blee@mozilla.com'
const request = {
app: {
isSuspiciousRequest: true
},
payload: {
metricsContext: {
context: 'iframe'
it(
'isSigninConfirmationEnabledForUser',
() => {
const uid = 'wibble'
const email = 'blee@mozilla.com'
const request = {
app: {
isSuspiciousRequest: true
},
payload: {
metricsContext: {
context: 'iframe'
}
}
}
// First 27 characters are ignored, last 13 are 0.02 * 0xfffffffffffff
hashResult = '000000000000000000000000000051eb851eb852'
config.signinConfirmation.enabled = true
config.signinConfirmation.sample_rate = 0.03
config.signinConfirmation.enabledEmailAddresses = /.+@mozilla\.com$/
config.signinConfirmation.supportedClients = [ 'wibble', 'iframe' ]
assert.equal(features.isSigninConfirmationEnabledForUser(uid, email, request), true, 'should return true when request is suspicious')
config.signinConfirmation.sample_rate = 0.02
request.app.isSuspiciousRequest = false
assert.equal(features.isSigninConfirmationEnabledForUser(uid, email, request), true, 'should return true when email address matches')
config.signinConfirmation.enabledEmailAddresses = /.+@mozilla\.org$/
request.payload.metricsContext.context = 'iframe'
assert.equal(features.isSigninConfirmationEnabledForUser(uid, email, request), false, 'should return false when email address and sample rate do not match')
config.signinConfirmation.sample_rate = 0.03
assert.equal(features.isSigninConfirmationEnabledForUser(uid, email, request), true, 'should return true when sample rate and context match')
request.payload.metricsContext.context = ''
assert.equal(features.isSigninConfirmationEnabledForUser(uid, email, request), false, 'should return false when context does not match')
config.signinConfirmation.enabled = false
config.signinConfirmation.forceEmailRegex = /.+@mozilla\.com$/
request.payload.metricsContext.context = 'iframe'
assert.equal(features.isSigninConfirmationEnabledForUser(uid, email, request), false, 'should return false when feature is disabled')
}
// First 27 characters are ignored, last 13 are 0.02 * 0xfffffffffffff
hashResult = '000000000000000000000000000051eb851eb852'
)
config.signinConfirmation.enabled = true
config.signinConfirmation.sample_rate = 0.03
config.signinConfirmation.enabledEmailAddresses = /.+@mozilla\.com$/
config.signinConfirmation.supportedClients = [ 'wibble', 'iframe' ]
t.equal(features.isSigninConfirmationEnabledForUser(uid, email, request), true, 'should return true when request is suspicious')
config.signinConfirmation.sample_rate = 0.02
request.app.isSuspiciousRequest = false
t.equal(features.isSigninConfirmationEnabledForUser(uid, email, request), true, 'should return true when email address matches')
config.signinConfirmation.enabledEmailAddresses = /.+@mozilla\.org$/
request.payload.metricsContext.context = 'iframe'
t.equal(features.isSigninConfirmationEnabledForUser(uid, email, request), false, 'should return false when email address and sample rate do not match')
config.signinConfirmation.sample_rate = 0.03
t.equal(features.isSigninConfirmationEnabledForUser(uid, email, request), true, 'should return true when sample rate and context match')
request.payload.metricsContext.context = ''
t.equal(features.isSigninConfirmationEnabledForUser(uid, email, request), false, 'should return false when context does not match')
config.signinConfirmation.enabled = false
config.signinConfirmation.forceEmailRegex = /.+@mozilla\.com$/
request.payload.metricsContext.context = 'iframe'
t.equal(features.isSigninConfirmationEnabledForUser(uid, email, request), false, 'should return false when feature is disabled')
t.end()
}
)
test(
'isSigninUnblockEnabledForUser',
t => {
const uid = 'wibble'
const email = 'blee@mozilla.com'
const request = {
payload: {
metricsContext: {
context: 'iframe'
it(
'isSigninUnblockEnabledForUser',
() => {
const uid = 'wibble'
const email = 'blee@mozilla.com'
const request = {
payload: {
metricsContext: {
context: 'iframe'
}
}
}
// First 27 characters are ignored, last 13 are 0.02 * 0xfffffffffffff
hashResult = '000000000000000000000000000051eb851eb852'
const unblock = config.signinUnblock
unblock.enabled = true
unblock.sampleRate = 0.02
unblock.allowedEmailAddresses = /.+@notmozilla.com$/
unblock.supportedClients = [ 'wibble', 'iframe' ]
assert.equal(features.isSigninUnblockEnabledForUser(uid, email, request), false, 'should return false when email is not allowed and uid is not sampled')
unblock.forcedEmailAddresses = /.+/
assert.equal(features.isSigninUnblockEnabledForUser(uid, email, request), true, 'should return true when forced on')
unblock.forcedEmailAddresses = /^$/
unblock.allowedEmailAddresses = /.+@mozilla.com$/
assert.equal(features.isSigninUnblockEnabledForUser(uid, email, request), true, 'should return true when email is allowed')
unblock.allowedEmailAddresses = /.+@notmozilla.com$/
unblock.sampleRate = 0.03
assert.equal(features.isSigninUnblockEnabledForUser(uid, email, request), true, 'should return when uid is sampled')
request.payload.metricsContext.context = ''
assert.equal(features.isSigninUnblockEnabledForUser(uid, email, request), false, 'should return false when context is not supported')
request.payload.metricsContext.context = 'iframe'
unblock.enabled = false
assert.equal(features.isSigninUnblockEnabledForUser(uid, email, request), false, 'should return false when feature is disabled')
}
// First 27 characters are ignored, last 13 are 0.02 * 0xfffffffffffff
hashResult = '000000000000000000000000000051eb851eb852'
const unblock = config.signinUnblock
unblock.enabled = true
unblock.sampleRate = 0.02
unblock.allowedEmailAddresses = /.+@notmozilla.com$/
unblock.supportedClients = [ 'wibble', 'iframe' ]
t.equal(features.isSigninUnblockEnabledForUser(uid, email, request), false, 'should return false when email is not allowed and uid is not sampled')
unblock.forcedEmailAddresses = /.+/
t.equal(features.isSigninUnblockEnabledForUser(uid, email, request), true, 'should return true when forced on')
unblock.forcedEmailAddresses = /^$/
unblock.allowedEmailAddresses = /.+@mozilla.com$/
t.equal(features.isSigninUnblockEnabledForUser(uid, email, request), true, 'should return true when email is allowed')
unblock.allowedEmailAddresses = /.+@notmozilla.com$/
unblock.sampleRate = 0.03
t.equal(features.isSigninUnblockEnabledForUser(uid, email, request), true, 'should return when uid is sampled')
request.payload.metricsContext.context = ''
t.equal(features.isSigninUnblockEnabledForUser(uid, email, request), false, 'should return false when context is not supported')
request.payload.metricsContext.context = 'iframe'
unblock.enabled = false
t.equal(features.isSigninUnblockEnabledForUser(uid, email, request), false, 'should return false when feature is disabled')
t.end()
}
)
)
})

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

@ -2,12 +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/. */
var test = require('../ptaptest')
var log = { trace: function() {} }
'use strict'
var timestamp = Date.now()
const assert = require('insist')
const log = { trace() {} }
var PasswordForgotToken = require('../../lib/tokens/password_forgot_token')(
const timestamp = Date.now()
const PasswordForgotToken = require('../../lib/tokens/password_forgot_token')(
log,
require('util').inherits,
require('../../lib/tokens')(log),
@ -16,70 +18,72 @@ var PasswordForgotToken = require('../../lib/tokens/password_forgot_token')(
)
var ACCOUNT = {
const ACCOUNT = {
uid: 'xxx',
email: Buffer('test@example.com').toString('hex')
}
describe('PasswordForgotToken', () => {
test(
're-creation from tokenData works',
function (t) {
var token = null
return PasswordForgotToken.create(ACCOUNT)
.then(
function (x) {
token = x
}
)
.then(
function () {
return PasswordForgotToken.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)
t.deepEqual(token.email, token2.email)
}
)
}
)
it(
'can re-create from tokenData',
() => {
var token = null
return PasswordForgotToken.create(ACCOUNT)
.then(
function (x) {
token = x
}
)
.then(
function () {
return PasswordForgotToken.fromHex(token.data, ACCOUNT)
}
)
.then(
function (token2) {
assert.deepEqual(token.data, token2.data)
assert.deepEqual(token.id, token2.id)
assert.deepEqual(token.authKey, token2.authKey)
assert.deepEqual(token.bundleKey, token2.bundleKey)
assert.deepEqual(token.uid, token2.uid)
assert.deepEqual(token.email, token2.email)
}
)
}
)
test(
'ttl "works"',
function (t) {
return PasswordForgotToken.create(ACCOUNT)
.then(
function (token) {
token.createdAt = timestamp
t.equal(token.ttl(timestamp), 900)
t.equal(token.ttl(timestamp + 1000), 899)
t.equal(token.ttl(timestamp + 2000), 898)
}
)
}
)
it(
'ttl "works"',
() => {
return PasswordForgotToken.create(ACCOUNT)
.then(
function (token) {
token.createdAt = timestamp
assert.equal(token.ttl(timestamp), 900)
assert.equal(token.ttl(timestamp + 1000), 899)
assert.equal(token.ttl(timestamp + 2000), 898)
}
)
}
)
test(
'failAttempt decrements `tries`',
function (t) {
return PasswordForgotToken.create(ACCOUNT)
.then(
function (x) {
t.equal(x.tries, 3)
t.equal(x.failAttempt(), false)
t.equal(x.tries, 2)
t.equal(x.failAttempt(), false)
t.equal(x.tries, 1)
t.equal(x.failAttempt(), true)
}
)
}
)
it(
'failAttempt decrements `tries`',
() => {
return PasswordForgotToken.create(ACCOUNT)
.then(
function (x) {
assert.equal(x.tries, 3)
assert.equal(x.failAttempt(), false)
assert.equal(x.tries, 2)
assert.equal(x.failAttempt(), false)
assert.equal(x.tries, 1)
assert.equal(x.failAttempt(), true)
}
)
}
)
})

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

@ -2,61 +2,62 @@
* 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 tap = require('tap')
var proxyquire = require('proxyquire')
var test = tap.test
var mockLog = require('../mocks').mockLog
'use strict'
test(
'returns location data when enabled',
function (t) {
var moduleMocks = {
'../config': {
'get': function (item) {
if (item === 'geodb') {
return {
enabled: true
const assert = require('insist')
const proxyquire = require('proxyquire')
const mockLog = require('../mocks').mockLog
describe('geodb', () => {
it(
'returns location data when enabled',
() => {
const moduleMocks = {
'../config': {
'get': function (item) {
if (item === 'geodb') {
return {
enabled: true
}
}
}
}
}
const thisMockLog = mockLog({})
const getGeoData = proxyquire('../../lib/geodb', moduleMocks)(thisMockLog)
return getGeoData('63.245.221.32') // MTV
.then(function (geoData) {
assert.equal(geoData.location.city, 'Mountain View')
assert.equal(geoData.location.country, 'United States')
assert.equal(geoData.timeZone, 'America/Los_Angeles')
assert.equal(geoData.location.state, 'California')
assert.equal(geoData.location.stateCode, 'CA')
})
}
var thisMockLog = mockLog({})
)
var getGeoData = proxyquire('../../lib/geodb', moduleMocks)(thisMockLog)
getGeoData('63.245.221.32') // MTV
.then(function (geoData) {
t.equal(geoData.location.city, 'Mountain View')
t.equal(geoData.location.country, 'United States')
t.equal(geoData.timeZone, 'America/Los_Angeles')
t.equal(geoData.location.state, 'California')
t.equal(geoData.location.stateCode, 'CA')
t.end()
})
}
)
test(
'returns empty object data when disabled',
function (t) {
var moduleMocks = {
'../config': {
'get': function (item) {
if (item === 'geodb') {
return {
enabled: false
it(
'returns empty object data when disabled',
() => {
const moduleMocks = {
'../config': {
'get': function (item) {
if (item === 'geodb') {
return {
enabled: false
}
}
}
}
}
}
var thisMockLog = mockLog({})
const thisMockLog = mockLog({})
var getGeoData = proxyquire('../../lib/geodb', moduleMocks)(thisMockLog)
getGeoData('8.8.8.8')
.then(function (geoData) {
t.deepEqual(geoData, {})
t.end()
})
}
)
const getGeoData = proxyquire('../../lib/geodb', moduleMocks)(thisMockLog)
return getGeoData('8.8.8.8')
.then(function (geoData) {
assert.deepEqual(geoData, {})
})
}
)
})

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

@ -2,50 +2,32 @@
* 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 hkdf = require('../../lib/crypto/hkdf')
'use strict'
test(
'hkdf basic',
function (t) {
var stretchedPw = 'c16d46c31bee242cb31f916e9e38d60b76431d3f5304549cc75ae4bc20c7108c'
stretchedPw = new Buffer (stretchedPw, 'hex')
var info = 'mainKDF'
var salt = new Buffer ('00f000000000000000000000000000000000000000000000000000000000034d', 'hex')
var lengthHkdf = 2 * 32
const assert = require('insist')
const hkdf = require('../../lib/crypto/hkdf')
return hkdf(stretchedPw, info, salt, lengthHkdf)
.then(
function (hkdfResult) {
var hkdfStr = hkdfResult.toString('hex')
describe('hkdf', () => {
t.equal(hkdfStr.substring(0, 64), '00f9b71800ab5337d51177d8fbc682a3653fa6dae5b87628eeec43a18af59a9d')
t.equal(hkdfStr.substring(64, 128), '6ea660be9c89ec355397f89afb282ea0bf21095760c8c5009bbcc894155bbe2a')
return hkdfResult
}
)
}
)
it(
'should extract',
() => {
let stretchedPw = 'c16d46c31bee242cb31f916e9e38d60b76431d3f5304549cc75ae4bc20c7108c'
stretchedPw = new Buffer (stretchedPw, 'hex')
const info = 'mainKDF'
const salt = new Buffer ('00f000000000000000000000000000000000000000000000000000000000034d', 'hex')
const lengthHkdf = 2 * 32
test(
'hkdf basic with salt',
function (t) {
var stretchedPw = 'c16d46c31bee242cb31f916e9e38d60b76431d3f5304549cc75ae4bc20c7108c'
stretchedPw = new Buffer (stretchedPw, 'hex')
var info = 'mainKDF'
var salt = new Buffer ('00f000000000000000000000000000000000000000000000000000000000034d', 'hex')
var lengthHkdf = 2 * 32
return hkdf(stretchedPw, info, salt, lengthHkdf)
.then(
function (hkdfResult) {
const hkdfStr = hkdfResult.toString('hex')
return hkdf(stretchedPw, info, salt, lengthHkdf)
.then(
function (hkdfResult) {
var hkdfStr = hkdfResult.toString('hex')
t.equal(hkdfStr.substring(0, 64), '00f9b71800ab5337d51177d8fbc682a3653fa6dae5b87628eeec43a18af59a9d')
t.equal(hkdfStr.substring(64, 128), '6ea660be9c89ec355397f89afb282ea0bf21095760c8c5009bbcc894155bbe2a')
t.equal(salt.toString('hex'), '00f000000000000000000000000000000000000000000000000000000000034d')
return hkdfResult
}
)
}
)
assert.equal(hkdfStr.substring(0, 64), '00f9b71800ab5337d51177d8fbc682a3653fa6dae5b87628eeec43a18af59a9d')
assert.equal(hkdfStr.substring(64, 128), '6ea660be9c89ec355397f89afb282ea0bf21095760c8c5009bbcc894155bbe2a')
assert.equal(salt.toString('hex'), '00f000000000000000000000000000000000000000000000000000000000034d')
}
)
}
)
})

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

@ -2,168 +2,172 @@
* 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() {} }
'use strict'
var tokens = require('../../lib/tokens')(log)
var KeyFetchToken = tokens.KeyFetchToken
const assert = require('insist')
const crypto = require('crypto')
const log = { trace() {} }
var ACCOUNT = {
const tokens = require('../../lib/tokens')(log)
const KeyFetchToken = tokens.KeyFetchToken
const ACCOUNT = {
uid: 'xxx',
kA: Buffer('0000000000000000000000000000000000000000000000000000000000000000', 'hex'),
wrapKb: Buffer('0000000000000000000000000000000000000000000000000000000000000000', 'hex'),
emailVerified: true
}
describe('KeyFetchToken', () => {
test(
're-creation from tokenData works',
function (t) {
var token = null
return KeyFetchToken.create(ACCOUNT)
.then(
function (x) {
token = x
}
)
.then(
function () {
return KeyFetchToken.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)
t.deepEqual(token.kA, token2.kA)
t.deepEqual(token.wrapKb, token2.wrapKb)
t.equal(token.emailVerified, token2.emailVerified)
}
)
}
)
it(
'should re-create from tokenData',
() => {
var token = null
return KeyFetchToken.create(ACCOUNT)
.then(
function (x) {
token = x
}
)
.then(
function () {
return KeyFetchToken.fromHex(token.data, ACCOUNT)
}
)
.then(
function (token2) {
assert.deepEqual(token.data, token2.data)
assert.deepEqual(token.id, token2.id)
assert.deepEqual(token.authKey, token2.authKey)
assert.deepEqual(token.bundleKey, token2.bundleKey)
assert.deepEqual(token.uid, token2.uid)
assert.deepEqual(token.kA, token2.kA)
assert.deepEqual(token.wrapKb, token2.wrapKb)
assert.equal(token.emailVerified, token2.emailVerified)
}
)
}
)
test(
're-creation from id works',
function (t) {
var token = null
return KeyFetchToken.create(ACCOUNT)
.then(
function (x) {
token = x
return KeyFetchToken.fromId(token.tokenId, token)
}
)
.then(
function (x) {
t.equal(x.tokenId, token.tokenId, 'should have same id')
t.equal(x.authKey, token.authKey, 'should have same authKey')
}
)
}
)
it(
'should re-create from id',
() => {
let token = null
return KeyFetchToken.create(ACCOUNT)
.then(
function (x) {
token = x
return KeyFetchToken.fromId(token.tokenId, token)
}
)
.then(
function (x) {
assert.equal(x.tokenId, token.tokenId, 'should have same id')
assert.equal(x.authKey, token.authKey, 'should have same authKey')
}
)
}
)
test(
'bundle / unbundle of keys works',
function (t) {
var token = null
var kA = crypto.randomBytes(32)
var wrapKb = crypto.randomBytes(32)
return KeyFetchToken.create(ACCOUNT)
.then(
function (x) {
token = x
return x.bundleKeys(kA, wrapKb)
}
)
.then(
function (b) {
return token.unbundleKeys(b)
}
)
.then(
function (ub) {
t.deepEqual(ub.kA, kA)
t.deepEqual(ub.wrapKb, wrapKb)
}
)
}
)
it(
'should bundle / unbundle of keys',
() => {
let token = null
const kA = crypto.randomBytes(32)
const wrapKb = crypto.randomBytes(32)
return KeyFetchToken.create(ACCOUNT)
.then(
function (x) {
token = x
return x.bundleKeys(kA, wrapKb)
}
)
.then(
function (b) {
return token.unbundleKeys(b)
}
)
.then(
function (ub) {
assert.deepEqual(ub.kA, kA)
assert.deepEqual(ub.wrapKb, wrapKb)
}
)
}
)
test(
'bundle / unbundle of keys only works with correct token',
function (t) {
var token1 = null
var token2 = null
var kA = crypto.randomBytes(32)
var wrapKb = crypto.randomBytes(32)
return KeyFetchToken.create(ACCOUNT)
.then(
function (x) {
token1 = x
return KeyFetchToken.create(ACCOUNT)
}
)
.then(
function (x) {
token2 = x
return token1.bundleKeys(kA, wrapKb)
}
)
.then(
function (b) {
return token2.unbundleKeys(b)
}
)
.then(
function (ub) {
t.fail('was able to unbundle using wrong token')
},
function (err) {
t.equal(err.errno, 109, 'expected an invalidSignature error')
}
)
}
)
it(
'should only bundle / unbundle of keys with correct token',
() => {
let token1 = null
let token2 = null
const kA = crypto.randomBytes(32)
const wrapKb = crypto.randomBytes(32)
return KeyFetchToken.create(ACCOUNT)
.then(
function (x) {
token1 = x
return KeyFetchToken.create(ACCOUNT)
}
)
.then(
function (x) {
token2 = x
return token1.bundleKeys(kA, wrapKb)
}
)
.then(
function (b) {
return token2.unbundleKeys(b)
}
)
.then(
function (ub) {
assert(false, 'was able to unbundle using wrong token')
},
function (err) {
assert.equal(err.errno, 109, 'expected an invalidSignature error')
}
)
}
)
test(
'keyFetchToken key derivations are test-vector compliant',
function (t) {
var token = null
var tokenData = '808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f'
return KeyFetchToken.fromHex(tokenData, ACCOUNT)
.then(
function (x) {
token = x
t.equal(token.data.toString('hex'), tokenData)
t.equal(token.id.toString('hex'), '3d0a7c02a15a62a2882f76e39b6494b500c022a8816e048625a495718998ba60')
t.equal(token.authKey.toString('hex'), '87b8937f61d38d0e29cd2d5600b3f4da0aa48ac41de36a0efe84bb4a9872ceb7')
t.equal(token.bundleKey.toString('hex'), '14f338a9e8c6324d9e102d4e6ee83b209796d5c74bb734a410e729e014a4a546')
}
)
.then(
function () {
var kA = Buffer('202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f', 'hex')
var wrapKb = Buffer('404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f', 'hex')
return token.bundleKeys(kA, wrapKb)
}
)
.then(
function (bundle) {
t.equal(bundle,
'ee5c58845c7c9412b11bbd20920c2fddd83c33c9cd2c2de2' +
'd66b222613364636c2c0f8cfbb7c630472c0bd88451342c6' +
'c05b14ce342c5ad46ad89e84464c993c3927d30230157d08' +
'17a077eef4b20d976f7a97363faf3f064c003ada7d01aa70')
}
)
}
)
it(
'should have key derivations that are test-vector compliant',
() => {
let token = null
const tokenData = '808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f'
return KeyFetchToken.fromHex(tokenData, ACCOUNT)
.then(
function (x) {
token = x
assert.equal(token.data.toString('hex'), tokenData)
assert.equal(token.id.toString('hex'), '3d0a7c02a15a62a2882f76e39b6494b500c022a8816e048625a495718998ba60')
assert.equal(token.authKey.toString('hex'), '87b8937f61d38d0e29cd2d5600b3f4da0aa48ac41de36a0efe84bb4a9872ceb7')
assert.equal(token.bundleKey.toString('hex'), '14f338a9e8c6324d9e102d4e6ee83b209796d5c74bb734a410e729e014a4a546')
}
)
.then(
function () {
const kA = Buffer('202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f', 'hex')
const wrapKb = Buffer('404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f', 'hex')
return token.bundleKeys(kA, wrapKb)
}
)
.then(
function (bundle) {
assert.equal(bundle,
'ee5c58845c7c9412b11bbd20920c2fddd83c33c9cd2c2de2' +
'd66b222613364636c2c0f8cfbb7c630472c0bd88451342c6' +
'c05b14ce342c5ad46ad89e84464c993c3927d30230157d08' +
'17a077eef4b20d976f7a97363faf3f064c003ada7d01aa70')
}
)
}
)
})

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -2,82 +2,79 @@
* 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('tap').test
'use strict'
const assert = require('insist')
var config = require('../../config').getProperties()
var log = {}
require('../../lib/mailer')(config, log)
.done(
function(mailer) {
describe('mailer locales', () => {
test(
'All configured supportedLanguages are available',
function (t) {
var locales = config.i18n.supportedLanguages
locales.forEach(function(lang) {
// sr-LATN is sr, but in Latin characters, not Cyrillic
if (lang === 'sr-LATN') {
t.equal('sr-Latn', mailer.translator(lang).language)
} else {
t.equal(lang, mailer.translator(lang).language)
}
})
t.end()
let mailer
before(() => {
return require('../../lib/mailer')(config, log)
.then(m => {
mailer = m
})
})
it(
'All configured supportedLanguages are available',
() => {
var locales = config.i18n.supportedLanguages
locales.forEach(function(lang) {
// sr-LATN is sr, but in Latin characters, not Cyrillic
if (lang === 'sr-LATN') {
assert.equal('sr-Latn', mailer.translator(lang).language)
} else {
assert.equal(lang, mailer.translator(lang).language)
}
)
test(
'unsupported languages get default/fallback content',
function (t) {
// These are locales for which we do not have explicit translations
var locales = [
// [ locale, expected result ]
[ '', 'en' ],
[ 'en-US', 'en' ],
[ 'en-CA', 'en' ],
[ 'db-LB', 'en' ],
[ 'el-GR', 'en' ],
[ 'es-BO', 'es' ],
[ 'fr-FR', 'fr' ],
[ 'fr-CA', 'fr' ],
]
locales.forEach(function(lang) {
t.equal(lang[1], mailer.translator(lang[0]).language)
})
t.end()
}
)
test(
'accept-language handled correctly',
function (t) {
// These are the Accept-Language headers from Firefox 37 L10N builds
var locales = [
// [ accept-language, expected result ]
[ 'bogus-value', 'en' ],
[ 'en-US,en;q=0.5', 'en' ],
[ 'es-AR,es;q=0.8,en-US;q=0.5,en;q=0.3', 'es-AR' ],
[ 'es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3', 'es-ES' ],
[ 'sv-SE,sv;q=0.8,en-US;q=0.5,en;q=0.3', 'sv-SE' ],
[ 'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3', 'zh-CN' ]
]
locales.forEach(function(lang) {
t.equal(lang[1], mailer.translator(lang[0]).language)
})
t.end()
}
)
test(
'teardown',
function (t) {
mailer.stop()
t.end()
}
)
})
}
)
it(
'unsupported languages get default/fallback content',
() => {
// These are locales for which we do not have explicit translations
var locales = [
// [ locale, expected result ]
[ '', 'en' ],
[ 'en-US', 'en' ],
[ 'en-CA', 'en' ],
[ 'db-LB', 'en' ],
[ 'el-GR', 'en' ],
[ 'es-BO', 'es' ],
[ 'fr-FR', 'fr' ],
[ 'fr-CA', 'fr' ],
]
locales.forEach(function(lang) {
assert.equal(lang[1], mailer.translator(lang[0]).language)
})
}
)
it(
'accept-language handled correctly',
() => {
// These are the Accept-Language headers from Firefox 37 L10N builds
var locales = [
// [ accept-language, expected result ]
[ 'bogus-value', 'en' ],
[ 'en-US,en;q=0.5', 'en' ],
[ 'es-AR,es;q=0.8,en-US;q=0.5,en;q=0.3', 'es-AR' ],
[ 'es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3', 'es-ES' ],
[ 'sv-SE,sv;q=0.8,en-US;q=0.5,en;q=0.3', 'sv-SE' ],
[ 'zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3', 'zh-CN' ]
]
locales.forEach(function(lang) {
assert.equal(lang[1], mailer.translator(lang[0]).language)
})
}
)
after(() => mailer.stop())
})

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -4,8 +4,8 @@
'use strict'
const assert = require('insist')
const sinon = require('sinon')
const test = require('../ptaptest')
const P = require('../../lib/promise')
const log = {
activityEvent: sinon.spy(() => {
@ -17,108 +17,108 @@ const log = {
}
const events = require('../../lib/metrics/events')(log)
test('events interface is correct', t => {
t.equal(typeof events, 'object', 'events is object')
t.notEqual(events, null, 'events is not null')
t.equal(Object.keys(events).length, 1, 'events has 1 property')
describe('metrics/events', () => {
t.equal(typeof events.emit, 'function', 'events.emit is function')
t.equal(events.emit.length, 2, 'events.emit expects 2 arguments')
it('interface is correct', () => {
assert.equal(typeof events, 'object', 'events is object')
assert.notEqual(events, null, 'events is not null')
assert.equal(Object.keys(events).length, 1, 'events has 1 property')
t.equal(log.activityEvent.callCount, 0, 'log.activityEvent was not called')
t.equal(log.flowEvent.callCount, 0, 'log.flowEvent was not called')
assert.equal(typeof events.emit, 'function', 'events.emit is function')
assert.equal(events.emit.length, 2, 'events.emit expects 2 arguments')
t.end()
})
assert.equal(log.activityEvent.callCount, 0, 'log.activityEvent was not called')
assert.equal(log.flowEvent.callCount, 0, 'log.flowEvent was not called')
})
test('events.emit with activity event', t => {
const request = {}
const data = {}
return events.emit.call(request, 'device.created', data)
.then(() => {
t.equal(log.activityEvent.callCount, 1, 'log.activityEvent was called once')
const args = log.activityEvent.args[0]
t.equal(args.length, 3, 'log.activityEvent was passed three arguments')
t.equal(args[0], 'device.created', 'first argument was event name')
t.equal(args[1], request, 'second argument was request object')
t.equal(args[2], data, 'third argument was event data')
it('.emit with activity event', () => {
const request = {}
const data = {}
return events.emit.call(request, 'device.created', data)
.then(() => {
assert.equal(log.activityEvent.callCount, 1, 'log.activityEvent was called once')
const args = log.activityEvent.args[0]
assert.equal(args.length, 3, 'log.activityEvent was passed three arguments')
assert.equal(args[0], 'device.created', 'first argument was event name')
assert.equal(args[1], request, 'second argument was request object')
assert.equal(args[2], data, 'third argument was event data')
t.equal(log.flowEvent.callCount, 0, 'log.flowEvent was not called')
assert.equal(log.flowEvent.callCount, 0, 'log.flowEvent was not called')
log.activityEvent.reset()
})
})
log.activityEvent.reset()
})
})
test('events.emit with flow event', t => {
const request = {}
const data = {}
return events.emit.call(request, 'account.reminder', data)
.then(() => {
t.equal(log.flowEvent.callCount, 1, 'log.flowEvent was called once')
const args = log.flowEvent.args[0]
t.equal(args.length, 2, 'log.flowEvent was passed two arguments')
t.equal(args[0], 'account.reminder', 'first argument was event name')
t.equal(args[1], request, 'second argument was request object')
it('.emit with flow event', () => {
const request = {}
const data = {}
return events.emit.call(request, 'account.reminder', data)
.then(() => {
assert.equal(log.flowEvent.callCount, 1, 'log.flowEvent was called once')
const args = log.flowEvent.args[0]
assert.equal(args.length, 2, 'log.flowEvent was passed two arguments')
assert.equal(args[0], 'account.reminder', 'first argument was event name')
assert.equal(args[1], request, 'second argument was request object')
t.equal(log.activityEvent.callCount, 0, 'log.activityEvent was not called')
assert.equal(log.activityEvent.callCount, 0, 'log.activityEvent was not called')
log.flowEvent.reset()
})
})
log.flowEvent.reset()
})
})
test('events.emit with hybrid activity/flow event', t => {
const request = {}
const data = {}
return events.emit.call(request, 'account.created', data)
.then(() => {
t.equal(log.activityEvent.callCount, 1, 'log.activityEvent was called once')
let args = log.activityEvent.args[0]
t.equal(args.length, 3, 'log.activityEvent was passed three arguments')
t.equal(args[0], 'account.created', 'first argument was event name')
t.equal(args[1], request, 'second argument was request object')
t.equal(args[2], data, 'third argument was event data')
it('.emit with hybrid activity/flow event', () => {
const request = {}
const data = {}
return events.emit.call(request, 'account.created', data)
.then(() => {
assert.equal(log.activityEvent.callCount, 1, 'log.activityEvent was called once')
let args = log.activityEvent.args[0]
assert.equal(args.length, 3, 'log.activityEvent was passed three arguments')
assert.equal(args[0], 'account.created', 'first argument was event name')
assert.equal(args[1], request, 'second argument was request object')
assert.equal(args[2], data, 'third argument was event data')
t.equal(log.flowEvent.callCount, 1, 'log.flowEvent was called once')
args = log.flowEvent.args[0]
t.equal(args.length, 2, 'log.flowEvent was passed two arguments')
t.equal(args[0], 'account.created', 'first argument was event name')
t.equal(args[1], request, 'second argument was request object')
assert.equal(log.flowEvent.callCount, 1, 'log.flowEvent was called once')
args = log.flowEvent.args[0]
assert.equal(args.length, 2, 'log.flowEvent was passed two arguments')
assert.equal(args[0], 'account.created', 'first argument was event name')
assert.equal(args[1], request, 'second argument was request object')
log.activityEvent.reset()
log.flowEvent.reset()
})
})
log.activityEvent.reset()
log.flowEvent.reset()
})
})
test('events.emit with content server account.signed event', t => {
const request = {
query: {
service: 'content-server'
it('.emit with content server account.signed event', () => {
const request = {
query: {
service: 'content-server'
}
}
}
const data = {}
return events.emit.call(request, 'account.signed', data)
.then(() => {
t.equal(log.activityEvent.callCount, 1, 'log.activityEvent was called once')
t.equal(log.flowEvent.callCount, 0, 'log.flowEvent was not called')
const data = {}
return events.emit.call(request, 'account.signed', data)
.then(() => {
assert.equal(log.activityEvent.callCount, 1, 'log.activityEvent was called once')
assert.equal(log.flowEvent.callCount, 0, 'log.flowEvent was not called')
log.activityEvent.reset()
})
})
log.activityEvent.reset()
})
})
test('events.emit with sync account.signed event', t => {
const request = {
query: {
service: 'sync'
it('.emit with sync account.signed event', () => {
const request = {
query: {
service: 'sync'
}
}
}
const data = {}
return events.emit.call(request, 'account.signed', data)
.then(() => {
t.equal(log.activityEvent.callCount, 1, 'log.activityEvent was called once')
t.equal(log.flowEvent.callCount, 1, 'log.flowEvent was called once')
const data = {}
return events.emit.call(request, 'account.signed', data)
.then(() => {
assert.equal(log.activityEvent.callCount, 1, 'log.activityEvent was called once')
assert.equal(log.flowEvent.callCount, 1, 'log.flowEvent was called once')
log.activityEvent.reset()
log.flowEvent.reset()
})
log.activityEvent.reset()
log.flowEvent.reset()
})
})
})

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

@ -2,10 +2,10 @@
* 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/. */
const assert = require('insist')
var sinon = require('sinon')
var P = require('../../lib/promise')
var test = require('../ptaptest')
var mockLog = require('../mocks').mockLog()
var config = {}
var Password = require('../../lib/crypto/password')(mockLog, config)
@ -28,106 +28,108 @@ var CLIENT_ADDRESS = '10.0.0.1'
var checkPassword = require('../../lib/routes/utils/password_check')(mockLog, config, Password, MockCustoms, MockDB)
test(
'password check with correct password',
function (t) {
var authPW = new Buffer('aaaaaaaaaaaaaaaa')
var emailRecord = {
uid: 'correct_password',
verifyHash: null,
verifierVersion: 0,
authSalt: new Buffer('bbbbbbbbbbbbbbbb')
describe('password_check', () => {
it(
'should check with correct password',
() => {
var authPW = new Buffer('aaaaaaaaaaaaaaaa')
var emailRecord = {
uid: 'correct_password',
verifyHash: null,
verifierVersion: 0,
authSalt: new Buffer('bbbbbbbbbbbbbbbb')
}
MockCustoms.flag.reset()
var password = new Password(
authPW, emailRecord.authSalt, emailRecord.verifierVersion)
return password.verifyHash()
.then(
function (hash) {
emailRecord.verifyHash = hash
return checkPassword(emailRecord, authPW, CLIENT_ADDRESS)
}
)
.then(
function (matches) {
assert.ok(matches, 'password matches, checkPassword returns true')
assert.equal(MockCustoms.flag.callCount, 0, 'customs.flag was not called')
}
)
}
MockCustoms.flag.reset()
)
var password = new Password(
authPW, emailRecord.authSalt, emailRecord.verifierVersion)
it(
'should return false when check with incorrect password',
() => {
var authPW = new Buffer('aaaaaaaaaaaaaaaa')
var emailRecord = {
uid: 'uid',
email: 'test@example.com',
verifyHash: null,
verifierVersion: 0,
authSalt: new Buffer('bbbbbbbbbbbbbbbb')
}
MockCustoms.flag.reset()
return password.verifyHash()
.then(
function (hash) {
emailRecord.verifyHash = hash
var password = new Password(
authPW, emailRecord.authSalt, emailRecord.verifierVersion)
return checkPassword(emailRecord, authPW, CLIENT_ADDRESS)
}
)
.then(
function (matches) {
t.ok(matches, 'password matches, checkPassword returns true')
t.equal(MockCustoms.flag.callCount, 0, 'customs.flag was not called')
}
)
}
)
return password.verifyHash()
.then(
function (hash) {
emailRecord.verifyHash = hash
test(
'password check with incorrect password',
function (t) {
var authPW = new Buffer('aaaaaaaaaaaaaaaa')
var emailRecord = {
uid: 'uid',
email: 'test@example.com',
verifyHash: null,
verifierVersion: 0,
authSalt: new Buffer('bbbbbbbbbbbbbbbb')
var incorrectAuthPW = new Buffer('cccccccccccccccc')
return checkPassword(emailRecord, incorrectAuthPW, CLIENT_ADDRESS)
}
)
.then(
function (match) {
assert.equal(!!match, false, 'password does not match, checkPassword returns false')
assert.equal(MockCustoms.flag.callCount, 1, 'customs.flag was called')
assert.equal(MockCustoms.flag.getCall(0).args[0], CLIENT_ADDRESS, 'customs.flag was called with client ip')
assert.deepEqual(MockCustoms.flag.getCall(0).args[1], {
email: emailRecord.email,
errno: error.ERRNO.INCORRECT_PASSWORD
}, 'customs.flag was called with correct event details')
}
)
}
MockCustoms.flag.reset()
)
var password = new Password(
authPW, emailRecord.authSalt, emailRecord.verifierVersion)
it(
'should error when check with account whose password must be reset',
() => {
var emailRecord = {
uid: 'must_reset',
email: 'test@example.com',
verifyHash: null,
verifierVersion: 0,
authSalt: butil.ONES
}
MockCustoms.flag.reset()
return password.verifyHash()
.then(
function (hash) {
emailRecord.verifyHash = hash
var incorrectAuthPW = new Buffer('cccccccccccccccc')
var incorrectAuthPW = new Buffer('cccccccccccccccc')
return checkPassword(emailRecord, incorrectAuthPW, CLIENT_ADDRESS)
}
)
.then(
function (match) {
t.equal(!!match, false, 'password does not match, checkPassword returns false')
t.equal(MockCustoms.flag.callCount, 1, 'customs.flag was called')
t.equal(MockCustoms.flag.getCall(0).args[0], CLIENT_ADDRESS, 'customs.flag was called with client ip')
t.deepEqual(MockCustoms.flag.getCall(0).args[1], {
email: emailRecord.email,
errno: error.ERRNO.INCORRECT_PASSWORD
}, 'customs.flag was called with correct event details')
}
)
}
)
test(
'password check with account whose password must be reset',
function (t) {
var emailRecord = {
uid: 'must_reset',
email: 'test@example.com',
verifyHash: null,
verifierVersion: 0,
authSalt: butil.ONES
return checkPassword(emailRecord, incorrectAuthPW, CLIENT_ADDRESS)
.then(
function (match) {
assert(false, 'password check should not have succeeded')
},
function (err) {
assert.equal(err.errno, error.ERRNO.ACCOUNT_RESET, 'an ACCOUNT_RESET error was thrown')
assert.equal(MockCustoms.flag.callCount, 1, 'customs.flag was called')
assert.equal(MockCustoms.flag.getCall(0).args[0], CLIENT_ADDRESS, 'customs.flag was called with client ip')
assert.deepEqual(MockCustoms.flag.getCall(0).args[1], {
email: emailRecord.email,
errno: error.ERRNO.ACCOUNT_RESET
}, 'customs.flag was called with correct event details')
}
)
}
MockCustoms.flag.reset()
var incorrectAuthPW = new Buffer('cccccccccccccccc')
return checkPassword(emailRecord, incorrectAuthPW, CLIENT_ADDRESS)
.then(
function (match) {
t.fail('password check should not have succeeded')
},
function (err) {
t.equal(err.errno, error.ERRNO.ACCOUNT_RESET, 'an ACCOUNT_RESET error was thrown')
t.equal(MockCustoms.flag.callCount, 1, 'customs.flag was called')
t.equal(MockCustoms.flag.getCall(0).args[0], CLIENT_ADDRESS, 'customs.flag was called with client ip')
t.deepEqual(MockCustoms.flag.getCall(0).args[1], {
email: emailRecord.email,
errno: error.ERRNO.ACCOUNT_RESET
}, 'customs.flag was called with correct event details')
}
)
}
)
)
})

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

@ -2,7 +2,9 @@
* 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')
'use strict'
const assert = require('insist')
var mocks = require('../mocks')
var getRoute = require('../routes_helpers').getRoute
@ -16,7 +18,7 @@ const log = require('../../lib/log')
var TEST_EMAIL = 'foo@gmail.com'
var makeRoutes = function (options) {
function makeRoutes(options) {
options = options || {}
var config = options.config || {
@ -43,246 +45,248 @@ var makeRoutes = function (options) {
)
}
test(
'/password/forgot/send_code',
function (t) {
var mockCustoms = mocks.mockCustoms()
var uid = uuid.v4('binary')
var mockDB = mocks.mockDB({
email: TEST_EMAIL,
passCode: 'foo',
passwordForgotTokenId: crypto.randomBytes(16),
uid: uid
})
var mockMailer = mocks.mockMailer()
var mockMetricsContext = mocks.mockMetricsContext()
var mockLog = log('ERROR', 'test', {
stdout: {
on: sinon.spy(),
write: sinon.spy()
},
stderr: {
on: sinon.spy(),
write: sinon.spy()
}
})
mockLog.flowEvent = sinon.spy(() => {
return P.resolve()
})
var passwordRoutes = makeRoutes({
customs: mockCustoms,
db: mockDB,
mailer : mockMailer,
metricsContext: mockMetricsContext,
log: mockLog
})
var mockRequest = mocks.mockRequest({
log: mockLog,
payload: {
email: TEST_EMAIL
},
query: {},
metricsContext: mockMetricsContext
})
return new P(function(resolve) {
getRoute(passwordRoutes, '/password/forgot/send_code')
.handler(mockRequest, resolve)
})
.then(function(response) {
t.equal(mockDB.emailRecord.callCount, 1, 'db.emailRecord was called once')
t.equal(mockDB.createPasswordForgotToken.callCount, 1, 'db.createPasswordForgotToken was called once')
var args = mockDB.createPasswordForgotToken.args[0]
t.equal(args.length, 1, 'db.createPasswordForgotToken was passed one argument')
t.deepEqual(args[0].uid, uid, 'db.createPasswordForgotToken was passed the correct uid')
t.equal(args[0].createdAt, undefined, 'db.createPasswordForgotToken was not passed a createdAt timestamp')
t.equal(mockRequest.validateMetricsContext.callCount, 1, 'validateMetricsContext was called')
t.equal(mockLog.flowEvent.callCount, 2, 'log.flowEvent was called twice')
t.equal(mockLog.flowEvent.args[0][0], 'password.forgot.send_code.start', 'password.forgot.send_code.start event was logged')
t.equal(mockLog.flowEvent.args[1][0], 'password.forgot.send_code.completed', 'password.forgot.send_code.completed event was logged')
})
}
)
test(
'/password/forgot/resend_code',
function (t) {
var mockCustoms = mocks.mockCustoms()
var uid = uuid.v4('binary')
var mockDB = mocks.mockDB()
var mockMailer = mocks.mockMailer()
var mockMetricsContext = mocks.mockMetricsContext()
var mockLog = log('ERROR', 'test', {
stdout: {
on: sinon.spy(),
write: sinon.spy()
},
stderr: {
on: sinon.spy(),
write: sinon.spy()
}
})
mockLog.flowEvent = sinon.spy(() => {
return P.resolve()
})
var passwordRoutes = makeRoutes({
customs: mockCustoms,
db: mockDB,
mailer : mockMailer,
metricsContext: mockMetricsContext,
log: mockLog
})
var mockRequest = mocks.mockRequest({
credentials: {
data: crypto.randomBytes(16),
describe('/password', () => {
it(
'/forgot/send_code',
() => {
var mockCustoms = mocks.mockCustoms()
var uid = uuid.v4('binary')
var mockDB = mocks.mockDB({
email: TEST_EMAIL,
passCode: Buffer('abcdef', 'hex'),
ttl: function () { return 17 },
passCode: 'foo',
passwordForgotTokenId: crypto.randomBytes(16),
uid: uid
},
log: mockLog,
payload: {
email: TEST_EMAIL
},
query: {},
metricsContext: mockMetricsContext
})
return new P(function(resolve) {
getRoute(passwordRoutes, '/password/forgot/resend_code')
.handler(mockRequest, resolve)
})
.then(function(response) {
t.equal(mockMailer.sendRecoveryCode.callCount, 1, 'mailer.sendRecoveryCode was called once')
t.equal(mockRequest.validateMetricsContext.callCount, 1, 'validateMetricsContext was called')
t.equal(mockLog.flowEvent.callCount, 2, 'log.flowEvent was called twice')
t.equal(mockLog.flowEvent.args[0][0], 'password.forgot.resend_code.start', 'password.forgot.resend_code.start event was logged')
t.equal(mockLog.flowEvent.args[1][0], 'password.forgot.resend_code.completed', 'password.forgot.resend_code.completed event was logged')
})
}
)
var mockMailer = mocks.mockMailer()
var mockMetricsContext = mocks.mockMetricsContext()
var mockLog = log('ERROR', 'test', {
stdout: {
on: sinon.spy(),
write: sinon.spy()
},
stderr: {
on: sinon.spy(),
write: sinon.spy()
}
})
mockLog.flowEvent = sinon.spy(() => {
return P.resolve()
})
var passwordRoutes = makeRoutes({
customs: mockCustoms,
db: mockDB,
mailer : mockMailer,
metricsContext: mockMetricsContext,
log: mockLog
})
test(
'/password/forgot/verify_code',
function (t) {
var mockCustoms = mocks.mockCustoms()
var uid = uuid.v4('binary')
var accountResetToken = {
data: crypto.randomBytes(16)
var mockRequest = mocks.mockRequest({
log: mockLog,
payload: {
email: TEST_EMAIL
},
query: {},
metricsContext: mockMetricsContext
})
return new P(function(resolve) {
getRoute(passwordRoutes, '/password/forgot/send_code')
.handler(mockRequest, resolve)
})
.then(function(response) {
assert.equal(mockDB.emailRecord.callCount, 1, 'db.emailRecord was called once')
assert.equal(mockDB.createPasswordForgotToken.callCount, 1, 'db.createPasswordForgotToken was called once')
var args = mockDB.createPasswordForgotToken.args[0]
assert.equal(args.length, 1, 'db.createPasswordForgotToken was passed one argument')
assert.deepEqual(args[0].uid, uid, 'db.createPasswordForgotToken was passed the correct uid')
assert.equal(args[0].createdAt, undefined, 'db.createPasswordForgotToken was not passed a createdAt timestamp')
assert.equal(mockRequest.validateMetricsContext.callCount, 1, 'validateMetricsContext was called')
assert.equal(mockLog.flowEvent.callCount, 2, 'log.flowEvent was called twice')
assert.equal(mockLog.flowEvent.args[0][0], 'password.forgot.send_code.start', 'password.forgot.send_code.start event was logged')
assert.equal(mockLog.flowEvent.args[1][0], 'password.forgot.send_code.completed', 'password.forgot.send_code.completed event was logged')
})
}
var mockDB = mocks.mockDB({
accountResetToken: accountResetToken,
email: TEST_EMAIL,
passCode: 'abcdef',
passwordForgotTokenId: crypto.randomBytes(16),
uid: uid
})
var mockMailer = mocks.mockMailer()
var mockMetricsContext = mocks.mockMetricsContext()
var mockLog = log('ERROR', 'test', {
stdout: {
on: sinon.spy(),
write: sinon.spy()
},
stderr: {
on: sinon.spy(),
write: sinon.spy()
}
})
mockLog.flowEvent = sinon.spy(() => {
return P.resolve()
})
var passwordRoutes = makeRoutes({
customs: mockCustoms,
db: mockDB,
mailer: mockMailer,
metricsContext: mockMetricsContext
})
)
var mockRequest = mocks.mockRequest({
log: mockLog,
credentials: {
email: TEST_EMAIL,
passCode: Buffer('abcdef', 'hex'),
ttl: function () { return 17 },
uid: uid
},
payload: {
code: 'abcdef'
},
query: {}
})
return new P(function(resolve) {
getRoute(passwordRoutes, '/password/forgot/verify_code')
.handler(mockRequest, resolve)
})
.then(function(response) {
t.deepEqual(Object.keys(response), ['accountResetToken'], 'an accountResetToken was returned')
t.equal(response.accountResetToken, accountResetToken.data.toString('hex'), 'correct accountResetToken was returned')
it(
'/forgot/resend_code',
() => {
var mockCustoms = mocks.mockCustoms()
var uid = uuid.v4('binary')
var mockDB = mocks.mockDB()
var mockMailer = mocks.mockMailer()
var mockMetricsContext = mocks.mockMetricsContext()
var mockLog = log('ERROR', 'test', {
stdout: {
on: sinon.spy(),
write: sinon.spy()
},
stderr: {
on: sinon.spy(),
write: sinon.spy()
}
})
mockLog.flowEvent = sinon.spy(() => {
return P.resolve()
})
var passwordRoutes = makeRoutes({
customs: mockCustoms,
db: mockDB,
mailer : mockMailer,
metricsContext: mockMetricsContext,
log: mockLog
})
t.equal(mockCustoms.check.callCount, 1, 'customs.check was called once')
var mockRequest = mocks.mockRequest({
credentials: {
data: crypto.randomBytes(16),
email: TEST_EMAIL,
passCode: Buffer('abcdef', 'hex'),
ttl: function () { return 17 },
uid: uid
},
log: mockLog,
payload: {
email: TEST_EMAIL
},
query: {},
metricsContext: mockMetricsContext
})
return new P(function(resolve) {
getRoute(passwordRoutes, '/password/forgot/resend_code')
.handler(mockRequest, resolve)
})
.then(function(response) {
assert.equal(mockMailer.sendRecoveryCode.callCount, 1, 'mailer.sendRecoveryCode was called once')
t.equal(mockDB.forgotPasswordVerified.callCount, 1, 'db.passwordForgotVerified was called once')
var args = mockDB.forgotPasswordVerified.args[0]
t.equal(args.length, 1, 'db.passwordForgotVerified was passed one argument')
t.deepEqual(args[0].uid, uid, 'db.forgotPasswordVerified was passed the correct token')
t.equal(mockRequest.validateMetricsContext.callCount, 1, 'validateMetricsContext was called')
t.equal(mockLog.flowEvent.callCount, 2, 'log.flowEvent was called twice')
t.equal(mockLog.flowEvent.args[0][0], 'password.forgot.verify_code.start', 'password.forgot.verify_code.start event was logged')
t.equal(mockLog.flowEvent.args[1][0], 'password.forgot.verify_code.completed', 'password.forgot.verify_code.completed event was logged')
})
}
)
test(
'/password/change/finish',
function (t) {
var uid = uuid.v4('binary')
var mockRequest = mocks.mockRequest({
credentials: {
uid: uid.toString('hex')
},
payload: {
authPW: crypto.randomBytes(32).toString('hex'),
wrapKb: crypto.randomBytes(32).toString('hex'),
sessionToken: crypto.randomBytes(32).toString('hex')
},
query: {
keys: 'true'
}
})
var mockDB = mocks.mockDB({
email: TEST_EMAIL,
uid: uid
})
var mockPush = mocks.mockPush()
var mockMailer = mocks.mockMailer()
var passwordRoutes = makeRoutes({
db: mockDB,
push: mockPush,
mailer: mockMailer
})
return new P(function(resolve) {
getRoute(passwordRoutes, '/password/change/finish')
.handler(mockRequest, function(response) {
resolve(response)
assert.equal(mockRequest.validateMetricsContext.callCount, 1, 'validateMetricsContext was called')
assert.equal(mockLog.flowEvent.callCount, 2, 'log.flowEvent was called twice')
assert.equal(mockLog.flowEvent.args[0][0], 'password.forgot.resend_code.start', 'password.forgot.resend_code.start event was logged')
assert.equal(mockLog.flowEvent.args[1][0], 'password.forgot.resend_code.completed', 'password.forgot.resend_code.completed event was logged')
})
})
.then(function(response) {
t.equal(mockDB.deletePasswordChangeToken.callCount, 1)
t.equal(mockDB.resetAccount.callCount, 1)
}
)
t.equal(mockPush.notifyPasswordChanged.callCount, 1)
t.equal(mockPush.notifyPasswordChanged.firstCall.args[0], uid.toString('hex'))
it(
'/forgot/verify_code',
() => {
var mockCustoms = mocks.mockCustoms()
var uid = uuid.v4('binary')
var accountResetToken = {
data: crypto.randomBytes(16)
}
var mockDB = mocks.mockDB({
accountResetToken: accountResetToken,
email: TEST_EMAIL,
passCode: 'abcdef',
passwordForgotTokenId: crypto.randomBytes(16),
uid: uid
})
var mockMailer = mocks.mockMailer()
var mockMetricsContext = mocks.mockMetricsContext()
var mockLog = log('ERROR', 'test', {
stdout: {
on: sinon.spy(),
write: sinon.spy()
},
stderr: {
on: sinon.spy(),
write: sinon.spy()
}
})
mockLog.flowEvent = sinon.spy(() => {
return P.resolve()
})
var passwordRoutes = makeRoutes({
customs: mockCustoms,
db: mockDB,
mailer: mockMailer,
metricsContext: mockMetricsContext
})
t.equal(mockDB.account.callCount, 1)
t.equal(mockMailer.sendPasswordChangedNotification.callCount, 1)
t.equal(mockMailer.sendPasswordChangedNotification.firstCall.args[0], TEST_EMAIL)
})
}
)
var mockRequest = mocks.mockRequest({
log: mockLog,
credentials: {
email: TEST_EMAIL,
passCode: Buffer('abcdef', 'hex'),
ttl: function () { return 17 },
uid: uid
},
payload: {
code: 'abcdef'
},
query: {}
})
return new P(function(resolve) {
getRoute(passwordRoutes, '/password/forgot/verify_code')
.handler(mockRequest, resolve)
})
.then(function(response) {
assert.deepEqual(Object.keys(response), ['accountResetToken'], 'an accountResetToken was returned')
assert.equal(response.accountResetToken, accountResetToken.data.toString('hex'), 'correct accountResetToken was returned')
assert.equal(mockCustoms.check.callCount, 1, 'customs.check was called once')
assert.equal(mockDB.forgotPasswordVerified.callCount, 1, 'db.passwordForgotVerified was called once')
var args = mockDB.forgotPasswordVerified.args[0]
assert.equal(args.length, 1, 'db.passwordForgotVerified was passed one argument')
assert.deepEqual(args[0].uid, uid, 'db.forgotPasswordVerified was passed the correct token')
assert.equal(mockRequest.validateMetricsContext.callCount, 1, 'validateMetricsContext was called')
assert.equal(mockLog.flowEvent.callCount, 2, 'log.flowEvent was called twice')
assert.equal(mockLog.flowEvent.args[0][0], 'password.forgot.verify_code.start', 'password.forgot.verify_code.start event was logged')
assert.equal(mockLog.flowEvent.args[1][0], 'password.forgot.verify_code.completed', 'password.forgot.verify_code.completed event was logged')
})
}
)
it(
'/change/finish',
() => {
var uid = uuid.v4('binary')
var mockRequest = mocks.mockRequest({
credentials: {
uid: uid.toString('hex')
},
payload: {
authPW: crypto.randomBytes(32).toString('hex'),
wrapKb: crypto.randomBytes(32).toString('hex'),
sessionToken: crypto.randomBytes(32).toString('hex')
},
query: {
keys: 'true'
}
})
var mockDB = mocks.mockDB({
email: TEST_EMAIL,
uid: uid
})
var mockPush = mocks.mockPush()
var mockMailer = mocks.mockMailer()
var passwordRoutes = makeRoutes({
db: mockDB,
push: mockPush,
mailer: mockMailer
})
return new P(function(resolve) {
getRoute(passwordRoutes, '/password/change/finish')
.handler(mockRequest, function(response) {
resolve(response)
})
})
.then(function(response) {
assert.equal(mockDB.deletePasswordChangeToken.callCount, 1)
assert.equal(mockDB.resetAccount.callCount, 1)
assert.equal(mockPush.notifyPasswordChanged.callCount, 1)
assert.equal(mockPush.notifyPasswordChanged.firstCall.args[0], uid.toString('hex'))
assert.equal(mockDB.account.callCount, 1)
assert.equal(mockMailer.sendPasswordChangedNotification.callCount, 1)
assert.equal(mockMailer.sendPasswordChangedNotification.firstCall.args[0], TEST_EMAIL)
})
}
)
})

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

@ -2,85 +2,88 @@
* 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 log = {}
var config = {}
var Password = require('../../lib/crypto/password')(log, config)
'use strict'
test(
'password version zero',
function (t) {
var pwd = Buffer('aaaaaaaaaaaaaaaa')
var salt = Buffer('bbbbbbbbbbbbbbbb')
var p1 = new Password(pwd, salt, 0)
t.equal(p1.version, 0, 'should be using version zero')
var p2 = new Password(pwd, salt, 0)
t.equal(p2.version, 0, 'should be using version zero')
return p1.verifyHash()
.then(
function (hash) {
return p2.matches(hash)
}
)
.then(
function (matched) {
t.ok(matched, 'identical passwords should match')
}
)
}
)
const assert = require('insist')
const log = {}
const config = {}
const Password = require('../../lib/crypto/password')(log, config)
test(
'password version one',
function (t) {
var pwd = Buffer('aaaaaaaaaaaaaaaa')
var salt = Buffer('bbbbbbbbbbbbbbbb')
var p1 = new Password(pwd, salt, 1)
t.equal(p1.version, 1, 'should be using version one')
var p2 = new Password(pwd, salt, 1)
t.equal(p2.version, 1, 'should be using version one')
return p1.verifyHash()
.then(
function (hash) {
return p2.matches(hash)
}
)
.then(
function (matched) {
t.ok(matched, 'identical passwords should match')
}
)
}
)
describe('Password', () => {
it(
'password version zero',
() => {
var pwd = Buffer('aaaaaaaaaaaaaaaa')
var salt = Buffer('bbbbbbbbbbbbbbbb')
var p1 = new Password(pwd, salt, 0)
assert.equal(p1.version, 0, 'should be using version zero')
var p2 = new Password(pwd, salt, 0)
assert.equal(p2.version, 0, 'should be using version zero')
return p1.verifyHash()
.then(
function (hash) {
return p2.matches(hash)
}
)
.then(
function (matched) {
assert.ok(matched, 'identical passwords should match')
}
)
}
)
test(
'passwords of different versions should not match',
function (t) {
var pwd = Buffer('aaaaaaaaaaaaaaaa')
var salt = Buffer('bbbbbbbbbbbbbbbb')
var p1 = new Password(pwd, salt, 0)
var p2 = new Password(pwd, salt, 1)
return p1.verifyHash()
.then(
function (hash) {
return p2.matches(hash)
}
)
.then(
function (matched) {
t.ok(!matched, 'passwords should not match')
}
)
}
)
it(
'password version one',
() => {
var pwd = Buffer('aaaaaaaaaaaaaaaa')
var salt = Buffer('bbbbbbbbbbbbbbbb')
var p1 = new Password(pwd, salt, 1)
assert.equal(p1.version, 1, 'should be using version one')
var p2 = new Password(pwd, salt, 1)
assert.equal(p2.version, 1, 'should be using version one')
return p1.verifyHash()
.then(
function (hash) {
return p2.matches(hash)
}
)
.then(
function (matched) {
assert.ok(matched, 'identical passwords should match')
}
)
}
)
test(
'scrypt queue stats can be reported',
function (t) {
var stat = Password.stat()
t.equal(stat.stat, 'scrypt')
t.ok(stat.hasOwnProperty('numPending'))
t.ok(stat.hasOwnProperty('numPendingHWM'))
t.end()
}
)
it(
'passwords of different versions should not match',
() => {
var pwd = Buffer('aaaaaaaaaaaaaaaa')
var salt = Buffer('bbbbbbbbbbbbbbbb')
var p1 = new Password(pwd, salt, 0)
var p2 = new Password(pwd, salt, 1)
return p1.verifyHash()
.then(
function (hash) {
return p2.matches(hash)
}
)
.then(
function (matched) {
assert.ok(!matched, 'passwords should not match')
}
)
}
)
it(
'scrypt queue stats can be reported',
() => {
var stat = Password.stat()
assert.equal(stat.stat, 'scrypt')
assert.ok(stat.hasOwnProperty('numPending'))
assert.ok(stat.hasOwnProperty('numPendingHWM'))
}
)
})

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

@ -2,56 +2,61 @@
* 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 pbkdf2 = require('../../lib/crypto/pbkdf2')
var ITERATIONS = 20000
var LENGTH = 32
'use strict'
test(
'pbkdf2 derive',
function (t) {
var salt = Buffer('identity.mozilla.com/picl/v1/first-PBKDF:andré@example.org')
var password = Buffer('pässwörd')
return pbkdf2.derive(password, salt, ITERATIONS, LENGTH)
.then(
function (K1) {
t.equal(K1.toString('hex'), 'f84913e3d8e6d624689d0a3e9678ac8dcc79d2c2f3d9641488cd9d6ef6cd83dd')
}
)
}
)
const assert = require('insist')
const pbkdf2 = require('../../lib/crypto/pbkdf2')
const ITERATIONS = 20000
const LENGTH = 32
test(
'pbkdf2 derive long input',
function (t) {
var email = Buffer('ijqmkkafer3xsj5rzoq+msnxsacvkmqxabtsvxvj@some-test-domain-with-a-long-name-example.org')
var password = Buffer('mSnxsacVkMQxAbtSVxVjCCoWArNUsFhiJqmkkafER3XSJ5rzoQ')
var salt = Buffer('identity.mozilla.com/picl/v1/first-PBKDF:' + email)
return pbkdf2.derive(password, salt, ITERATIONS, LENGTH)
.then(
function (K1) {
t.equal(K1.toString('hex'), '5f99c22dfac713b6d73094604a05082e6d345f8a00d4947e57105733f51216eb')
}
)
}
)
describe('pbkdf2', () => {
it(
'pbkdf2 derive',
() => {
var salt = Buffer('identity.mozilla.com/picl/v1/first-PBKDF:andré@example.org')
var password = Buffer('pässwörd')
return pbkdf2.derive(password, salt, ITERATIONS, LENGTH)
.then(
function (K1) {
assert.equal(K1.toString('hex'), 'f84913e3d8e6d624689d0a3e9678ac8dcc79d2c2f3d9641488cd9d6ef6cd83dd')
}
)
}
)
test(
'pbkdf2 derive bit array',
function (t) {
var salt = Buffer('identity.mozilla.com/picl/v1/second-PBKDF:andré@example.org')
var K2 = '5b82f146a64126923e4167a0350bb181feba61f63cb1714012b19cb0be0119c5'
var passwordString = 'pässwörd'
var password = Buffer.concat([
Buffer(K2, 'hex'),
Buffer(passwordString)
])
it(
'pbkdf2 derive long input',
() => {
var email = Buffer('ijqmkkafer3xsj5rzoq+msnxsacvkmqxabtsvxvj@some-test-domain-with-a-long-name-example.org')
var password = Buffer('mSnxsacVkMQxAbtSVxVjCCoWArNUsFhiJqmkkafER3XSJ5rzoQ')
var salt = Buffer('identity.mozilla.com/picl/v1/first-PBKDF:' + email)
return pbkdf2.derive(password, salt, ITERATIONS, LENGTH)
.then(
function (K1) {
assert.equal(K1.toString('hex'), '5f99c22dfac713b6d73094604a05082e6d345f8a00d4947e57105733f51216eb')
}
)
}
)
return pbkdf2.derive(password, salt, ITERATIONS, LENGTH)
.then(
function (K1) {
t.equal(K1.toString('hex'), 'c16d46c31bee242cb31f916e9e38d60b76431d3f5304549cc75ae4bc20c7108c')
}
)
}
)
it(
'pbkdf2 derive bit array',
() => {
var salt = Buffer('identity.mozilla.com/picl/v1/second-PBKDF:andré@example.org')
var K2 = '5b82f146a64126923e4167a0350bb181feba61f63cb1714012b19cb0be0119c5'
var passwordString = 'pässwörd'
var password = Buffer.concat([
Buffer(K2, 'hex'),
Buffer(passwordString)
])
return pbkdf2.derive(password, salt, ITERATIONS, LENGTH)
.then(
function (K1) {
assert.equal(K1.toString('hex'), 'c16d46c31bee242cb31f916e9e38d60b76431d3f5304549cc75ae4bc20c7108c')
}
)
}
)
})

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

@ -2,297 +2,258 @@
* 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 sinon = require('sinon')
var proxyquire = require('proxyquire')
var Pool, poolee
'use strict'
test(
'pool.request with default options',
function (t) {
setup()
const assert = require('insist')
const sinon = require('sinon')
const proxyquire = require('proxyquire')
var pool = new Pool('http://example.com/ignore/me')
pool.request()
describe('Pool', () => {
t.equal(poolee.request.callCount, 1, 'poolee.request was called once')
let Pool, poolee
var args = poolee.request.getCall(0).args
t.equal(args.length, 2, 'poolee.request was passed two arguments')
var options = args[0]
t.equal(typeof options, 'object', 'options is object')
t.equal(Object.keys(options).length, 4, 'options has 4 properties')
t.equal(options.method, 'GET', 'options.method is GET')
t.equal(options.path, undefined, 'options.path is undefined')
t.equal(typeof options.headers, 'object', 'options.headers is object')
t.equal(Object.keys(options.headers).length, 1, 'options.headers has 1 property')
t.equal(options.headers['Content-Type'], 'application/json', 'Content-Type header is application/json')
t.equal(options.data, undefined, 'options.data is undefined')
var callback = args[1]
t.equal(typeof callback, 'function', 'callback is function')
t.end()
}
)
test(
'pool.request with alternative options',
function (t) {
setup()
var pool = new Pool('http://example.com/')
pool.request('POST', '/foo', { bar: 'baz' })
t.equal(poolee.request.callCount, 1, 'poolee.request was called once')
var args = poolee.request.getCall(0).args
t.equal(args.length, 2, 'poolee.request was passed two arguments')
var options = args[0]
t.equal(typeof options, 'object', 'options is object')
t.equal(Object.keys(options).length, 4, 'options has 4 properties')
t.equal(options.method, 'POST', 'options.method is POST')
t.equal(options.path, '/foo', 'options.path is /foo')
t.equal(typeof options.headers, 'object', 'options.headers is object')
t.equal(Object.keys(options.headers).length, 1, 'options.headers has 1 property')
t.equal(options.headers['Content-Type'], 'application/json', 'Content-Type header is application/json')
t.equal(options.data, '{"bar":"baz"}', 'options.data is correct')
var callback = args[1]
t.equal(typeof callback, 'function', 'callback is function')
t.end()
}
)
test(
'pool.request callback with error',
function (t) {
setup()
var pool = new Pool('http://example.com/')
pool.request()
.then(function () {
t.fail('request should have failed')
t.end()
}, function (error) {
t.equal(typeof error, 'string', 'error is string')
t.equal(error, 'foo', 'error is correct')
t.end()
})
var args = poolee.request.getCall(0).args
var callback = args[1]
callback('foo')
}
)
test(
'pool.request callback with HTTP error response',
function (t) {
setup()
var pool = new Pool('http://example.com/')
pool.request()
.then(function () {
t.fail('request should have failed')
t.end()
}, function (error) {
t.ok(error instanceof Error, 'error is Error instance')
t.equal(error.statusCode, 404, 'error.statusCode is 404')
t.equal(error.message, 'wibble', 'error.message is correct')
t.end()
})
var args = poolee.request.getCall(0).args
var callback = args[1]
callback(null, { statusCode: 404 }, 'wibble')
}
)
test(
'pool.request callback with HTTP error response and JSON body',
function (t) {
setup()
var pool = new Pool('http://example.com/')
pool.request()
.then(function () {
t.fail('request should have failed')
t.end()
}, function (error) {
t.equal(error instanceof Error, false, 'error is not Error instance')
t.equal(typeof error, 'object', 'error is object')
t.equal(Object.keys(error).length, 2, 'error has two properties')
t.equal(error.statusCode, 418, 'error.statusCode is 418')
t.equal(error.foo, 'bar', 'other error data is correct')
t.end()
})
var args = poolee.request.getCall(0).args
var callback = args[1]
callback(null, { statusCode: 418 }, '{"foo":"bar"}')
}
)
test(
'pool.request callback with HTTP success response and empty body',
function (t) {
setup()
var pool = new Pool('http://example.com/')
pool.request()
.then(function (result) {
t.equal(result, undefined, 'result is undefined')
t.end()
}, function () {
t.fail('request should have succeeded')
t.end()
})
var args = poolee.request.getCall(0).args
var callback = args[1]
callback(null, { statusCode: 200 }, '')
}
)
test(
'pool.request callback with HTTP success response and valid JSON body',
function (t) {
setup()
var pool = new Pool('http://example.com/')
pool.request()
.then(function (result) {
t.equal(typeof result, 'object', 'result is object')
t.equal(Object.keys(result).length, 1, 'result has 1 property')
t.equal(result.foo, 'bar', 'result data is correct')
t.end()
}, function () {
t.fail('request should have succeeded')
t.end()
})
var args = poolee.request.getCall(0).args
var callback = args[1]
callback(null, { statusCode: 200 }, '{"foo":"bar"}')
}
)
test(
'pool.request callback with HTTP success response and invalid JSON body',
function (t) {
setup()
var pool = new Pool('http://example.com/')
pool.request()
.then(function () {
t.fail('request should have failed')
t.end()
}, function (error) {
t.ok(error instanceof Error, 'error is Error instance')
t.equal(error.statusCode, undefined, 'error.statusCode is undefined')
t.equal(error.message, 'Invalid JSON', 'error.message is correct')
t.end()
})
var args = poolee.request.getCall(0).args
var callback = args[1]
callback(null, { statusCode: 200 }, 'foo')
}
)
test(
'pool.get',
function (t) {
setup()
var pool = new Pool('http://example.com/')
sinon.stub(pool, 'request', function () {})
pool.get('foo')
t.equal(pool.request.callCount, 1, 'pool.request was called once')
var args = pool.request.getCall(0).args
t.equal(args.length, 2, 'pool.request was passed three arguments')
t.equal(args[0], 'GET', 'first argument to pool.request was POST')
t.equal(args[1], 'foo', 'second argument to pool.request was correct')
t.end()
}
)
test(
'pool.put',
function (t) {
setup()
var pool = new Pool('http://example.com/')
sinon.stub(pool, 'request', function () {})
pool.put('baz', 'qux')
t.equal(pool.request.callCount, 1, 'pool.request was called once')
var args = pool.request.getCall(0).args
t.equal(args.length, 3, 'pool.request was passed three arguments')
t.equal(args[0], 'PUT', 'first argument to pool.request was POST')
t.equal(args[1], 'baz', 'second argument to pool.request was correct')
t.equal(args[2], 'qux', 'third argument to pool.request was correct')
t.end()
}
)
test(
'pool.post',
function (t) {
setup()
var pool = new Pool('http://example.com/')
sinon.stub(pool, 'request', function () {})
pool.post('foo', 'bar')
t.equal(pool.request.callCount, 1, 'pool.request was called once')
var args = pool.request.getCall(0).args
t.equal(args.length, 3, 'pool.request was passed three arguments')
t.equal(args[0], 'POST', 'first argument to pool.request was POST')
t.equal(args[1], 'foo', 'second argument to pool.request was correct')
t.equal(args[2], 'bar', 'third argument to pool.request was correct')
t.end()
}
)
test(
'pool.del',
function (t) {
setup()
var pool = new Pool('http://example.com/')
sinon.stub(pool, 'request', function () {})
pool.del('foo', 'bar')
t.equal(pool.request.callCount, 1, 'pool.request was called once')
var args = pool.request.getCall(0).args
t.equal(args.length, 3, 'pool.request was passed three arguments')
t.equal(args[0], 'DELETE', 'first argument to pool.request was POST')
t.equal(args[1], 'foo', 'second argument to pool.request was correct')
t.equal(args[2], 'bar', 'second argument can be data')
t.end()
}
)
function setup () {
poolee = sinon.createStubInstance(require('poolee'))
Pool = proxyquire('../../lib/pool', {
poolee: function () {
return poolee
}
beforeEach(() => {
poolee = sinon.createStubInstance(require('poolee'))
Pool = proxyquire('../../lib/pool', {
poolee: function () {
return poolee
}
})
})
}
it(
'pool.request with default options',
() => {
var pool = new Pool('http://example.com/ignore/me')
pool.request()
assert.equal(poolee.request.callCount, 1, 'poolee.request was called once')
var args = poolee.request.getCall(0).args
assert.equal(args.length, 2, 'poolee.request was passed two arguments')
var options = args[0]
assert.equal(typeof options, 'object', 'options is object')
assert.equal(Object.keys(options).length, 4, 'options has 4 properties')
assert.equal(options.method, 'GET', 'options.method is GET')
assert.equal(options.path, undefined, 'options.path is undefined')
assert.equal(typeof options.headers, 'object', 'options.headers is object')
assert.equal(Object.keys(options.headers).length, 1, 'options.headers has 1 property')
assert.equal(options.headers['Content-Type'], 'application/json', 'Content-Type header is application/json')
assert.equal(options.data, undefined, 'options.data is undefined')
var callback = args[1]
assert.equal(typeof callback, 'function', 'callback is function')
}
)
it(
'pool.request with alternative options',
() => {
var pool = new Pool('http://example.com/')
pool.request('POST', '/foo', { bar: 'baz' })
assert.equal(poolee.request.callCount, 1, 'poolee.request was called once')
var args = poolee.request.getCall(0).args
assert.equal(args.length, 2, 'poolee.request was passed two arguments')
var options = args[0]
assert.equal(typeof options, 'object', 'options is object')
assert.equal(Object.keys(options).length, 4, 'options has 4 properties')
assert.equal(options.method, 'POST', 'options.method is POST')
assert.equal(options.path, '/foo', 'options.path is /foo')
assert.equal(typeof options.headers, 'object', 'options.headers is object')
assert.equal(Object.keys(options.headers).length, 1, 'options.headers has 1 property')
assert.equal(options.headers['Content-Type'], 'application/json', 'Content-Type header is application/json')
assert.equal(options.data, '{"bar":"baz"}', 'options.data is correct')
var callback = args[1]
assert.equal(typeof callback, 'function', 'callback is function')
}
)
it(
'pool.request callback with error',
() => {
var pool = new Pool('http://example.com/')
let p = pool.request()
.then(function () {
assert(false, 'request should have failed')
}, function (error) {
assert.equal(typeof error, 'string', 'error is string')
assert.equal(error, 'foo', 'error is correct')
})
var args = poolee.request.getCall(0).args
var callback = args[1]
callback('foo')
return p
}
)
it(
'pool.request callback with HTTP error response',
() => {
var pool = new Pool('http://example.com/')
let p = pool.request()
.then(function () {
assert(false, 'request should have failed')
}, function (error) {
assert.ok(error instanceof Error, 'error is Error instance')
assert.equal(error.statusCode, 404, 'error.statusCode is 404')
assert.equal(error.message, 'wibble', 'error.message is correct')
})
var args = poolee.request.getCall(0).args
var callback = args[1]
callback(null, { statusCode: 404 }, 'wibble')
return p
}
)
it(
'pool.request callback with HTTP error response and JSON body',
() => {
var pool = new Pool('http://example.com/')
let p = pool.request()
.then(function () {
assert(false, 'request should have failed')
}, function (error) {
assert.equal(error instanceof Error, false, 'error is not Error instance')
assert.equal(typeof error, 'object', 'error is object')
assert.equal(Object.keys(error).length, 2, 'error has two properties')
assert.equal(error.statusCode, 418, 'error.statusCode is 418')
assert.equal(error.foo, 'bar', 'other error data is correct')
})
var args = poolee.request.getCall(0).args
var callback = args[1]
callback(null, { statusCode: 418 }, '{"foo":"bar"}')
return p
}
)
it(
'pool.request callback with HTTP success response and empty body',
() => {
var pool = new Pool('http://example.com/')
let p = pool.request()
.then(function (result) {
assert.equal(result, undefined, 'result is undefined')
})
var args = poolee.request.getCall(0).args
var callback = args[1]
callback(null, { statusCode: 200 }, '')
return p
}
)
it(
'pool.request callback with HTTP success response and valid JSON body',
() => {
var pool = new Pool('http://example.com/')
let p = pool.request()
.then(function (result) {
assert.equal(typeof result, 'object', 'result is object')
assert.equal(Object.keys(result).length, 1, 'result has 1 property')
assert.equal(result.foo, 'bar', 'result data is correct')
})
var args = poolee.request.getCall(0).args
var callback = args[1]
callback(null, { statusCode: 200 }, '{"foo":"bar"}')
return p
}
)
it(
'pool.request callback with HTTP success response and invalid JSON body',
() => {
var pool = new Pool('http://example.com/')
let p = pool.request()
.then(function () {
assert(false, 'request should have failed')
}, function (error) {
assert.ok(error instanceof Error, 'error is Error instance')
assert.equal(error.statusCode, undefined, 'error.statusCode is undefined')
assert.equal(error.message, 'Invalid JSON', 'error.message is correct')
})
var args = poolee.request.getCall(0).args
var callback = args[1]
callback(null, { statusCode: 200 }, 'foo')
return p
}
)
it(
'pool.get',
() => {
var pool = new Pool('http://example.com/')
sinon.stub(pool, 'request', function () {})
pool.get('foo')
assert.equal(pool.request.callCount, 1, 'pool.request was called once')
var args = pool.request.getCall(0).args
assert.equal(args.length, 2, 'pool.request was passed three arguments')
assert.equal(args[0], 'GET', 'first argument to pool.request was POST')
assert.equal(args[1], 'foo', 'second argument to pool.request was correct')
}
)
it(
'pool.put',
() => {
var pool = new Pool('http://example.com/')
sinon.stub(pool, 'request', function () {})
pool.put('baz', 'qux')
assert.equal(pool.request.callCount, 1, 'pool.request was called once')
var args = pool.request.getCall(0).args
assert.equal(args.length, 3, 'pool.request was passed three arguments')
assert.equal(args[0], 'PUT', 'first argument to pool.request was POST')
assert.equal(args[1], 'baz', 'second argument to pool.request was correct')
assert.equal(args[2], 'qux', 'third argument to pool.request was correct')
}
)
it(
'pool.post',
() => {
var pool = new Pool('http://example.com/')
sinon.stub(pool, 'request', function () {})
pool.post('foo', 'bar')
assert.equal(pool.request.callCount, 1, 'pool.request was called once')
var args = pool.request.getCall(0).args
assert.equal(args.length, 3, 'pool.request was passed three arguments')
assert.equal(args[0], 'POST', 'first argument to pool.request was POST')
assert.equal(args[1], 'foo', 'second argument to pool.request was correct')
assert.equal(args[2], 'bar', 'third argument to pool.request was correct')
}
)
it(
'pool.del',
() => {
var pool = new Pool('http://example.com/')
sinon.stub(pool, 'request', function () {})
pool.del('foo', 'bar')
assert.equal(pool.request.callCount, 1, 'pool.request was called once')
var args = pool.request.getCall(0).args
assert.equal(args.length, 3, 'pool.request was passed three arguments')
assert.equal(args[0], 'DELETE', 'first argument to pool.request was POST')
assert.equal(args[1], 'foo', 'second argument to pool.request was correct')
assert.equal(args[2], 'bar', 'second argument can be data')
}
)
})

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -4,28 +4,26 @@
'use strict'
const test = require('../ptaptest')
const assert = require('insist')
const requestHelper = require('../../lib/routes/utils/request_helper')
test(
'interface is correct',
t => {
t.equal(typeof requestHelper, 'object', 'object type should be exported')
t.equal(Object.keys(requestHelper).length, 1, 'object should have one properties')
t.equal(typeof requestHelper.wantsKeys, 'function', 'wantsKeys should be function')
describe('requestHelper', () => {
it(
'interface is correct',
() => {
assert.equal(typeof requestHelper, 'object', 'object type should be exported')
assert.equal(Object.keys(requestHelper).length, 1, 'object should have one properties')
assert.equal(typeof requestHelper.wantsKeys, 'function', 'wantsKeys should be function')
}
)
t.end()
}
)
test(
'wantsKeys',
t => {
t.equal(!! requestHelper.wantsKeys({}), false, 'should return falsey if request.query is not set')
t.equal(requestHelper.wantsKeys({ query: {} }), false, 'should return false if query.keys is not set')
t.equal(requestHelper.wantsKeys({ query: { keys: 'wibble' } }), false, 'should return false if keys is not true')
t.equal(requestHelper.wantsKeys({ query: { keys: 'true' } }), true, 'should return true if keys is true')
t.end()
}
)
it(
'wantsKeys',
() => {
assert.equal(!! requestHelper.wantsKeys({}), false, 'should return falsey if request.query is not set')
assert.equal(requestHelper.wantsKeys({ query: {} }), false, 'should return false if query.keys is not set')
assert.equal(requestHelper.wantsKeys({ query: { keys: 'wibble' } }), false, 'should return false if keys is not true')
assert.equal(requestHelper.wantsKeys({ query: { keys: 'true' } }), true, 'should return true if keys is true')
}
)
})

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

@ -2,7 +2,9 @@
* 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')
'use strict'
const assert = require('insist')
var promise = require('../../lib/promise')
var config = { scrypt: { maxPending: 5 } }
var log = {
@ -12,43 +14,45 @@ var log = {
var scrypt = require('../../lib/crypto/scrypt')(log, config)
test(
'scrypt basic',
function (t) {
var K1 = Buffer('f84913e3d8e6d624689d0a3e9678ac8dcc79d2c2f3d9641488cd9d6ef6cd83dd', 'hex')
var salt = Buffer('identity.mozilla.com/picl/v1/scrypt')
describe('scrypt', () => {
it(
'scrypt basic',
() => {
var K1 = Buffer('f84913e3d8e6d624689d0a3e9678ac8dcc79d2c2f3d9641488cd9d6ef6cd83dd', 'hex')
var salt = Buffer('identity.mozilla.com/picl/v1/scrypt')
return scrypt.hash(K1, salt, 65536, 8, 1, 32)
.then(
function (K2) {
t.equal(K2, '5b82f146a64126923e4167a0350bb181feba61f63cb1714012b19cb0be0119c5')
return scrypt.hash(K1, salt, 65536, 8, 1, 32)
.then(
function (K2) {
assert.equal(K2, '5b82f146a64126923e4167a0350bb181feba61f63cb1714012b19cb0be0119c5')
}
)
}
)
it(
'scrypt enforces maximum number of pending requests',
() => {
var K1 = Buffer('f84913e3d8e6d624689d0a3e9678ac8dcc79d2c2f3d9641488cd9d6ef6cd83dd', 'hex')
var salt = Buffer('identity.mozilla.com/picl/v1/scrypt')
// Check the we're using the lower maxPending setting from config.
assert.equal(scrypt.maxPending, 5, 'maxPending is correctly set from config')
// Send many concurrent requests.
// Not yielding the event loop ensures they will pile up quickly.
var promises = []
for (var i = 0; i < 10; i++) {
promises.push(scrypt.hash(K1, salt, 65536, 8, 1, 32))
}
return promise.all(promises).then(
function () {
assert(false, 'too many pending scrypt hashes were allowed')
},
function (err) {
assert.equal(err.message, 'too many pending scrypt hashes')
assert.equal(scrypt.numPendingHWM, 6, 'HWM should be maxPending+1')
assert.equal(log.buffer[0].op, 'scrypt.maxPendingExceeded')
}
)
}
)
test(
'scrypt enforces maximum number of pending requests',
function (t) {
var K1 = Buffer('f84913e3d8e6d624689d0a3e9678ac8dcc79d2c2f3d9641488cd9d6ef6cd83dd', 'hex')
var salt = Buffer('identity.mozilla.com/picl/v1/scrypt')
// Check the we're using the lower maxPending setting from config.
t.equal(scrypt.maxPending, 5, 'maxPending is correctly set from config')
// Send many concurrent requests.
// Not yielding the event loop ensures they will pile up quickly.
var promises = []
for (var i = 0; i < 10; i++) {
promises.push(scrypt.hash(K1, salt, 65536, 8, 1, 32))
}
return promise.all(promises).then(
function () {
t.fail('too many pending scrypt hashes were allowed')
},
function (err) {
t.equal(err.message, 'too many pending scrypt hashes')
t.equal(scrypt.numPendingHWM, 6, 'HWM should be maxPending+1')
t.equal(log.buffer[0].op, 'scrypt.maxPendingExceeded')
}
)
}
)
)
})

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

@ -2,8 +2,10 @@
* 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/. */
'use strict'
const assert = require('insist')
var sinon = require('sinon')
var test = require('../ptaptest')
var log = { trace: function() {}, info: function () {} }
var crypto = require('crypto')
@ -24,312 +26,314 @@ var ACCOUNT = {
tokenVerificationId: crypto.randomBytes(16)
}
test(
'interface is correct',
function (t) {
return SessionToken.create(ACCOUNT)
.then(function (token) {
t.equal(typeof token.lastAuthAt, 'function', 'lastAuthAt method is defined')
t.equal(typeof token.update, 'function', 'update method is defined')
t.equal(typeof token.isFresh, 'function', 'isFresh method is defined')
t.equal(typeof token.setUserAgentInfo, 'function', 'setUserAgentInfo method is defined')
})
}
)
describe('SessionToken', () => {
it(
'interface is correct',
() => {
return SessionToken.create(ACCOUNT)
.then(function (token) {
assert.equal(typeof token.lastAuthAt, 'function', 'lastAuthAt method is defined')
assert.equal(typeof token.update, 'function', 'update method is defined')
assert.equal(typeof token.isFresh, 'function', 'isFresh method is defined')
assert.equal(typeof token.setUserAgentInfo, 'function', 'setUserAgentInfo method is defined')
})
}
)
test(
're-creation from tokenData works',
function (t) {
var token = null
return SessionToken.create(ACCOUNT)
.then(
function (x) {
token = x
t.equal(token.accountCreatedAt, ACCOUNT.createdAt)
it(
're-creation from tokenData works',
() => {
var token = null
return SessionToken.create(ACCOUNT)
.then(
function (x) {
token = x
assert.equal(token.accountCreatedAt, ACCOUNT.createdAt)
}
)
.then(
function () {
return SessionToken.fromHex(token.data, ACCOUNT)
}
)
.then(
function (token2) {
assert.deepEqual(token.data, token2.data)
assert.deepEqual(token.id, token2.id)
assert.deepEqual(token.authKey, token2.authKey)
assert.deepEqual(token.bundleKey, token2.bundleKey)
assert.deepEqual(token.uid, token2.uid)
assert.equal(token.email, token2.email)
assert.equal(token.emailCode, token2.emailCode)
assert.equal(token.emailVerified, token2.emailVerified)
assert.equal(token.accountCreatedAt, token2.accountCreatedAt)
assert.equal(token.tokenVerified, token2.tokenVerified)
assert.equal(token.tokenVerificationId, token2.tokenVerificationId)
}
)
}
)
it(
'create with NaN createdAt',
() => {
return SessionToken.create({
createdAt: NaN,
email: 'foo',
uid: 'bar'
}).then(
function (token) {
var now = Date.now()
assert.ok(token.createdAt > now - 1000 && token.createdAt <= now)
assert.equal(token.accountCreatedAt, null)
}
)
.then(
function () {
return SessionToken.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)
t.equal(token.email, token2.email)
t.equal(token.emailCode, token2.emailCode)
t.equal(token.emailVerified, token2.emailVerified)
t.equal(token.accountCreatedAt, token2.accountCreatedAt)
t.equal(token.tokenVerified, token2.tokenVerified)
t.equal(token.tokenVerificationId, token2.tokenVerificationId)
}
)
}
)
}
)
test(
'create with NaN createdAt',
function (t) {
return SessionToken.create({
createdAt: NaN,
email: 'foo',
uid: 'bar'
}).then(
function (token) {
var now = Date.now()
t.ok(token.createdAt > now - 1000 && token.createdAt <= now)
t.equal(token.accountCreatedAt, null)
}
)
}
)
it(
'sessionToken key derivations are test-vector compliant',
() => {
var token = null
var tokenData = 'a0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf'
return SessionToken.fromHex(tokenData, ACCOUNT)
.then(
function (x) {
token = x
assert.equal(token.data.toString('hex'), tokenData)
assert.equal(token.id.toString('hex'), 'c0a29dcf46174973da1378696e4c82ae10f723cf4f4d9f75e39f4ae3851595ab')
assert.equal(token.authKey.toString('hex'), '9d8f22998ee7f5798b887042466b72d53e56ab0c094388bf65831f702d2febc0')
}
)
}
)
test(
'sessionToken key derivations are test-vector compliant',
function (t) {
var token = null
var tokenData = 'a0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebf'
return SessionToken.fromHex(tokenData, ACCOUNT)
.then(
function (x) {
token = x
t.equal(token.data.toString('hex'), tokenData)
t.equal(token.id.toString('hex'), 'c0a29dcf46174973da1378696e4c82ae10f723cf4f4d9f75e39f4ae3851595ab')
t.equal(token.authKey.toString('hex'), '9d8f22998ee7f5798b887042466b72d53e56ab0c094388bf65831f702d2febc0')
}
)
}
)
it(
'SessionToken.setUserAgentInfo',
() => {
return SessionToken.create(ACCOUNT)
.then(function (token) {
token.setUserAgentInfo({
data: 'foo',
tokenId: 'foo',
authKey: 'foo',
bundleKey: 'foo',
algorithm: 'foo',
uid: 'foo',
lifetime: 'foo',
createdAt: 'foo',
email: 'foo',
emailCode: 'foo',
emailVerified: 'foo',
verifierSetAt: 'foo',
locale: 'foo',
accountCreatedAt: 'foo',
uaBrowser: 'foo',
uaBrowserVersion: 'bar',
uaOS: 'baz',
uaOSVersion: 'qux',
uaDeviceType: 'wibble',
lastAccessTime: 'mnngh'
})
assert.notEqual(token.data, 'foo', 'data was not updated')
assert.notEqual(token.tokenId, 'foo', 'tokenId was not updated')
assert.notEqual(token.authKey, 'foo', 'authKey was not updated')
assert.notEqual(token.bundleKey, 'foo', 'bundleKey was not updated')
assert.notEqual(token.algorithm, 'foo', 'algorithm was not updated')
assert.notEqual(token.uid, 'foo', 'uid was not updated')
assert.notEqual(token.lifetime, 'foo', 'lifetime was not updated')
assert.notEqual(token.createdAt, 'foo', 'createdAt was not updated')
assert.notEqual(token.email, 'foo', 'email was not updated')
assert.notEqual(token.emailVerified, 'foo', 'emailVerified was not updated')
assert.notEqual(token.verifierSetAt, 'foo', 'verifierSetAt was not updated')
assert.notEqual(token.locale, 'foo', 'locale was not updated')
assert.notEqual(token.accountCreatedAt, 'foo', 'accountCreatedAt was not updated')
assert.equal(token.uaBrowser, 'foo', 'uaBrowser was updated')
assert.equal(token.uaBrowserVersion, 'bar', 'uaBrowserVersion was updated')
assert.equal(token.uaOS, 'baz', 'uaOS was updated')
assert.equal(token.uaOSVersion, 'qux', 'uaOSVersion was updated')
assert.equal(token.uaDeviceType, 'wibble', 'uaDeviceType was updated')
assert.equal(token.lastAccessTime, 'mnngh', 'lastAccessTime was updated')
})
}
)
test(
'SessionToken.setUserAgentInfo',
function (t) {
return SessionToken.create(ACCOUNT)
.then(function (token) {
token.setUserAgentInfo({
data: 'foo',
tokenId: 'foo',
authKey: 'foo',
bundleKey: 'foo',
algorithm: 'foo',
uid: 'foo',
lifetime: 'foo',
createdAt: 'foo',
email: 'foo',
emailCode: 'foo',
emailVerified: 'foo',
verifierSetAt: 'foo',
locale: 'foo',
accountCreatedAt: 'foo',
it(
'SessionToken.isFresh with lastAccessTime updates enabled',
() => {
config.lastAccessTimeUpdates.enabled = true
config.lastAccessTimeUpdates.sampleRate = 1
config.lastAccessTimeUpdates.enabledEmailAddresses = /.+/
return SessionToken.create({
uaBrowser: 'foo',
uaBrowserVersion: 'bar',
uaOS: 'baz',
uaOSVersion: 'qux',
uaDeviceType: 'wibble',
lastAccessTime: 0
}).then(token => {
assert.equal(token.isFresh({
uaBrowser: 'foo',
uaBrowserVersion: 'bar',
uaOS: 'baz',
uaOSVersion: 'qux',
uaDeviceType: 'wibble',
lastAccessTime: 'mnngh'
})
t.notEqual(token.data, 'foo', 'data was not updated')
t.notEqual(token.tokenId, 'foo', 'tokenId was not updated')
t.notEqual(token.authKey, 'foo', 'authKey was not updated')
t.notEqual(token.bundleKey, 'foo', 'bundleKey was not updated')
t.notEqual(token.algorithm, 'foo', 'algorithm was not updated')
t.notEqual(token.uid, 'foo', 'uid was not updated')
t.notEqual(token.lifetime, 'foo', 'lifetime was not updated')
t.notEqual(token.createdAt, 'foo', 'createdAt was not updated')
t.notEqual(token.email, 'foo', 'email was not updated')
t.notEqual(token.emailVerified, 'foo', 'emailVerified was not updated')
t.notEqual(token.verifierSetAt, 'foo', 'verifierSetAt was not updated')
t.notEqual(token.locale, 'foo', 'locale was not updated')
t.notEqual(token.accountCreatedAt, 'foo', 'accountCreatedAt was not updated')
t.equal(token.uaBrowser, 'foo', 'uaBrowser was updated')
t.equal(token.uaBrowserVersion, 'bar', 'uaBrowserVersion was updated')
t.equal(token.uaOS, 'baz', 'uaOS was updated')
t.equal(token.uaOSVersion, 'qux', 'uaOSVersion was updated')
t.equal(token.uaDeviceType, 'wibble', 'uaDeviceType was updated')
t.equal(token.lastAccessTime, 'mnngh', 'lastAccessTime was updated')
lastAccessTime: 0
}), true, 'returns true when all fields are the same')
assert.equal(token.isFresh({
uaBrowser: 'Foo',
uaBrowserVersion: 'bar',
uaOS: 'baz',
uaOSVersion: 'qux',
uaDeviceType: 'wibble',
lastAccessTime: 0
}), false, 'returns false when uaBrowser is different')
assert.equal(token.isFresh({
uaBrowser: 'foo',
uaBrowserVersion: 'baR',
uaOS: 'baz',
uaOSVersion: 'qux',
uaDeviceType: 'wibble',
lastAccessTime: 0
}), false, 'returns false when uaBrowserVersion is different')
assert.equal(token.isFresh({
uaBrowser: 'foo',
uaBrowserVersion: 'bar',
uaOS: 'foo',
uaOSVersion: 'qux',
uaDeviceType: 'wibble',
lastAccessTime: 0
}), false, 'returns false when uaOS is different')
assert.equal(token.isFresh({
uaBrowser: 'foo',
uaBrowserVersion: 'bar',
uaOS: 'baz',
uaOSVersion: 'QUX',
uaDeviceType: 'wibble',
lastAccessTime: 0
}), false, 'returns false when uaOSVersion is different')
assert.equal(token.isFresh({
uaBrowser: 'foo',
uaBrowserVersion: 'bar',
uaOS: 'baz',
uaOSVersion: 'qux',
uaDeviceType: 'wobble',
lastAccessTime: 0
}), false, 'returns false when uaDeviceType is different')
assert.equal(token.isFresh({
uaBrowser: 'foo',
uaBrowserVersion: 'bar',
uaOS: 'baz',
uaOSVersion: 'qux',
uaDeviceType: 'wibble',
lastAccessTime: TOKEN_FRESHNESS_THRESHOLD
}), false, 'returns false when lastAccessTime is TOKEN_FRESHNESS_THRESHOLD milliseconds newer')
assert.equal(token.isFresh({
uaBrowser: 'foo',
uaBrowserVersion: 'bar',
uaOS: 'baz',
uaOSVersion: 'qux',
uaDeviceType: 'wibble',
lastAccessTime: 3599999
}), true, 'returns true when lastAccessTime is 3,599,999 milliseconds newer')
})
}
)
}
)
test(
'SessionToken.isFresh with lastAccessTime updates enabled',
t => {
config.lastAccessTimeUpdates.enabled = true
config.lastAccessTimeUpdates.sampleRate = 1
config.lastAccessTimeUpdates.enabledEmailAddresses = /.+/
return SessionToken.create({
uaBrowser: 'foo',
uaBrowserVersion: 'bar',
uaOS: 'baz',
uaOSVersion: 'qux',
uaDeviceType: 'wibble',
lastAccessTime: 0
}).then(token => {
t.equal(token.isFresh({
it(
'SessionToken.isFresh with lastAccessTime updates disabled',
() => {
config.lastAccessTimeUpdates.enabled = false
return SessionToken.create({
uaBrowser: 'foo',
uaBrowserVersion: 'bar',
uaOS: 'baz',
uaOSVersion: 'qux',
uaDeviceType: 'wibble',
lastAccessTime: 0
}), true, 'returns true when all fields are the same')
t.equal(token.isFresh({
uaBrowser: 'Foo',
uaBrowserVersion: 'bar',
uaOS: 'baz',
uaOSVersion: 'qux',
uaDeviceType: 'wibble',
lastAccessTime: 0
}), false, 'returns false when uaBrowser is different')
t.equal(token.isFresh({
uaBrowser: 'foo',
uaBrowserVersion: 'baR',
uaOS: 'baz',
uaOSVersion: 'qux',
uaDeviceType: 'wibble',
lastAccessTime: 0
}), false, 'returns false when uaBrowserVersion is different')
t.equal(token.isFresh({
uaBrowser: 'foo',
uaBrowserVersion: 'bar',
uaOS: 'foo',
uaOSVersion: 'qux',
uaDeviceType: 'wibble',
lastAccessTime: 0
}), false, 'returns false when uaOS is different')
t.equal(token.isFresh({
uaBrowser: 'foo',
uaBrowserVersion: 'bar',
uaOS: 'baz',
uaOSVersion: 'QUX',
uaDeviceType: 'wibble',
lastAccessTime: 0
}), false, 'returns false when uaOSVersion is different')
t.equal(token.isFresh({
uaBrowser: 'foo',
uaBrowserVersion: 'bar',
uaOS: 'baz',
uaOSVersion: 'qux',
uaDeviceType: 'wobble',
lastAccessTime: 0
}), false, 'returns false when uaDeviceType is different')
t.equal(token.isFresh({
uaBrowser: 'foo',
uaBrowserVersion: 'bar',
uaOS: 'baz',
uaOSVersion: 'qux',
uaDeviceType: 'wibble',
lastAccessTime: TOKEN_FRESHNESS_THRESHOLD
}), false, 'returns false when lastAccessTime is TOKEN_FRESHNESS_THRESHOLD milliseconds newer')
t.equal(token.isFresh({
uaBrowser: 'foo',
uaBrowserVersion: 'bar',
uaOS: 'baz',
uaOSVersion: 'qux',
uaDeviceType: 'wibble',
lastAccessTime: 3599999
}), true, 'returns true when lastAccessTime is 3,599,999 milliseconds newer')
})
}
)
test(
'SessionToken.isFresh with lastAccessTime updates disabled',
t => {
config.lastAccessTimeUpdates.enabled = false
return SessionToken.create({
uaBrowser: 'foo',
uaBrowserVersion: 'bar',
uaOS: 'baz',
uaOSVersion: 'qux',
uaDeviceType: 'wibble',
lastAccessTime: 0
}).then(token => {
t.equal(token.isFresh({
uaBrowser: 'foo',
uaBrowserVersion: 'bar',
uaOS: 'baz',
uaOSVersion: 'qux',
uaDeviceType: 'wibble',
lastAccessTime: TOKEN_FRESHNESS_THRESHOLD
}), true, 'returns true when lastAccessTime is TOKEN_FRESHNESS_THRESHOLD milliseconds newer')
})
}
)
test(
'SessionToken.update on fresh token',
function (t) {
return SessionToken.create(
).then(function (token) {
sinon.stub(SessionToken.prototype, 'isFresh', function () {
return true
}).then(token => {
assert.equal(token.isFresh({
uaBrowser: 'foo',
uaBrowserVersion: 'bar',
uaOS: 'baz',
uaOSVersion: 'qux',
uaDeviceType: 'wibble',
lastAccessTime: TOKEN_FRESHNESS_THRESHOLD
}), true, 'returns true when lastAccessTime is TOKEN_FRESHNESS_THRESHOLD milliseconds newer')
})
sinon.spy(SessionToken.prototype, 'setUserAgentInfo')
}
)
t.equal(
token.update(
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:41.0) Gecko/20100101 Firefox/41.0'
), false, 'returns'
)
t.equal(SessionToken.prototype.isFresh.callCount, 1, 'isFresh was called once')
t.equal(SessionToken.prototype.isFresh.thisValues[0], token, 'isFresh context was token')
var isFreshArgs = SessionToken.prototype.isFresh.args[0]
t.equal(isFreshArgs.length, 1, 'isFresh was passed one argument')
var isFreshData = isFreshArgs[0]
t.equal(typeof isFreshData, 'object', 'isFresh was passed an object')
t.equal(Object.keys(isFreshData).length, 6, 'isFresh data had six properties')
t.equal(isFreshData.uaBrowser, 'Firefox', 'uaBrowser was correct')
t.equal(isFreshData.uaBrowserVersion, '41', 'uaBrowserVersion was correct')
t.equal(isFreshData.uaOS, 'Mac OS X', 'uaOS was correct')
t.equal(isFreshData.uaOSVersion, '10.10', 'uaOSVersion was correct')
t.equal(isFreshData.uaDeviceType, null, 'uaDeviceType was correct')
t.ok(isFreshData.lastAccessTime > Date.now() - 10000, 'lastAccessTime was greater than 10 seconds ago')
t.ok(isFreshData.lastAccessTime < Date.now(), 'lastAccessTime was less then Date.now()')
t.equal(SessionToken.prototype.setUserAgentInfo.callCount, 0, 'setUserAgentInfo was not called')
})
.finally(function () {
SessionToken.prototype.isFresh.restore()
SessionToken.prototype.setUserAgentInfo.restore()
})
}
)
test(
'SessionToken.update on stale token',
function (t) {
return SessionToken.create()
.then(function (token) {
it(
'SessionToken.update on fresh token',
() => {
return SessionToken.create(
).then(function (token) {
sinon.stub(SessionToken.prototype, 'isFresh', function () {
return false
return true
})
sinon.spy(SessionToken.prototype, 'setUserAgentInfo')
t.equal(
assert.equal(
token.update(
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:41.0) Gecko/20100101 Firefox/41.0'
), true, 'returns true'
), false, 'returns'
)
t.equal(SessionToken.prototype.isFresh.callCount, 1, 'isFresh was called once')
var isFreshArgs = SessionToken.prototype.setUserAgentInfo.args[0]
t.equal(isFreshArgs.length, 1, 'isFresh was passed one argument')
assert.equal(SessionToken.prototype.isFresh.callCount, 1, 'isFresh was called once')
assert.equal(SessionToken.prototype.isFresh.thisValues[0], token, 'isFresh context was token')
var isFreshArgs = SessionToken.prototype.isFresh.args[0]
assert.equal(isFreshArgs.length, 1, 'isFresh was passed one argument')
var isFreshData = isFreshArgs[0]
assert.equal(typeof isFreshData, 'object', 'isFresh was passed an object')
assert.equal(Object.keys(isFreshData).length, 6, 'isFresh data had six properties')
assert.equal(isFreshData.uaBrowser, 'Firefox', 'uaBrowser was correct')
assert.equal(isFreshData.uaBrowserVersion, '41', 'uaBrowserVersion was correct')
assert.equal(isFreshData.uaOS, 'Mac OS X', 'uaOS was correct')
assert.equal(isFreshData.uaOSVersion, '10.10', 'uaOSVersion was correct')
assert.equal(isFreshData.uaDeviceType, null, 'uaDeviceType was correct')
assert.ok(isFreshData.lastAccessTime > Date.now() - 10000, 'lastAccessTime was greater than 10 seconds ago')
assert.ok(isFreshData.lastAccessTime < Date.now(), 'lastAccessTime was less then Date.now()')
t.equal(SessionToken.prototype.setUserAgentInfo.callCount, 1, 'setUserAgentInfo called once')
t.equal(SessionToken.prototype.setUserAgentInfo.thisValues[0], token, 'setUserAgentInfo context was token')
var setUserAgentInfoArgs = SessionToken.prototype.setUserAgentInfo.args[0]
t.equal(setUserAgentInfoArgs.length, 1, 'setUserAgentInfo was passed one argument')
t.deepEqual(setUserAgentInfoArgs[0], isFreshArgs[0], 'setUserAgentInfo was passed correct argument')
assert.equal(SessionToken.prototype.setUserAgentInfo.callCount, 0, 'setUserAgentInfo was not called')
})
.finally(function () {
SessionToken.prototype.isFresh.restore()
SessionToken.prototype.setUserAgentInfo.restore()
})
}
)
}
)
it(
'SessionToken.update on stale token',
() => {
return SessionToken.create()
.then(function (token) {
sinon.stub(SessionToken.prototype, 'isFresh', function () {
return false
})
sinon.spy(SessionToken.prototype, 'setUserAgentInfo')
assert.equal(
token.update(
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:41.0) Gecko/20100101 Firefox/41.0'
), true, 'returns true'
)
assert.equal(SessionToken.prototype.isFresh.callCount, 1, 'isFresh was called once')
var isFreshArgs = SessionToken.prototype.setUserAgentInfo.args[0]
assert.equal(isFreshArgs.length, 1, 'isFresh was passed one argument')
assert.equal(SessionToken.prototype.setUserAgentInfo.callCount, 1, 'setUserAgentInfo called once')
assert.equal(SessionToken.prototype.setUserAgentInfo.thisValues[0], token, 'setUserAgentInfo context was token')
var setUserAgentInfoArgs = SessionToken.prototype.setUserAgentInfo.args[0]
assert.equal(setUserAgentInfoArgs.length, 1, 'setUserAgentInfo was passed one argument')
assert.deepEqual(setUserAgentInfoArgs[0], isFreshArgs[0], 'setUserAgentInfo was passed correct argument')
})
.finally(function () {
SessionToken.prototype.isFresh.restore()
SessionToken.prototype.setUserAgentInfo.restore()
})
}
)
})

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

@ -2,6 +2,9 @@
* 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/. */
'use strict'
const assert = require('insist')
var crypto = require('crypto')
var uuid = require('uuid')
var error = require('../../lib/error')
@ -9,151 +12,152 @@ var getRoute = require('../routes_helpers').getRoute
var isA = require('joi')
var mocks = require('../mocks')
var P = require('../../lib/promise')
var test = require('../ptaptest')
test(
'/certificate/sign',
function (t) {
t.plan(4)
var deviceId = crypto.randomBytes(16)
var mockDevices = mocks.mockDevices({
deviceId: deviceId
})
const mockLog = mocks.spyLog()
const mockRequest = mocks.mockRequest({
credentials: {
accountCreatedAt: Date.now(),
emailVerified: true,
lastAuthAt: function () {
return Date.now()
},
locale: 'en',
tokenId: crypto.randomBytes(16),
uid: uuid.v4('binary')
},
log: mockLog,
payload: {
duration: 0,
publicKey: {
algorithm: 'RS',
n: 'bar',
e: 'baz'
}
},
query: {}
})
t.test('without service', function (t) {
return runTest({
devices: mockDevices,
log: mockLog
}, mockRequest, function () {
t.equal(mockDevices.upsert.callCount, 1, 'devices.upsert was called once')
var args = mockDevices.upsert.args[0]
t.equal(args.length, 3, 'devices.upsert was passed one argument')
t.equal(args[0], mockRequest, 'first argument was request object')
t.equal(args[1], mockRequest.auth.credentials, 'second argument was sessionToken')
t.deepEqual(args[2], {}, 'third argument was empty object')
t.equal(mockLog.activityEvent.callCount, 1, 'log.activityEvent was called once')
args = mockLog.activityEvent.args[0]
t.equal(args.length, 3, 'log.activityEvent was passed three arguments')
t.equal(args[0], 'account.signed', 'first argument was event name')
t.equal(args[1], mockRequest, 'second argument was request object')
t.deepEqual(args[2], {
uid: mockRequest.auth.credentials.uid.toString('hex'),
account_created_at: mockRequest.auth.credentials.accountCreatedAt,
device_id: deviceId.toString('hex')
}, 'third argument was event data')
})
.then(function () {
mockLog.activityEvent.reset()
mockDevices.upsert.reset()
})
})
t.test('with service=sync', function (t) {
mockRequest.query.service = 'sync'
return runTest({
devices: mockDevices,
log: mockLog
}, mockRequest, function () {
t.equal(mockDevices.upsert.callCount, 1, 'devices.upsert was called once')
t.equal(mockLog.activityEvent.callCount, 1, 'log.activityEvent was called once')
})
.then(function () {
mockLog.activityEvent.reset()
mockDevices.upsert.reset()
})
})
t.test('with service=foo', function (t) {
mockRequest.query.service = 'foo'
return runTest({
devices: mockDevices,
log: mockLog
}, mockRequest, function () {
t.equal(mockDevices.upsert.callCount, 0, 'devices.upsert was not called')
t.equal(mockLog.activityEvent.callCount, 1, 'log.activityEvent was called once')
t.equal(mockLog.activityEvent.args[0][2].device_id, undefined, 'device_id was undefined')
})
.then(function () {
mockLog.activityEvent.reset()
mockDevices.upsert.reset()
})
})
t.test('with deviceId', function (t) {
mockRequest.query.service = 'sync'
mockRequest.auth.credentials.deviceId = crypto.randomBytes(16)
return runTest({
devices: mockDevices,
log: mockLog
}, mockRequest, function () {
t.equal(mockDevices.upsert.callCount, 0, 'devices.upsert was not called')
t.equal(mockLog.activityEvent.callCount, 1, 'log.activityEvent was called once')
t.equal(mockLog.activityEvent.args[0][2].device_id, mockRequest.auth.credentials.deviceId.toString('hex'), 'device_id was correct')
})
.then(function () {
mockLog.activityEvent.reset()
mockDevices.upsert.reset()
})
})
}
)
function runTest (options, request, assertions) {
return new P(function (resolve) {
getRoute(makeRoutes(options), '/certificate/sign')
.handler(request, resolve)
describe('/certificate/sign', () => {
var deviceId = crypto.randomBytes(16)
var mockDevices = mocks.mockDevices({
deviceId: deviceId
})
.then(assertions)
}
function makeRoutes (options) {
options = options || {}
var log = options.log || mocks.mockLog()
return require('../../lib/routes/sign')(
log,
P,
isA,
error,
options.signer || {
sign: function () {
return P.resolve({})
const mockLog = mocks.spyLog()
const mockRequest = mocks.mockRequest({
credentials: {
accountCreatedAt: Date.now(),
emailVerified: true,
lastAuthAt: function () {
return Date.now()
},
locale: 'en',
tokenId: crypto.randomBytes(16),
uid: uuid.v4('binary')
},
log: mockLog,
payload: {
duration: 0,
publicKey: {
algorithm: 'RS',
n: 'bar',
e: 'baz'
}
},
options.db || {
updateSessionToken: function () {},
updateLocale: function () {}
},
options.domain || 'wibble',
options.devices
)
}
query: {}
})
it('without service', function () {
return runTest({
devices: mockDevices,
log: mockLog
}, mockRequest, function () {
assert.equal(mockDevices.upsert.callCount, 1, 'devices.upsert was called once')
var args = mockDevices.upsert.args[0]
assert.equal(args.length, 3, 'devices.upsert was passed one argument')
assert.equal(args[0], mockRequest, 'first argument was request object')
assert.equal(args[1], mockRequest.auth.credentials, 'second argument was sessionToken')
assert.deepEqual(args[2], {}, 'third argument was empty object')
assert.equal(mockLog.activityEvent.callCount, 1, 'log.activityEvent was called once')
args = mockLog.activityEvent.args[0]
assert.equal(args.length, 3, 'log.activityEvent was passed three arguments')
assert.equal(args[0], 'account.signed', 'first argument was event name')
assert.equal(args[1], mockRequest, 'second argument was request object')
assert.deepEqual(args[2], {
uid: mockRequest.auth.credentials.uid.toString('hex'),
account_created_at: mockRequest.auth.credentials.accountCreatedAt,
device_id: deviceId.toString('hex')
}, 'third argument was event data')
})
.then(function () {
mockLog.activityEvent.reset()
mockDevices.upsert.reset()
})
})
it('with service=sync', () => {
mockRequest.query.service = 'sync'
return runTest({
devices: mockDevices,
log: mockLog
}, mockRequest, function () {
assert.equal(mockDevices.upsert.callCount, 1, 'devices.upsert was called once')
assert.equal(mockLog.activityEvent.callCount, 1, 'log.activityEvent was called once')
})
.then(function () {
mockLog.activityEvent.reset()
mockDevices.upsert.reset()
})
})
it('with service=foo', () => {
mockRequest.query.service = 'foo'
return runTest({
devices: mockDevices,
log: mockLog
}, mockRequest, function () {
assert.equal(mockDevices.upsert.callCount, 0, 'devices.upsert was not called')
assert.equal(mockLog.activityEvent.callCount, 1, 'log.activityEvent was called once')
assert.equal(mockLog.activityEvent.args[0][2].device_id, undefined, 'device_id was undefined')
})
.then(function () {
mockLog.activityEvent.reset()
mockDevices.upsert.reset()
})
})
it('with deviceId', () => {
mockRequest.query.service = 'sync'
mockRequest.auth.credentials.deviceId = crypto.randomBytes(16)
return runTest({
devices: mockDevices,
log: mockLog
}, mockRequest, function () {
assert.equal(mockDevices.upsert.callCount, 0, 'devices.upsert was not called')
assert.equal(mockLog.activityEvent.callCount, 1, 'log.activityEvent was called once')
assert.equal(mockLog.activityEvent.args[0][2].device_id, mockRequest.auth.credentials.deviceId.toString('hex'), 'device_id was correct')
})
.then(function () {
mockLog.activityEvent.reset()
mockDevices.upsert.reset()
})
})
function runTest (options, request, assertions) {
return new P(function (resolve, reject) {
getRoute(makeRoutes(options), '/certificate/sign')
.handler(request, (res) => {
if (res instanceof Error) {
reject(res)
} else {
resolve(res)
}
})
})
.then(assertions)
}
function makeRoutes (options) {
options = options || {}
var log = options.log || mocks.mockLog()
return require('../../lib/routes/sign')(
log,
P,
isA,
error,
options.signer || {
sign: function () {
return P.resolve({})
}
},
options.db || {
updateSessionToken: function () {},
updateLocale: function () {}
},
options.domain || 'wibble',
options.devices
)
}
})

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

@ -2,226 +2,230 @@
* 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 tap = require('tap')
var test = tap.test
'use strict'
const assert = require('insist')
var StatsDCollector = require('../../lib/metrics/statsd')
var mockLog = require('../mocks').mockLog()
test(
'statsd init failure cases',
function (t) {
describe('metrics/statsd', () => {
it(
'statsd init failure cases',
() => {
assert.throws(() => {
void new StatsDCollector()
}, 'Log is required')
try {
void new StatsDCollector()
} catch (e) {
t.equal(e.message, 'Log is required')
assert.doesNotThrow(() => {
var statsd = new StatsDCollector(mockLog)
statsd.init()
})
}
)
try {
it(
'statsd init',
() => {
var statsd = new StatsDCollector(mockLog)
statsd.init()
} catch (e) {
t.fail('should not throw')
assert.equal(statsd.connected, true)
}
)
t.end()
}
)
test(
'statsd init',
function (t) {
var statsd = new StatsDCollector(mockLog)
statsd.init()
t.equal(statsd.connected, true)
t.end()
}
)
test(
'statsd write',
function (t) {
function StatsDMock() {
return {
socket: {},
increment: function (name, value, sampleRate, tags) {
t.equal(name, 'fxa.auth.some-event')
t.equal(value, 1)
t.ok(sampleRate)
t.equal(Array.isArray(tags), true)
t.equal(tags.length, 0)
t.end()
it(
'statsd write',
() => {
let count = 0
function StatsDMock() {
return {
socket: {},
increment: function (name, value, sampleRate, tags) {
assert.equal(name, 'fxa.auth.some-event')
assert.equal(value, 1)
assert.ok(sampleRate)
assert.equal(Array.isArray(tags), true)
assert.equal(tags.length, 0)
count++
}
}
}
var statsd = new StatsDCollector(mockLog)
statsd.init()
statsd.client = new StatsDMock()
statsd.write({
event: 'some-event',
uid: 'id'
})
assert.equal(count, 1)
}
)
var statsd = new StatsDCollector(mockLog)
statsd.init()
statsd.client = new StatsDMock()
statsd.write({
event: 'some-event',
uid: 'id'
})
}
)
test(
'statsd write with tags',
function (t) {
function StatsDMock() {
return {
socket: {},
increment: function (name, value, sampleRate, tags) {
t.equal(name, 'fxa.auth.some-event')
t.equal(value, 1)
t.ok(sampleRate)
t.deepEquals(tags, [
'agent_ua_family:Firefox',
'agent_ua_version:43.0',
'agent_ua_version_major:43',
'agent_os_version:10.11',
'agent_os_family:Mac OS X',
'agent_os_major:10'
])
t.end()
it(
'statsd write with tags',
() => {
let count = 0
function StatsDMock() {
return {
socket: {},
increment: function (name, value, sampleRate, tags) {
assert.equal(name, 'fxa.auth.some-event')
assert.equal(value, 1)
assert.ok(sampleRate)
assert.deepEqual(tags, [
'agent_ua_family:Firefox',
'agent_ua_version:43.0',
'agent_ua_version_major:43',
'agent_os_version:10.11',
'agent_os_family:Mac OS X',
'agent_os_major:10'
])
count++
}
}
}
var statsd = new StatsDCollector(mockLog)
statsd.init()
statsd.client = new StatsDMock()
statsd.write({
event: 'some-event',
uid: 'id',
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:43.0) Gecko/20100101 Firefox/43.0'
})
assert.equal(count, 1)
}
)
var statsd = new StatsDCollector(mockLog)
statsd.init()
statsd.client = new StatsDMock()
statsd.write({
event: 'some-event',
uid: 'id',
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.11; rv:43.0) Gecko/20100101 Firefox/43.0'
})
}
)
test(
'statsd write via log.increment',
function (t) {
function StatsDMock() {
return {
socket: {},
increment: function (name, value, sampleRate, tags) {
t.equal(name, 'fxa.auth.some-event')
t.equal(value, 1)
t.ok(sampleRate)
t.equal(Array.isArray(tags), true)
t.equal(tags.length, 0)
t.end()
it(
'statsd write via log.increment',
() => {
let count = 0
function StatsDMock() {
return {
socket: {},
increment: function (name, value, sampleRate, tags) {
assert.equal(name, 'fxa.auth.some-event')
assert.equal(value, 1)
assert.ok(sampleRate)
assert.equal(Array.isArray(tags), true)
assert.equal(tags.length, 0)
count++
}
}
}
var statsd = new StatsDCollector(mockLog)
statsd.init()
statsd.client = new StatsDMock()
var log = require('../../lib/log')('info')
log.statsd = statsd
log.increment('some-event')
assert.equal(count, 1)
}
)
var statsd = new StatsDCollector(mockLog)
statsd.init()
statsd.client = new StatsDMock()
var log = require('../../lib/log')('info')
log.statsd = statsd
log.increment('some-event')
}
)
test(
'statsd write error',
function (t) {
var mockLog = {
error: function (log) {
t.equal(log.op, 'statsd.increment')
t.equal(log.err.message, 'Failed to send message')
t.end()
}
}
function StatsDMock() {
return {
socket: {},
increment: function (name, value, sampleRate, tags, cb) {
cb(new Error('Failed to send message'))
it(
'statsd write error',
() => {
let count = 0
var mockLog = {
error: function (log) {
assert.equal(log.op, 'statsd.increment')
assert.equal(log.err.message, 'Failed to send message')
count++
}
}
}
var statsd = new StatsDCollector(mockLog)
statsd.init()
statsd.client = new StatsDMock()
statsd.write({
event: 'some-event',
uid: 'id'
})
}
)
test(
'statsd.timing',
function (t) {
function StatsDMock() {
return {
socket: {},
increment: function () {
t.fail('statsd.increment should not be called')
t.end()
},
timing: function () {
t.equal(arguments.length, 5, 'statsd.timing received the correct number arguments')
t.equal(arguments[0], 'fxa.auth.foo.time', 'statsd.timing received the correct name argument')
t.equal(arguments[1], 1, 'statsd.timing received the correct timing argument')
t.equal(typeof arguments[2], 'number', 'statsd.timing received the correct timing argument')
t.equal(arguments[3], undefined, 'statsd.timing received the correct tags argument')
t.equal(typeof arguments[4], 'function', 'statsd.timing received the correct callback argument')
t.end()
function StatsDMock() {
return {
socket: {},
increment: function (name, value, sampleRate, tags, cb) {
cb(new Error('Failed to send message'))
}
}
}
var statsd = new StatsDCollector(mockLog)
statsd.init()
statsd.client = new StatsDMock()
statsd.write({
event: 'some-event',
uid: 'id'
})
assert.equal(count, 1)
}
)
var statsd = new StatsDCollector(mockLog)
statsd.init()
statsd.client = new StatsDMock()
var log = require('../../lib/log')('info')
log.statsd = statsd
log.timing('foo', 1)
}
)
test(
'statsd.timing error',
function (t) {
var mockLog = {
error: function (log) {
t.equal(log.op, 'statsd.timing')
t.equal(log.err, 'foo')
t.end()
}
}
function StatsDMock() {
return {
socket: {},
timing: function () {
arguments[4]('foo')
it(
'statsd.timing',
() => {
let count = 0
function StatsDMock() {
return {
socket: {},
increment: function () {
assert(false, 'statsd.increment should not be called')
},
timing: function () {
assert.equal(arguments.length, 5, 'statsd.timing received the correct number arguments')
assert.equal(arguments[0], 'fxa.auth.foo.time', 'statsd.timing received the correct name argument')
assert.equal(arguments[1], 1, 'statsd.timing received the correct timing argument')
assert.equal(typeof arguments[2], 'number', 'statsd.timing received the correct timing argument')
assert.equal(arguments[3], undefined, 'statsd.timing received the correct tags argument')
assert.equal(typeof arguments[4], 'function', 'statsd.timing received the correct callback argument')
count++
}
}
}
var statsd = new StatsDCollector(mockLog)
statsd.init()
statsd.client = new StatsDMock()
var log = require('../../lib/log')('info')
log.statsd = statsd
log.timing('foo', 1)
assert.equal(count, 1)
}
)
var statsd = new StatsDCollector(mockLog)
statsd.init()
statsd.client = new StatsDMock()
statsd.timing('wibble', 42)
}
)
it(
'statsd.timing error',
() => {
let count = 0
var mockLog = {
error: function (log) {
assert.equal(log.op, 'statsd.timing')
assert.equal(log.err, 'foo')
count++
}
}
test(
'statsd close',
function (t) {
var statsd = new StatsDCollector(mockLog)
statsd.init()
t.equal(statsd.connected, true)
statsd.close()
t.equal(statsd.connected, false)
t.end()
}
)
function StatsDMock() {
return {
socket: {},
timing: function () {
arguments[4]('foo')
}
}
}
var statsd = new StatsDCollector(mockLog)
statsd.init()
statsd.client = new StatsDMock()
statsd.timing('wibble', 42)
assert.equal(count, 1)
}
)
it(
'statsd close',
() => {
var statsd = new StatsDCollector(mockLog)
statsd.init()
assert.equal(statsd.connected, true)
statsd.close()
assert.equal(statsd.connected, false)
}
)
})

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

@ -4,12 +4,12 @@
'use strict'
const assert = require('insist')
var crypto = require('crypto')
var hkdf = require('../../lib/crypto/hkdf')
var mocks = require('../mocks')
var P = require('../../lib/promise')
var sinon = require('sinon')
var test = require('tap').test
var Bundle = {
bundle: sinon.spy(),
@ -18,58 +18,60 @@ var Bundle = {
var log = mocks.spyLog()
var modulePath = '../../lib/tokens/token'
test('NODE_ENV=dev', function (t) {
process.env.NODE_ENV = 'dev'
var Token = require(modulePath)(log, crypto, P, hkdf, Bundle, null)
describe('Token', () => {
t.plan(4)
describe('NODE_ENV=dev', () => {
let Token
before(() => {
delete require.cache[require.resolve(modulePath)]
delete require.cache[require.resolve('../../config')]
process.env.NODE_ENV = 'dev'
Token = require(modulePath)(log, crypto, P, hkdf, Bundle, null)
})
t.test('Token constructor was exported', function (t) {
t.equal(typeof Token, 'function', 'Token is function')
t.equal(Token.name, 'Token', 'function is called Token')
t.equal(Token.length, 2, 'function expects two arguments')
t.end()
it('Token constructor was exported', () => {
assert.equal(typeof Token, 'function', 'Token is function')
assert.equal(Token.name, 'Token', 'function is called Token')
assert.equal(Token.length, 2, 'function expects two arguments')
})
it('Token constructor sets createdAt', () => {
var now = Date.now() - 1
var token = new Token({}, { createdAt: now })
assert.equal(token.createdAt, now, 'token.createdAt is correct')
})
it('Token constructor does not set createdAt if it is negative', () => {
var notNow = -Date.now()
var token = new Token({}, { createdAt: notNow })
assert.ok(token.createdAt > 0, 'token.createdAt seems correct')
})
it('Token constructor does not set createdAt if it is in the future', () => {
var notNow = Date.now() + 1000
var token = new Token({}, { createdAt: notNow })
assert.ok(token.createdAt > 0 && token.createdAt < notNow, 'token.createdAt seems correct')
})
})
t.test('Token constructor sets createdAt', function (t) {
var now = Date.now() - 1
var token = new Token({}, { createdAt: now })
describe('NODE_ENV=prod', () => {
let Token
before(() => {
delete require.cache[require.resolve(modulePath)]
delete require.cache[require.resolve('../../config')]
process.env.NODE_ENV = 'prod'
Token = require(modulePath)(log, crypto, P, hkdf, Bundle, null)
})
t.equal(token.createdAt, now, 'token.createdAt is correct')
t.end()
it('Token constructor does not set createdAt', () => {
var notNow = Date.now() - 1
var token = new Token({}, { createdAt: notNow })
assert.ok(token.createdAt > notNow, 'token.createdAt seems correct')
})
})
t.test('Token constructor does not set createdAt if it is negative', function (t) {
var notNow = -Date.now()
var token = new Token({}, { createdAt: notNow })
t.ok(token.createdAt > 0, 'token.createdAt seems correct')
t.end()
})
t.test('Token constructor does not set createdAt if it is in the future', function (t) {
var notNow = Date.now() + 1000
var token = new Token({}, { createdAt: notNow })
t.ok(token.createdAt > 0 && token.createdAt < notNow, 'token.createdAt seems correct')
t.end()
})
})
test('NODE_ENV=prod', function (t) {
process.env.NODE_ENV = 'prod'
delete require.cache[require.resolve(modulePath)]
delete require.cache[require.resolve('../../config')]
var Token = require(modulePath)(log, crypto, P, hkdf, Bundle, null)
t.plan(1)
t.test('Token constructor does not set createdAt', function (t) {
var notNow = Date.now() - 1
var token = new Token({}, { createdAt: notNow })
t.ok(token.createdAt > notNow, 'token.createdAt seems correct')
t.end()
})
})

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

@ -4,7 +4,7 @@
'use strict'
var test = require('../ptaptest')
const assert = require('insist')
var mocks = require('../mocks')
var proxyquire = require('proxyquire')
var sinon = require('sinon')
@ -24,494 +24,480 @@ var userAgent = proxyquire('../../lib/userAgent', {
var log = mocks.spyLog()
test(
'exports function',
function (t) {
t.equal(typeof userAgent, 'function')
t.equal(userAgent.length, 2)
t.end()
}
)
test(
'sets data correctly',
function (t) {
parserResult = {
ua: {
family: 'foo',
major: '1',
minor: '0'
},
os: {
family: 'bar',
major: '2',
minor: '0'
},
device: {
family: 'baz'
}
describe('userAgent', () => {
it(
'exports function',
() => {
assert.equal(typeof userAgent, 'function')
assert.equal(userAgent.length, 2)
}
var context = {}
var result = userAgent.call(context, 'qux', log)
)
t.equal(uaParser.parse.callCount, 1)
t.ok(uaParser.parse.calledWithExactly('qux'))
t.equal(result, context)
t.equal(Object.keys(result).length, 5)
t.equal(result.uaBrowser, 'foo')
t.equal(result.uaBrowserVersion, '1')
t.equal(result.uaOS, 'bar')
t.equal(result.uaOSVersion, '2')
t.equal(result.uaDeviceType, 'mobile')
t.equal(log.info.callCount, 0)
t.end()
uaParser.parse.reset()
}
)
test(
'ignores family:Other',
function (t) {
parserResult = {
ua: {
family: 'Other',
major: '1',
minor: '0'
},
os: {
family: 'Other',
major: '2',
minor: '0'
},
device: {
family: 'Other'
it(
'sets data correctly',
() => {
parserResult = {
ua: {
family: 'foo',
major: '1',
minor: '0'
},
os: {
family: 'bar',
major: '2',
minor: '0'
},
device: {
family: 'baz'
}
}
var context = {}
var result = userAgent.call(context, 'qux', log)
assert.equal(uaParser.parse.callCount, 1)
assert.ok(uaParser.parse.calledWithExactly('qux'))
assert.equal(result, context)
assert.equal(Object.keys(result).length, 5)
assert.equal(result.uaBrowser, 'foo')
assert.equal(result.uaBrowserVersion, '1')
assert.equal(result.uaOS, 'bar')
assert.equal(result.uaOSVersion, '2')
assert.equal(result.uaDeviceType, 'mobile')
assert.equal(log.info.callCount, 0)
uaParser.parse.reset()
}
var context = {}
var result = userAgent.call(context, 'wibble', log)
)
t.equal(uaParser.parse.callCount, 1)
t.ok(uaParser.parse.calledWithExactly('wibble'))
t.equal(result, context)
t.equal(Object.keys(result).length, 5)
t.equal(result.uaBrowser, 'wibble')
t.equal(result.uaOS, null)
t.equal(result.uaDeviceType, null)
t.equal(log.info.callCount, 1)
var args = log.info.args[0]
t.equal(args.length, 1)
t.deepEqual(args[0], {
op: 'userAgent:truncate',
userAgent: 'wibble'
})
t.end()
uaParser.parse.reset()
log.info.reset()
}
)
test(
'appends minor version if set',
function (t) {
parserResult = {
ua: {
family: 'foo',
major: '1',
minor: '1'
},
os: {
family: 'bar',
major: '2',
minor: '34567'
},
device: {
family: 'baz'
it(
'ignores family:Other',
() => {
parserResult = {
ua: {
family: 'Other',
major: '1',
minor: '0'
},
os: {
family: 'Other',
major: '2',
minor: '0'
},
device: {
family: 'Other'
}
}
var context = {}
var result = userAgent.call(context, 'wibble', log)
assert.equal(uaParser.parse.callCount, 1)
assert.ok(uaParser.parse.calledWithExactly('wibble'))
assert.equal(result, context)
assert.equal(Object.keys(result).length, 5)
assert.equal(result.uaBrowser, 'wibble')
assert.equal(result.uaOS, null)
assert.equal(result.uaDeviceType, null)
assert.equal(log.info.callCount, 1)
var args = log.info.args[0]
assert.equal(args.length, 1)
assert.deepEqual(args[0], {
op: 'userAgent:truncate',
userAgent: 'wibble'
})
uaParser.parse.reset()
log.info.reset()
}
var context = {}
var result = userAgent.call(context, log)
)
t.equal(result.uaBrowserVersion, '1.1')
t.equal(result.uaOSVersion, '2.34567')
t.equal(log.info.callCount, 0)
t.end()
uaParser.parse.reset()
}
)
test(
'recognises Android phones as a mobile OS',
function (t) {
parserResult = {
ua: {
family: 'foo',
major: '1',
minor: '0'
},
os: {
family: 'Android',
major: '2',
minor: '0'
},
device: {
family: 'Other'
it(
'appends minor version if set',
() => {
parserResult = {
ua: {
family: 'foo',
major: '1',
minor: '1'
},
os: {
family: 'bar',
major: '2',
minor: '34567'
},
device: {
family: 'baz'
}
}
var context = {}
var result = userAgent.call(context, log)
assert.equal(result.uaBrowserVersion, '1.1')
assert.equal(result.uaOSVersion, '2.34567')
assert.equal(log.info.callCount, 0)
uaParser.parse.reset()
}
var context = {}
var result = userAgent.call(context, log)
)
t.equal(result.uaDeviceType, 'mobile')
t.equal(log.info.callCount, 0)
t.end()
uaParser.parse.reset()
}
)
test(
'recognises iOS as a mobile OS',
function (t) {
parserResult = {
ua: {
family: 'foo',
major: '1',
minor: '0'
},
os: {
family: 'iOS',
major: '2',
minor: '0'
},
device: {
family: 'Other'
it(
'recognises Android phones as a mobile OS',
() => {
parserResult = {
ua: {
family: 'foo',
major: '1',
minor: '0'
},
os: {
family: 'Android',
major: '2',
minor: '0'
},
device: {
family: 'Other'
}
}
var context = {}
var result = userAgent.call(context, log)
assert.equal(result.uaDeviceType, 'mobile')
assert.equal(log.info.callCount, 0)
uaParser.parse.reset()
}
var context = {}
var result = userAgent.call(context, log)
)
t.equal(result.uaDeviceType, 'mobile')
t.equal(log.info.callCount, 0)
t.end()
uaParser.parse.reset()
}
)
test(
'recognises Firefox OS as a mobile OS',
function (t) {
parserResult = {
ua: {
family: 'foo',
major: '1',
minor: '0'
},
os: {
family: 'Firefox OS',
major: '2',
minor: '0'
},
device: {
family: 'Other'
it(
'recognises iOS as a mobile OS',
() => {
parserResult = {
ua: {
family: 'foo',
major: '1',
minor: '0'
},
os: {
family: 'iOS',
major: '2',
minor: '0'
},
device: {
family: 'Other'
}
}
var context = {}
var result = userAgent.call(context, log)
assert.equal(result.uaDeviceType, 'mobile')
assert.equal(log.info.callCount, 0)
uaParser.parse.reset()
}
var context = {}
var result = userAgent.call(context, log)
)
t.equal(result.uaDeviceType, 'mobile')
t.equal(log.info.callCount, 0)
t.end()
uaParser.parse.reset()
}
)
test(
'recognises Windows Phone as a mobile OS',
function (t) {
parserResult = {
ua: {
family: 'foo',
major: '1',
minor: '0'
},
os: {
family: 'Windows Phone',
major: '2',
minor: '0'
},
device: {
family: 'Other'
it(
'recognises Firefox OS as a mobile OS',
() => {
parserResult = {
ua: {
family: 'foo',
major: '1',
minor: '0'
},
os: {
family: 'Firefox OS',
major: '2',
minor: '0'
},
device: {
family: 'Other'
}
}
var context = {}
var result = userAgent.call(context, log)
assert.equal(result.uaDeviceType, 'mobile')
assert.equal(log.info.callCount, 0)
uaParser.parse.reset()
}
var context = {}
var result = userAgent.call(context, log)
)
t.equal(result.uaDeviceType, 'mobile')
t.equal(log.info.callCount, 0)
t.end()
uaParser.parse.reset()
}
)
test(
'recognises BlackBerry OS as a mobile OS',
function (t) {
parserResult = {
ua: {
family: 'foo',
major: '1',
minor: '0'
},
os: {
family: 'BlackBerry OS',
major: '2',
minor: '0'
},
device: {
family: 'Other'
it(
'recognises Windows Phone as a mobile OS',
() => {
parserResult = {
ua: {
family: 'foo',
major: '1',
minor: '0'
},
os: {
family: 'Windows Phone',
major: '2',
minor: '0'
},
device: {
family: 'Other'
}
}
var context = {}
var result = userAgent.call(context, log)
assert.equal(result.uaDeviceType, 'mobile')
assert.equal(log.info.callCount, 0)
uaParser.parse.reset()
}
var context = {}
var result = userAgent.call(context, log)
)
t.equal(result.uaDeviceType, 'mobile')
t.equal(log.info.callCount, 0)
t.end()
uaParser.parse.reset()
}
)
test(
'does not recognise Mac OS X as a mobile OS',
function (t) {
parserResult = {
ua: {
family: 'foo',
major: '1',
minor: '0'
},
os: {
family: 'Mac OS X',
major: '2',
minor: '0'
},
device: {
family: 'Other'
it(
'recognises BlackBerry OS as a mobile OS',
() => {
parserResult = {
ua: {
family: 'foo',
major: '1',
minor: '0'
},
os: {
family: 'BlackBerry OS',
major: '2',
minor: '0'
},
device: {
family: 'Other'
}
}
var context = {}
var result = userAgent.call(context, log)
assert.equal(result.uaDeviceType, 'mobile')
assert.equal(log.info.callCount, 0)
uaParser.parse.reset()
}
var context = {}
var result = userAgent.call(context, log)
)
t.equal(result.uaDeviceType, null)
t.equal(log.info.callCount, 0)
t.end()
uaParser.parse.reset()
}
)
test(
'does not recognise Linux as a mobile OS',
function (t) {
parserResult = {
ua: {
family: 'foo',
major: '1',
minor: '0'
},
os: {
family: 'Linux',
major: '2',
minor: '0'
},
device: {
family: 'Other'
it(
'does not recognise Mac OS X as a mobile OS',
() => {
parserResult = {
ua: {
family: 'foo',
major: '1',
minor: '0'
},
os: {
family: 'Mac OS X',
major: '2',
minor: '0'
},
device: {
family: 'Other'
}
}
var context = {}
var result = userAgent.call(context, log)
assert.equal(result.uaDeviceType, null)
assert.equal(log.info.callCount, 0)
uaParser.parse.reset()
}
var context = {}
var result = userAgent.call(context, log)
)
t.equal(result.uaDeviceType, null)
t.equal(log.info.callCount, 0)
t.end()
uaParser.parse.reset()
}
)
test(
'does not recognise Windows as a mobile OS',
function (t) {
parserResult = {
ua: {
family: 'foo',
major: '1',
minor: '0'
},
os: {
family: 'Windows',
major: '2',
minor: '0'
},
device: {
family: 'Other'
it(
'does not recognise Linux as a mobile OS',
() => {
parserResult = {
ua: {
family: 'foo',
major: '1',
minor: '0'
},
os: {
family: 'Linux',
major: '2',
minor: '0'
},
device: {
family: 'Other'
}
}
var context = {}
var result = userAgent.call(context, log)
assert.equal(result.uaDeviceType, null)
assert.equal(log.info.callCount, 0)
uaParser.parse.reset()
}
var context = {}
var result = userAgent.call(context, log)
)
t.equal(result.uaDeviceType, null)
t.equal(log.info.callCount, 0)
t.end()
uaParser.parse.reset()
}
)
test(
'recognises iPads as tablets',
function (t) {
parserResult = {
userAgent: 'Mozilla/5.0 (iPad; CPU OS 7_0 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Mobile/11A465',
ua: {
family: 'Mobile Safari UI/WKWebView',
major: '7',
minor: '0'
},
os: {
family: 'iOS',
major: '7',
minor: '0'
},
device: {
family: 'iPad'
it(
'does not recognise Windows as a mobile OS',
() => {
parserResult = {
ua: {
family: 'foo',
major: '1',
minor: '0'
},
os: {
family: 'Windows',
major: '2',
minor: '0'
},
device: {
family: 'Other'
}
}
var context = {}
var result = userAgent.call(context, log)
assert.equal(result.uaDeviceType, null)
assert.equal(log.info.callCount, 0)
uaParser.parse.reset()
}
var context = {}
var result = userAgent.call(context, log)
)
t.equal(result.uaDeviceType, 'tablet')
t.end()
uaParser.parse.reset()
}
)
test(
'recognises Android tablets as tablets',
function (t) {
parserResult = {
userAgent: 'Mozilla/5.0 (Linux; Android 4.4.2; Nexus 7 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.59 Safari/537.36',
ua: {
family: 'Chrome Mobile',
major: '31',
minor: '0'
},
os: {
family: 'Android',
major: '4',
minor: '4'
},
device: {
family: 'Nexus 7'
it(
'recognises iPads as tablets',
() => {
parserResult = {
userAgent: 'Mozilla/5.0 (iPad; CPU OS 7_0 like Mac OS X) AppleWebKit/537.51.1 (KHTML, like Gecko) Mobile/11A465',
ua: {
family: 'Mobile Safari UI/WKWebView',
major: '7',
minor: '0'
},
os: {
family: 'iOS',
major: '7',
minor: '0'
},
device: {
family: 'iPad'
}
}
var context = {}
var result = userAgent.call(context, log)
assert.equal(result.uaDeviceType, 'tablet')
uaParser.parse.reset()
}
var context = {}
var result = userAgent.call(context, log)
)
t.equal(result.uaDeviceType, 'tablet')
t.end()
uaParser.parse.reset()
}
)
test(
'uaBrowser falls back to truncated user agent string',
function (t) {
parserResult = {
ua: {
family: 'Other'
},
os: {
family: 'Other'
},
device: {
family: 'Other'
it(
'recognises Android tablets as tablets',
() => {
parserResult = {
userAgent: 'Mozilla/5.0 (Linux; Android 4.4.2; Nexus 7 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.59 Safari/537.36',
ua: {
family: 'Chrome Mobile',
major: '31',
minor: '0'
},
os: {
family: 'Android',
major: '4',
minor: '4'
},
device: {
family: 'Nexus 7'
}
}
var context = {}
var result = userAgent.call(context, log)
assert.equal(result.uaDeviceType, 'tablet')
uaParser.parse.reset()
}
var context = {}
var userAgentString = new Array(201).join('x')
var result = userAgent.call(context, userAgentString, log)
)
t.equal(result.uaBrowser, new Array(61).join('x') + ELLIPSIS)
t.equal(log.info.callCount, 1)
var args = log.info.args[0]
t.equal(args.length, 1)
t.deepEqual(args[0], {
op: 'userAgent:truncate',
userAgent: userAgentString
})
t.end()
uaParser.parse.reset()
log.info.reset()
}
)
test(
'truncated fallback is relaxed for parentheses',
function (t) {
parserResult = {
ua: {
family: 'Other'
},
os: {
family: 'Other'
},
device: {
family: 'Other'
it(
'uaBrowser falls back to truncated user agent string',
() => {
parserResult = {
ua: {
family: 'Other'
},
os: {
family: 'Other'
},
device: {
family: 'Other'
}
}
var context = {}
var userAgentString = new Array(201).join('x')
var result = userAgent.call(context, userAgentString, log)
assert.equal(result.uaBrowser, new Array(61).join('x') + ELLIPSIS)
assert.equal(log.info.callCount, 1)
var args = log.info.args[0]
assert.equal(args.length, 1)
assert.deepEqual(args[0], {
op: 'userAgent:truncate',
userAgent: userAgentString
})
uaParser.parse.reset()
log.info.reset()
}
var context = {}
var expected = new Array(11).join('x') + ' (' + new Array(61).join('y') + ')'
var userAgentString = expected + new Array(101).join('z')
var result = userAgent.call(context, userAgentString, log)
)
t.equal(result.uaBrowser, expected + ELLIPSIS)
it(
'truncated fallback is relaxed for parentheses',
() => {
parserResult = {
ua: {
family: 'Other'
},
os: {
family: 'Other'
},
device: {
family: 'Other'
}
}
var context = {}
var expected = new Array(11).join('x') + ' (' + new Array(61).join('y') + ')'
var userAgentString = expected + new Array(101).join('z')
var result = userAgent.call(context, userAgentString, log)
t.equal(log.info.callCount, 1)
var args = log.info.args[0]
t.equal(args.length, 1)
t.deepEqual(args[0], {
op: 'userAgent:truncate',
userAgent: userAgentString
})
assert.equal(result.uaBrowser, expected + ELLIPSIS)
t.end()
uaParser.parse.reset()
log.info.reset()
}
)
assert.equal(log.info.callCount, 1)
var args = log.info.args[0]
assert.equal(args.length, 1)
assert.deepEqual(args[0], {
op: 'userAgent:truncate',
userAgent: userAgentString
})
uaParser.parse.reset()
log.info.reset()
}
)
})

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

@ -2,11 +2,12 @@
* 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 tap = require('tap')
'use strict'
const assert = require('insist')
var proxyquire = require('proxyquire')
var uuid = require('uuid')
var test = tap.test
var P = require('../../lib/promise')
var mockLog = require('../mocks').mockLog
@ -29,109 +30,115 @@ var mockDb = {
}
}
test(
'creates reminders with valid options and rate',
function (t) {
var moduleMocks = {
'../config': {
'get': function (item) {
if (item === 'verificationReminders') {
return {
rate: 1
describe('verification reminders', () => {
it(
'creates reminders with valid options and rate',
() => {
var moduleMocks = {
'../config': {
'get': function (item) {
if (item === 'verificationReminders') {
return {
rate: 1
}
}
}
}
}
}
var addedTimes = 0
var thisMockLog = mockLog({
increment: function (name) {
if (name === 'verification-reminders.created') {
addedTimes++
if (addedTimes === 5) {
t.end()
var addedTimes = 0
var thisMockLog = mockLog({
increment: function (name) {
if (name === 'verification-reminders.created') {
addedTimes++
}
}
}
})
var verificationReminder = proxyquire('../../lib/verification-reminders', moduleMocks)(thisMockLog, mockDb)
verificationReminder.create(reminderData)
verificationReminder.create(reminderData)
verificationReminder.create(reminderData)
verificationReminder.create(reminderData)
verificationReminder.create(reminderData)
}
)
test(
'does not create reminders when rate is 0',
function (t) {
var moduleMocks = {
'../config': {
'get': function (item) {
if (item === 'verificationReminders') {
return {
rate: 0
}
}
}
}
}
var verificationReminder = proxyquire('../../lib/verification-reminders', moduleMocks)(mockLog, mockDb)
verificationReminder.create(reminderData)
.then(function (result) {
if (result === false) {
t.end()
}
})
}
)
test(
'deletes reminders',
function (t) {
var thisMockLog = mockLog({
increment: function (name) {
if (name === 'verification-reminders.deleted') {
t.end()
var verificationReminder = proxyquire('../../lib/verification-reminders', moduleMocks)(thisMockLog, mockDb)
return P.all([
verificationReminder.create(reminderData),
verificationReminder.create(reminderData),
verificationReminder.create(reminderData),
verificationReminder.create(reminderData),
verificationReminder.create(reminderData)
]).then(() => {
assert.equal(addedTimes, 5)
})
}
)
it(
'does not create reminders when rate is 0',
() => {
var moduleMocks = {
'../config': {
'get': function (item) {
if (item === 'verificationReminders') {
return {
rate: 0
}
}
}
}
}
})
var thisMockDb = {
deleteVerificationReminder: function (reminderData) {
t.ok(reminderData.email)
t.ok(reminderData.type)
return P.resolve()
}
var verificationReminder = proxyquire('../../lib/verification-reminders', moduleMocks)(mockLog, mockDb)
verificationReminder.create(reminderData)
.then(function (result) {
assert.equal(result, false)
})
}
)
var verificationReminder = proxyquire('../../lib/verification-reminders', {})(thisMockLog, thisMockDb)
verificationReminder.delete(reminderData)
}
)
it(
'deletes reminders',
() => {
let count = 0
var thisMockLog = mockLog({
increment: function (name) {
if (name === 'verification-reminders.deleted') {
count++
}
}
})
var thisMockDb = {
deleteVerificationReminder: function (reminderData) {
assert.ok(reminderData.email)
assert.ok(reminderData.type)
return P.resolve()
}
}
test(
'deletes reminders can catch errors',
function (t) {
var thisMockLog = mockLog({
error: function (logErr) {
t.equal(logErr.op, 'verification-reminder.delete')
t.ok(logErr.err.message)
t.end()
}
})
var thisMockDb = {
deleteVerificationReminder: function () {
return P.reject(new Error('Something is wrong'))
}
var verificationReminder = proxyquire('../../lib/verification-reminders', {})(thisMockLog, thisMockDb)
return verificationReminder.delete(reminderData).then(() => {
assert.equal(count, 1)
})
}
)
var verificationReminder = proxyquire('../../lib/verification-reminders', {})(thisMockLog, thisMockDb)
verificationReminder.delete(reminderData)
}
)
it(
'deletes reminders can catch errors',
() => {
let count = 0
var thisMockLog = mockLog({
error: function (logErr) {
assert.equal(logErr.op, 'verification-reminder.delete')
assert.ok(logErr.err.message)
count++
}
})
var thisMockDb = {
deleteVerificationReminder: function () {
return P.reject(new Error('Something is wrong'))
}
}
var verificationReminder = proxyquire('../../lib/verification-reminders', {})(thisMockLog, thisMockDb)
return verificationReminder.delete(reminderData).then(() => {
assert.equal(count, 1)
})
}
)
})

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

@ -7,13 +7,19 @@ var request = require('request')
const EventEmitter = require('events').EventEmitter
/* eslint-disable no-console */
module.exports = function (host, port) {
module.exports = function (host, port, printLogs) {
host = host || '127.0.0.1'
port = port || 9001
const eventEmitter = new EventEmitter()
function log() {
if (printLogs) {
console.log.apply(console, arguments)
}
}
function waitForCode(email) {
return waitForEmail(email)
.then(
@ -26,10 +32,11 @@ module.exports = function (host, port) {
function loop(name, tries, cb) {
var url = 'http://' + host + ':' + port + '/mail/' + encodeURIComponent(name)
console.log('checking mail', url)
log('checking mail', url)
request({ url: url, method: 'GET' },
function (err, res, body) {
console.log('mail status', res && res.statusCode, 'tries', tries)
log('mail status', res && res.statusCode, 'tries', tries)
log('mail body', body)
var json = null
try {
json = JSON.parse(body)[0]
@ -44,7 +51,7 @@ module.exports = function (host, port) {
}
return setTimeout(loop.bind(null, name, --tries, cb), 1000)
}
console.log('deleting mail', url)
log('deleting mail', url)
request({ url: url, method: 'DELETE' },
function (err, res, body) {
cb(err, json)

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

@ -26,4 +26,12 @@ api.route(
]
)
api.start()
process.on('SIGINT', () => {
process.exit(0)
})
api.start((err) => {
if (err) {
console.log(err) // eslint-disable-line no-console
}
})

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

@ -1,57 +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/. */
/*
* A promise-ified version of tap.test.
*
* This module provides a 'test' function that operates just like tap.test, but
* will properly close a promise if the test returns one. This makes it easier
* to ensure that any unhandled errors cause the test to fail. Use like so:
*
* var test = require('./ptap')
*
* test(
* 'an example test',
* function (t) {
* return someAPI.thingThatReturnsPromise()
* .then(function(result) {
* t.assertEqual(result, 42)
* })
* }
* )
*
* Because the test function returns a promise, we get the following for free:
*
* * wait for the promise to resolve, and call t.end() when it does
* * check for unhandled errors and fail the test if they occur
*
*/
/* eslint-disable no-console */
var tap = require('tap')
module.exports = function(name, testfunc, parentTest) {
var t = parentTest || tap
if (!testfunc) {
return t.test(name)
}
var wrappedtestfunc = function(t) {
var res = testfunc(t)
if (typeof res !== 'undefined') {
if (typeof res.done === 'function') {
res.done(
function() {
t.end()
},
function(err) {
console.error(err.stack)
t.fail(err.message || err.error || err)
t.end()
}
)
}
}
}
return t.test(name, wrappedtestfunc)
}

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

@ -2,20 +2,29 @@
* 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('tap').test
'use strict'
const assert = require('insist')
var TestServer = require('../test_server')
var crypto = require('crypto')
const Client = require('../client')()
var config = require('../../config').getProperties()
// XXX: update this later to avoid issues.
process.env.NODE_ENV = 'dev'
TestServer.start(config)
.then(function main(server) {
describe('remote account create', function() {
this.timeout(15000)
let server
before(() => {
// XXX: update this later to avoid issues.
process.env.NODE_ENV = 'dev'
return TestServer.start(config)
.then(s => {
server = s
})
})
test(
it(
'unverified account fail when getting keys',
function (t) {
() => {
var email = server.uniqueEmail()
var password = 'allyourbasearebelongtous'
var client = null
@ -23,7 +32,7 @@ TestServer.start(config)
.then(
function (x) {
client = x
t.ok(client.authAt, 'authAt was set')
assert.ok(client.authAt, 'authAt was set')
}
)
.then(
@ -33,19 +42,19 @@ TestServer.start(config)
)
.then(
function (keys) {
t.fail('got keys before verifying email')
assert(false, 'got keys before verifying email')
},
function (err) {
t.equal(err.errno, 104, 'Unverified account error code')
t.equal(err.message, 'Unverified account', 'Unverified account error message')
assert.equal(err.errno, 104, 'Unverified account error code')
assert.equal(err.message, 'Unverified account', 'Unverified account error message')
}
)
}
)
test(
it(
'create and verify account',
function (t) {
() => {
var email = server.uniqueEmail()
var password = 'allyourbasearebelongtous'
var client = null
@ -53,7 +62,7 @@ TestServer.start(config)
.then(
function (x) {
client = x
t.ok(client.authAt, 'authAt was set')
assert.ok(client.authAt, 'authAt was set')
}
)
.then(
@ -63,7 +72,7 @@ TestServer.start(config)
)
.then(
function (status) {
t.equal(status.verified, false)
assert.equal(status.verified, false)
}
)
.then(
@ -83,7 +92,7 @@ TestServer.start(config)
)
.then(
function (emailData) {
t.equal(
assert.equal(
emailData.headers['x-link'].indexOf(config.smtp.syncUrl),
0,
'sync url present')
@ -96,16 +105,16 @@ TestServer.start(config)
)
.then(
function (status) {
t.equal(status.verified, true)
assert.equal(status.verified, true)
}
)
}
)
test(
it(
'create account with service identifier and resume',
function (t) {
() => {
var email = server.uniqueEmail()
var password = 'allyourbasearebelongtous'
var client = null // eslint-disable-line no-unused-vars
@ -123,16 +132,16 @@ TestServer.start(config)
)
.then(
function (emailData) {
t.equal(emailData.headers['x-service-id'], 'abcdef')
t.ok(emailData.headers['x-link'].indexOf('resume=foo') > -1)
assert.equal(emailData.headers['x-service-id'], 'abcdef')
assert.ok(emailData.headers['x-link'].indexOf('resume=foo') > -1)
}
)
}
)
test(
it(
'create account allows localization of emails',
function (t) {
() => {
var email = server.uniqueEmail()
var password = 'allyourbasearebelongtous'
var client = null
@ -149,8 +158,8 @@ TestServer.start(config)
)
.then(
function (emailData) {
t.assert(emailData.text.indexOf('Activate now') !== -1, 'not en-US')
t.assert(emailData.text.indexOf('Ativar agora') === -1, 'not pt-BR')
assert(emailData.text.indexOf('Activate now') !== -1, 'not en-US')
assert(emailData.text.indexOf('Ativar agora') === -1, 'not pt-BR')
return client.destroyAccount()
}
)
@ -171,35 +180,35 @@ TestServer.start(config)
)
.then(
function (emailData) {
t.assert(emailData.text.indexOf('Activate now') === -1, 'not en-US')
t.assert(emailData.text.indexOf('Ativar agora') !== -1, 'is pt-BR')
assert(emailData.text.indexOf('Activate now') === -1, 'not en-US')
assert(emailData.text.indexOf('Ativar agora') !== -1, 'is pt-BR')
return client.destroyAccount()
}
)
}
)
test(
it(
'Unknown account should not exist',
function (t) {
() => {
var client = new Client(config.publicUrl)
client.email = server.uniqueEmail()
client.authPW = crypto.randomBytes(32)
return client.auth()
.then(
function () {
t.fail('account should not exist')
assert(false, 'account should not exist')
},
function (err) {
t.equal(err.errno, 102, 'account does not exist')
assert.equal(err.errno, 102, 'account does not exist')
}
)
}
)
test(
it(
'/account/create works with proper data',
function (t) {
() => {
var email = server.uniqueEmail()
var password = 'ilikepancakes'
var client
@ -207,7 +216,7 @@ TestServer.start(config)
.then(
function (x) {
client = x
t.ok(client.uid, 'account created')
assert.ok(client.uid, 'account created')
}
).then(
function () {
@ -215,15 +224,15 @@ TestServer.start(config)
}
).then(
function () {
t.ok(client.sessionToken, 'client can login')
assert.ok(client.sessionToken, 'client can login')
}
)
}
)
test(
it(
'/account/create returns a sessionToken',
function (t) {
() => {
var email = server.uniqueEmail()
var password = 'ilikepancakes'
var client = new Client(config.publicUrl)
@ -233,8 +242,8 @@ TestServer.start(config)
return c.api.accountCreate(c.email, c.authPW)
.then(
function (response) {
t.ok(response.sessionToken, 'has a sessionToken')
t.equal(response.keyFetchToken, undefined, 'no keyFetchToken without keys=true')
assert.ok(response.sessionToken, 'has a sessionToken')
assert.equal(response.keyFetchToken, undefined, 'no keyFetchToken without keys=true')
}
)
}
@ -242,9 +251,9 @@ TestServer.start(config)
}
)
test(
it(
'/account/create returns a keyFetchToken when keys=true',
function (t) {
() => {
var email = server.uniqueEmail()
var password = 'ilikepancakes'
var client = new Client(config.publicUrl)
@ -254,8 +263,8 @@ TestServer.start(config)
return c.api.accountCreate(c.email, c.authPW, { keys: true })
.then(
function (response) {
t.ok(response.sessionToken, 'has a sessionToken')
t.ok(response.keyFetchToken, 'keyFetchToken with keys=true')
assert.ok(response.sessionToken, 'has a sessionToken')
assert.ok(response.keyFetchToken, 'keyFetchToken with keys=true')
}
)
}
@ -263,9 +272,9 @@ TestServer.start(config)
}
)
test(
it(
'signup with same email, different case',
function (t) {
() => {
var email = server.uniqueEmail()
var email2 = email.toUpperCase()
var password = 'abcdef'
@ -276,18 +285,18 @@ TestServer.start(config)
}
)
.then(
t.fail,
assert.fail,
function (err) {
t.equal(err.code, 400)
t.equal(err.errno, 101, 'Account already exists')
assert.equal(err.code, 400)
assert.equal(err.errno, 101, 'Account already exists')
}
)
}
)
test(
it(
're-signup against an unverified email',
function (t) {
() => {
var email = server.uniqueEmail()
var password = 'abcdef'
return Client.create(config.publicUrl, email, password)
@ -304,15 +313,15 @@ TestServer.start(config)
)
.then(
function (client) {
t.ok(client.uid, 'account created')
assert.ok(client.uid, 'account created')
}
)
}
)
test(
it(
'invalid redirectTo',
function (t) {
() => {
var api = new Client.Api(config.publicUrl)
var email = server.uniqueEmail()
var authPW = '0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF'
@ -321,9 +330,9 @@ TestServer.start(config)
}
return api.accountCreate(email, authPW, options)
.then(
t.fail,
assert.fail,
function (err) {
t.equal(err.errno, 107, 'bad redirectTo rejected')
assert.equal(err.errno, 107, 'bad redirectTo rejected')
}
)
.then(
@ -332,17 +341,17 @@ TestServer.start(config)
}
)
.then(
t.fail,
assert.fail,
function (err) {
t.equal(err.errno, 107, 'bad redirectTo rejected')
assert.equal(err.errno, 107, 'bad redirectTo rejected')
}
)
}
)
test(
it(
'another invalid redirectTo',
function (t) {
() => {
var api = new Client.Api(config.publicUrl)
var email = server.uniqueEmail()
var authPW = '0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF'
@ -352,9 +361,9 @@ TestServer.start(config)
return api.accountCreate(email, authPW, options)
.then(
t.fail,
assert.fail,
function (err) {
t.equal(err.errno, 107, 'bad redirectTo rejected')
assert.equal(err.errno, 107, 'bad redirectTo rejected')
}
)
.then(
@ -365,46 +374,46 @@ TestServer.start(config)
}
)
.then(
t.fail,
assert.fail,
function (err) {
t.equal(err.errno, 107, 'bad redirectTo rejected')
assert.equal(err.errno, 107, 'bad redirectTo rejected')
}
)
}
)
test(
it(
'create account with service query parameter',
function (t) {
() => {
var email = server.uniqueEmail()
return Client.create(config.publicUrl, email, 'foo', { serviceQuery: 'bar' })
.then(function () {
return server.mailbox.waitForEmail(email)
})
.then(function (emailData) {
t.equal(emailData.headers['x-service-id'], 'bar', 'service query parameter was propagated')
assert.equal(emailData.headers['x-service-id'], 'bar', 'service query parameter was propagated')
})
}
)
test(
it(
'account creation works with unicode email address',
function (t) {
() => {
var email = server.uniqueUnicodeEmail()
return Client.create(config.publicUrl, email, 'foo')
.then(function (client) {
t.ok(client, 'created account')
assert.ok(client, 'created account')
return server.mailbox.waitForEmail(email)
})
.then(function (emailData) {
t.ok(emailData, 'received email')
assert.ok(emailData, 'received email')
})
}
)
test(
it(
'account creation works with minimal metricsContext metadata',
function (t) {
() => {
var email = server.uniqueEmail()
return Client.create(config.publicUrl, email, 'foo', {
metricsContext: {
@ -412,14 +421,14 @@ TestServer.start(config)
flowBeginTime: 1
}
}).then(function (client) {
t.ok(client, 'created account')
assert.ok(client, 'created account')
})
}
)
test(
it(
'account creation fails with invalid metricsContext flowId',
function (t) {
() => {
var email = server.uniqueEmail()
return Client.create(config.publicUrl, email, 'foo', {
metricsContext: {
@ -427,16 +436,16 @@ TestServer.start(config)
flowBeginTime: 1
}
}).then(function () {
t.fail('account creation should have failed')
assert(false, 'account creation should have failed')
}, function (err) {
t.ok(err, 'account creation failed')
assert.ok(err, 'account creation failed')
})
}
)
test(
it(
'account creation fails with invalid metricsContext flowBeginTime',
function (t) {
() => {
var email = server.uniqueEmail()
return Client.create(config.publicUrl, email, 'foo', {
metricsContext: {
@ -444,16 +453,16 @@ TestServer.start(config)
flowBeginTime: 0
}
}).then(function () {
t.fail('account creation should have failed')
assert(false, 'account creation should have failed')
}, function (err) {
t.ok(err, 'account creation failed')
assert.ok(err, 'account creation failed')
})
}
)
test(
it(
'account creation works with maximal metricsContext metadata',
function (t) {
() => {
var email = server.uniqueEmail()
return Client.create(config.publicUrl, email, 'foo', {
metricsContext: {
@ -470,58 +479,58 @@ TestServer.start(config)
utmTerm: 'frang'
}
}).then(function (client) {
t.ok(client, 'created account')
assert.ok(client, 'created account')
})
}
)
test(
it(
'account creation works with empty metricsContext metadata',
function (t) {
() => {
var email = server.uniqueEmail()
return Client.create(config.publicUrl, email, 'foo', {
metricsContext: {}
}).then(function (client) {
t.ok(client, 'created account')
assert.ok(client, 'created account')
})
}
)
test(
it(
'account creation fails with missing flowBeginTime',
function (t) {
() => {
var email = server.uniqueEmail()
return Client.create(config.publicUrl, email, 'foo', {
metricsContext: {
flowId: 'deadbeefbaadf00ddeadbeefbaadf00ddeadbeefbaadf00ddeadbeefbaadf00d'
}
}).then(function () {
t.fail('account creation should have failed')
assert(false, 'account creation should have failed')
}, function (err) {
t.ok(err, 'account creation failed')
assert.ok(err, 'account creation failed')
})
}
)
test(
it(
'account creation fails with missing flowId',
function (t) {
() => {
var email = server.uniqueEmail()
return Client.create(config.publicUrl, email, 'foo', {
metricsContext: {
flowBeginTime: Date.now()
}
}).then(function () {
t.fail('account creation should have failed')
assert(false, 'account creation should have failed')
}, function (err) {
t.ok(err, 'account creation failed')
assert.ok(err, 'account creation failed')
})
}
)
test(
it(
'create account for non-sync service does not get post-verify email',
function (t) {
() => {
var email = server.uniqueEmail()
var password = 'allyourbasearebelongtous'
var client = null
@ -529,7 +538,7 @@ TestServer.start(config)
.then(
function (x) {
client = x
t.ok('account was created')
assert.ok('account was created')
}
)
.then(
@ -549,7 +558,7 @@ TestServer.start(config)
)
.then(
function (status) {
t.equal(status.verified, true)
assert.equal(status.verified, true)
}
)
.then(
@ -567,15 +576,15 @@ TestServer.start(config)
)
.then(
function (code) {
t.ok(code, 'the next email was reset-password, not post-verify')
assert.ok(code, 'the next email was reset-password, not post-verify')
}
)
}
)
test(
it(
'create account for unspecified service does not get post-verify email',
function (t) {
() => {
var email = server.uniqueEmail()
var password = 'allyourbasearebelongtous'
var client = null
@ -583,7 +592,7 @@ TestServer.start(config)
.then(
function (x) {
client = x
t.ok('account was created')
assert.ok('account was created')
}
)
.then(
@ -603,7 +612,7 @@ TestServer.start(config)
)
.then(
function (status) {
t.equal(status.verified, true)
assert.equal(status.verified, true)
}
)
.then(
@ -621,17 +630,13 @@ TestServer.start(config)
)
.then(
function (code) {
t.ok(code, 'the next email was reset-password, not post-verify')
assert.ok(code, 'the next email was reset-password, not post-verify')
}
)
}
)
test(
'teardown',
function (t) {
server.stop()
t.end()
}
)
after(() => {
return TestServer.stop(server)
})
})

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

@ -2,18 +2,29 @@
* 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 TestServer = require('../test_server')
'use strict'
const assert = require('insist')
const TestServer = require('../test_server')
const Client = require('../client')()
var config = require('../../config').getProperties()
const config = require('../../config').getProperties()
TestServer.start(config)
.then(function main(server) {
describe('remote account destroy', () => {
test(
let server
before(function() {
this.timeout(15000)
return TestServer.start(config)
.then(s => {
server = s
})
})
it(
'account destroy',
function (t) {
() => {
var email = server.uniqueEmail()
var password = 'allyourbasearebelongtous'
var client = null
@ -36,18 +47,18 @@ TestServer.start(config)
)
.then(
function (keys) {
t.fail('account not destroyed')
assert(false, 'account not destroyed')
},
function (err) {
t.equal(err.message, 'Unknown account', 'account destroyed')
assert.equal(err.message, 'Unknown account', 'account destroyed')
}
)
}
)
test(
it(
'invalid authPW on account destroy',
function (t) {
() => {
var email = server.uniqueEmail()
var password = 'ok'
return Client.createAndVerify(config.publicUrl, email, password, server.mailbox)
@ -58,19 +69,17 @@ TestServer.start(config)
}
)
.then(
t.fail,
() => {
assert(false)
},
function (err) {
t.equal(err.errno, 103)
assert.equal(err.errno, 103)
}
)
}
)
test(
'teardown',
function (t) {
server.stop()
t.end()
}
)
after(() => {
return TestServer.stop(server)
})
})

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

@ -2,8 +2,10 @@
* 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 TestServer = require('../test_server')
'use strict'
const assert = require('insist')
const TestServer = require('../test_server')
const Client = require('../client')()
var config = require('../../config').getProperties()
@ -13,12 +15,20 @@ var key = {
'e': '65537'
}
TestServer.start(config)
.then(function main(server) {
describe('remote account locale', function() {
this.timeout(15000)
test(
let server
before(() => {
return TestServer.start(config)
.then(s => {
server = s
})
})
it(
'signing a cert against an account with no locale will save the locale',
function (t) {
() => {
var email = server.uniqueEmail()
var password = 'ilikepancakes'
var client
@ -31,7 +41,7 @@ TestServer.start(config)
)
.then(
function (response) {
t.ok(!response.locale, 'account has no locale')
assert.ok(!response.locale, 'account has no locale')
return client.login()
}
)
@ -52,15 +62,15 @@ TestServer.start(config)
)
.then(
function (response) {
t.equal(response.locale, 'en-US', 'account has a locale')
assert.equal(response.locale, 'en-US', 'account has a locale')
}
)
}
)
test(
it(
'a really long (invalid) locale',
function (t) {
() => {
var email = server.uniqueEmail()
var password = 'ilikepancakes'
return Client.create(
@ -76,15 +86,15 @@ TestServer.start(config)
)
.then(
function (response) {
t.ok(!response.locale, 'account has no locale')
assert.ok(!response.locale, 'account has no locale')
}
)
}
)
test(
it(
'a really long (valid) locale',
function (t) {
() => {
var email = server.uniqueEmail()
var password = 'ilikepancakes'
return Client.create(
@ -100,17 +110,14 @@ TestServer.start(config)
)
.then(
function (response) {
t.equal(response.locale, 'en-US,en;q=0.8', 'account has no locale')
assert.equal(response.locale, 'en-US,en;q=0.8', 'account has no locale')
}
)
}
)
test(
'teardown',
function (t) {
server.stop()
t.end()
}
)
after(() => {
return TestServer.stop(server)
})
})

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

@ -2,19 +2,29 @@
* 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/. */
'use strict'
const assert = require('assert')
const Client = require('../client')()
var crypto = require('crypto')
var test = require('tap').test
var TestServer = require('../test_server')
var config = require('../../config').getProperties()
TestServer.start(config)
.then(function main(server) {
describe('remote account login', () => {
let server
test(
before(function() {
this.timeout(15000)
return TestServer.start(config)
.then(s => {
server = s
})
})
it(
'the email is returned in the error on Incorrect password errors',
function (t) {
() => {
var email = server.uniqueEmail()
var password = 'abcdef'
return Client.createAndVerify(config.publicUrl, email, password, server.mailbox)
@ -24,19 +34,19 @@ TestServer.start(config)
}
)
.then(
t.fail,
() => assert(false),
function (err) {
t.equal(err.code, 400)
t.equal(err.errno, 103)
t.equal(err.email, email)
assert.equal(err.code, 400)
assert.equal(err.errno, 103)
assert.equal(err.email, email)
}
)
}
)
test(
it(
'the email is returned in the error on Incorrect email case errors with correct password',
function (t) {
() => {
var signupEmail = server.uniqueEmail()
var loginEmail = signupEmail.toUpperCase()
var password = 'abcdef'
@ -47,37 +57,37 @@ TestServer.start(config)
}
)
.then(
t.fail,
() => assert(false),
function (err) {
t.equal(err.code, 400)
t.equal(err.errno, 120)
t.equal(err.email, signupEmail)
assert.equal(err.code, 400)
assert.equal(err.errno, 120)
assert.equal(err.email, signupEmail)
}
)
}
)
test(
it(
'Unknown account should not exist',
function (t) {
() => {
var client = new Client(config.publicUrl)
client.email = server.uniqueEmail()
client.authPW = crypto.randomBytes(32)
return client.login()
.then(
function () {
t.fail('account should not exist')
assert(false, 'account should not exist')
},
function (err) {
t.equal(err.errno, 102, 'account does not exist')
assert.equal(err.errno, 102, 'account does not exist')
}
)
}
)
test(
it(
'No keyFetchToken without keys=true',
function (t) {
() => {
var email = server.uniqueEmail()
var password = 'abcdef'
return Client.createAndVerify(config.publicUrl, email, password, server.mailbox)
@ -88,15 +98,15 @@ TestServer.start(config)
)
.then(
function (c) {
t.equal(c.keyFetchToken, null, 'should not have keyFetchToken')
assert.equal(c.keyFetchToken, null, 'should not have keyFetchToken')
}
)
}
)
test(
it(
'login works with unicode email address',
function (t) {
() => {
var email = server.uniqueUnicodeEmail()
var password = 'wibble'
return Client.createAndVerify(config.publicUrl, email, password, server.mailbox)
@ -107,15 +117,15 @@ TestServer.start(config)
)
.then(
function (client) {
t.ok(client, 'logged in to account')
assert.ok(client, 'logged in to account')
}
)
}
)
test(
it(
'account login works with minimal metricsContext metadata',
function (t) {
() => {
var email = server.uniqueEmail()
return Client.createAndVerify(config.publicUrl, email, 'foo', server.mailbox)
.then(function () {
@ -127,14 +137,14 @@ TestServer.start(config)
})
})
.then(function (client) {
t.ok(client, 'logged in to account')
assert.ok(client, 'logged in to account')
})
}
)
test(
it(
'account login fails with invalid metricsContext flowId',
function (t) {
() => {
var email = server.uniqueEmail()
return Client.createAndVerify(config.publicUrl, email, 'foo', server.mailbox)
.then(function () {
@ -146,16 +156,16 @@ TestServer.start(config)
})
})
.then(function () {
t.fail('account login should have failed')
assert(false, 'account login should have failed')
}, function (err) {
t.ok(err, 'account login failed')
assert.ok(err, 'account login failed')
})
}
)
test(
it(
'account login fails with invalid metricsContext flowBeginTime',
function (t) {
() => {
var email = server.uniqueEmail()
return Client.createAndVerify(config.publicUrl, email, 'foo', server.mailbox)
.then(function () {
@ -167,18 +177,14 @@ TestServer.start(config)
})
})
.then(function () {
t.fail('account login should have failed')
assert(false, 'account login should have failed')
}, function (err) {
t.ok(err, 'account login failed')
assert.ok(err, 'account login failed')
})
}
)
test(
'teardown',
function (t) {
server.stop()
t.end()
}
)
after(() => {
return TestServer.stop(server)
})
})

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

@ -2,15 +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/. */
var test = require('tap').test
'use strict'
const assert = require('insist')
var TestServer = require('../test_server')
const Client = require('../client')()
var JWTool = require('fxa-jwtool')
var config = require('../../config').getProperties()
process.env.TRUSTED_JKUS = 'http://127.0.0.1:9000/.well-known/public-keys'
process.env.SIGNIN_CONFIRMATION_ENABLED = false
var secretKey = JWTool.JWK.fromFile(
config.secretKeyFile,
{
@ -25,12 +24,22 @@ function nowSeconds() {
return Math.floor(Date.now() / 1000)
}
TestServer.start(config)
.then(function main(server) {
describe('remote account preverified token', function() {
this.timeout(15000)
let server
before(() => {
process.env.TRUSTED_JKUS = 'http://127.0.0.1:9000/.well-known/public-keys'
process.env.SIGNIN_CONFIRMATION_ENABLED = false
test(
return TestServer.start(config)
.then(s => {
server = s
})
})
it(
'a valid preVerifyToken creates a verified account',
function (t) {
() => {
var email = server.uniqueEmail()
var password = 'ok'
var token = secretKey.signSync(
@ -48,16 +57,16 @@ TestServer.start(config)
)
.then(
function (keys) {
t.ok(Buffer.isBuffer(keys.kA), 'kA exists')
t.ok(Buffer.isBuffer(keys.wrapKb), 'wrapKb exists')
assert.ok(Buffer.isBuffer(keys.kA), 'kA exists')
assert.ok(Buffer.isBuffer(keys.wrapKb), 'wrapKb exists')
}
)
}
)
test(
it(
'an invalid preVerifyToken return an invalid verification code error',
function (t) {
() => {
var email = server.uniqueEmail()
var password = 'ok'
var token = secretKey.signSync(
@ -71,15 +80,15 @@ TestServer.start(config)
.then(
fail,
function (err) {
t.equal(err.errno, 105, 'invalid verification code')
assert.equal(err.errno, 105, 'invalid verification code')
}
)
}
)
test(
it(
're-signup against an unverified email',
function (t) {
function() {
var email = server.uniqueEmail()
var password = 'abcdef'
return Client.create(config.publicUrl, email, password)
@ -103,24 +112,20 @@ TestServer.start(config)
)
.then(
function (client) {
t.ok(client.uid, 'account created')
assert.ok(client.uid, 'account created')
return client.keys()
}
)
.then(
function (keys) {
t.ok(Buffer.isBuffer(keys.kA), 'kA exists')
t.ok(Buffer.isBuffer(keys.wrapKb), 'wrapKb exists')
assert.ok(Buffer.isBuffer(keys.kA), 'kA exists')
assert.ok(Buffer.isBuffer(keys.wrapKb), 'wrapKb exists')
}
)
}
)
test(
'teardown',
function (t) {
server.stop()
t.end()
}
)
after(() => {
return TestServer.stop(server)
})
})

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

@ -2,12 +2,13 @@
* 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/. */
'use strict'
const assert = require('insist')
var path = require('path')
var test = require('../ptaptest')
var TestServer = require('../test_server')
const Client = require('../client')()
process.env.CONFIG_FILES = path.join(__dirname, '../config/mock_oauth.json')
var config = require('../../config').getProperties()
function makeMockOAuthHeader(opts) {
@ -15,12 +16,21 @@ function makeMockOAuthHeader(opts) {
return 'Bearer ' + token
}
TestServer.start(config)
.then(function main(server) {
describe('remote account profile', function() {
this.timeout(15000)
test(
let server
before(() => {
process.env.CONFIG_FILES = path.join(__dirname, '../config/mock_oauth.json')
return TestServer.start(config)
.then(s => {
server = s
})
})
it(
'account profile authenticated with session returns profile data',
function (t) {
() => {
return Client.create(config.publicUrl, server.uniqueEmail(), 'password', { lang: 'en-US' })
.then(
function (c) {
@ -29,16 +39,16 @@ TestServer.start(config)
)
.then(
function (response) {
t.ok(response.email, 'email address is returned')
t.equal(response.locale, 'en-US', 'locale is returned')
assert.ok(response.email, 'email address is returned')
assert.equal(response.locale, 'en-US', 'locale is returned')
}
)
}
)
test(
it(
'account profile authenticated with oauth returns profile data',
function (t) {
() => {
return Client.create(config.publicUrl, server.uniqueEmail(), 'password', { lang: 'en-US' })
.then(
function (c) {
@ -52,16 +62,16 @@ TestServer.start(config)
)
.then(
function (response) {
t.ok(response.email, 'email address is returned')
t.equal(response.locale, 'en-US', 'locale is returned')
assert.ok(response.email, 'email address is returned')
assert.equal(response.locale, 'en-US', 'locale is returned')
}
)
}
)
test(
it(
'account profile authenticated with invalid oauth token returns an error',
function (t) {
() => {
return Client.create(config.publicUrl, server.uniqueEmail(), 'password', { lang: 'en-US' })
.then(
function (c) {
@ -75,24 +85,24 @@ TestServer.start(config)
)
.then(
function () {
t.fail('should get an error')
assert(false, 'should get an error')
},
function (e) {
t.equal(e.code, 401, 'correct error status code')
t.equal(e.errno, 110, 'correct errno')
assert.equal(e.code, 401, 'correct error status code')
assert.equal(e.errno, 110, 'correct errno')
}
)
}
)
test(
it(
'account status authenticated with oauth for unknown uid returns an error',
function (t) {
() => {
return Client.create(config.publicUrl, server.uniqueEmail(), 'password', { lang: 'en-US' })
.then(
function (c) {
var UNKNOWN_UID = 'abcdef123456'
t.notEqual(c.uid, UNKNOWN_UID)
assert.notEqual(c.uid, UNKNOWN_UID)
return c.api.accountProfile(null, {
Authorization: makeMockOAuthHeader({
user: UNKNOWN_UID,
@ -103,19 +113,19 @@ TestServer.start(config)
)
.then(
function () {
t.fail('should get an error')
assert(false, 'should get an error')
},
function (e) {
t.equal(e.code, 400, 'correct error status code')
t.equal(e.errno, 102, 'correct errno')
assert.equal(e.code, 400, 'correct error status code')
assert.equal(e.errno, 102, 'correct errno')
}
)
}
)
test(
it(
'account status authenticated with oauth for wrong scope returns no info',
function (t) {
() => {
return Client.create(config.publicUrl, server.uniqueEmail(), 'password', { lang: 'en-US' })
.then(
function (c) {
@ -129,15 +139,15 @@ TestServer.start(config)
)
.then(
function (response) {
t.deepEqual(response, {}, 'no info should be returned')
assert.deepEqual(response, {}, 'no info should be returned')
}
)
}
)
test(
it(
'account profile authenticated with limited oauth scopes returns limited profile data',
function (t) {
() => {
var client
return Client.create(config.publicUrl, server.uniqueEmail(), 'password', { lang: 'en-US' })
.then(
@ -153,8 +163,8 @@ TestServer.start(config)
)
.then(
function (response) {
t.ok(response.email, 'email address is returned')
t.ok(!response.locale, 'locale should not be returned')
assert.ok(response.email, 'email address is returned')
assert.ok(!response.locale, 'locale should not be returned')
}
)
.then(
@ -169,16 +179,16 @@ TestServer.start(config)
)
.then(
function (response) {
t.ok(!response.email, 'email address should not be returned')
t.equal(response.locale, 'en-US', 'locale is returned')
assert.ok(!response.email, 'email address should not be returned')
assert.equal(response.locale, 'en-US', 'locale is returned')
}
)
}
)
test(
it(
'account profile authenticated with oauth :write scopes returns profile data',
function (t) {
() => {
var client
return Client.create(config.publicUrl, server.uniqueEmail(), 'password', { lang: 'en-US' })
.then(
@ -194,8 +204,8 @@ TestServer.start(config)
)
.then(
function (response) {
t.ok(response.email, 'email address is returned')
t.ok(response.locale, 'locale is returned')
assert.ok(response.email, 'email address is returned')
assert.ok(response.locale, 'locale is returned')
}
)
.then(
@ -210,8 +220,8 @@ TestServer.start(config)
)
.then(
function (response) {
t.ok(!response.email, 'email address should not be returned')
t.ok(response.locale, 'locale is returned')
assert.ok(!response.email, 'email address should not be returned')
assert.ok(response.locale, 'locale is returned')
}
)
.then(
@ -226,16 +236,16 @@ TestServer.start(config)
)
.then(
function (response) {
t.ok(response.email, 'email address is returned')
t.ok(!response.locale, 'locale should not be returned')
assert.ok(response.email, 'email address is returned')
assert.ok(!response.locale, 'locale should not be returned')
}
)
}
)
test(
it(
'account profile works with unicode email address',
function (t) {
() => {
var email = server.uniqueUnicodeEmail()
return Client.create(config.publicUrl, email, 'password')
.then(
@ -245,17 +255,14 @@ TestServer.start(config)
)
.then(
function (response) {
t.equal(response.email, email, 'email address is returned')
assert.equal(response.email, email, 'email address is returned')
}
)
}
)
test(
'teardown',
function (t) {
server.stop()
t.end()
}
)
after(() => {
delete process.env.CONFIG_FILES
return TestServer.stop(server)
})
})

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

@ -2,233 +2,239 @@
* 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('tap').test
'use strict'
const assert = require('insist')
var url = require('url')
const Client = require('../client')()
var TestServer = require('../test_server')
var config = require('../../config').getProperties()
process.env.SIGNIN_CONFIRMATION_ENABLED = false
TestServer.start(config)
.then(function main(server) {
test(
'account reset w/o sessionToken',
function (t) {
var email = server.uniqueEmail()
var password = 'allyourbasearebelongtous'
var newPassword = 'ez'
var wrapKb, kA, client
return Client.createAndVerify(config.publicUrl, email, password, server.mailbox, {keys:true})
.then(
function (x) {
client = x
}
)
.then(
function () {
return client.keys()
}
)
.then(
function (keys) {
wrapKb = keys.wrapKb
kA = keys.kA
return client.forgotPassword()
}
)
.then(
function () {
return server.mailbox.waitForCode(email)
}
)
.then(
function (code) {
t.throws(function() {
client.resetPassword(newPassword)
})
return resetPassword(client, code, newPassword, {sessionToken: false})
}
)
.then(
function (response) {
t.notOk(response.sessionToken, 'session token is not in response')
t.notOk(response.keyFetchToken, 'keyFetchToken token is not in response')
t.notOk(response.verified, 'verified is not in response')
}
)
.then(
function () {
return server.mailbox.waitForEmail(email)
}
)
.then(
function (emailData) {
var link = emailData.headers['x-link']
var query = url.parse(link, true).query
t.ok(query.email, 'email is in the link')
}
)
.then(
function () {
// make sure we can still login after password reset
return Client.login(config.publicUrl, email, newPassword, server.mailbox, {keys:true})
}
)
.then(
function (x) {
client = x
return client.keys()
}
)
.then(
function (keys) {
t.ok(Buffer.isBuffer(keys.wrapKb), 'yep, wrapKb')
t.notDeepEqual(wrapKb, keys.wrapKb, 'wrapKb was reset')
t.deepEqual(kA, keys.kA, 'kA was not reset')
t.equal(client.kB.length, 32, 'kB exists, has the right length')
}
)
}
)
test(
'account reset with keys',
function (t) {
var email = server.uniqueEmail()
var password = 'allyourbasearebelongtous'
var newPassword = 'ez'
var wrapKb, kA, client
return Client.createAndVerify(config.publicUrl, email, password, server.mailbox, {keys:true})
.then(
function (x) {
client = x
}
)
.then(
function () {
return client.keys()
}
)
.then(
function (keys) {
wrapKb = keys.wrapKb
kA = keys.kA
return client.forgotPassword()
}
)
.then(
function () {
return server.mailbox.waitForCode(email)
}
)
.then(
function (code) {
t.throws(function() {
client.resetPassword(newPassword)
})
return resetPassword(client, code, newPassword, {keys:true})
}
)
.then(
function (response) {
t.ok(response.sessionToken, 'session token is in response')
t.ok(response.keyFetchToken, 'keyFetchToken token is in response')
t.equal(response.verified, true, 'verified is true')
}
)
.then(
function () {
return server.mailbox.waitForEmail(email)
}
)
.then(
function (emailData) {
var link = emailData.headers['x-link']
var query = url.parse(link, true).query
t.ok(query.email, 'email is in the link')
}
)
.then(
function () {
// make sure we can still login after password reset
return Client.login(config.publicUrl, email, newPassword, server.mailbox, {keys:true})
}
)
.then(
function (x) {
client = x
return client.keys()
}
)
.then(
function (keys) {
t.ok(Buffer.isBuffer(keys.wrapKb), 'yep, wrapKb')
t.notDeepEqual(wrapKb, keys.wrapKb, 'wrapKb was reset')
t.deepEqual(kA, keys.kA, 'kA was not reset')
t.equal(client.kB.length, 32, 'kB exists, has the right length')
}
)
}
)
test(
'account reset w/o keys, with sessionToken',
function (t) {
var email = server.uniqueEmail()
var password = 'allyourbasearebelongtous'
var newPassword = 'ez'
var client
return Client.createAndVerify(config.publicUrl, email, password, server.mailbox)
.then(
function (x) {
client = x
}
)
.then(
function () {
return client.forgotPassword()
}
)
.then(
function () {
return server.mailbox.waitForCode(email)
}
)
.then(
function (code) {
t.throws(function() {
client.resetPassword(newPassword)
})
return resetPassword(client, code, newPassword)
}
)
.then(
function (response) {
t.ok(response.sessionToken, 'session token is in response')
t.notOk(response.keyFetchToken, 'keyFetchToken token is not in response')
t.equal(response.verified, true, 'verified is true')
}
)
}
)
test(
'teardown',
function (t) {
server.stop()
t.end()
}
)
describe('remote account reset', function() {
this.timeout(15000)
let server
before(() => {
process.env.SIGNIN_CONFIRMATION_ENABLED = false
return TestServer.start(config)
.then(s => {
server = s
})
})
function resetPassword(client, code, newPassword, options) {
return client.verifyPasswordResetCode(code)
.then(function() {
return client.resetPassword(newPassword, {}, options)
})
}
it(
'account reset w/o sessionToken',
() => {
var email = server.uniqueEmail()
var password = 'allyourbasearebelongtous'
var newPassword = 'ez'
var wrapKb, kA, client
return Client.createAndVerify(config.publicUrl, email, password, server.mailbox, {keys:true})
.then(
function (x) {
client = x
}
)
.then(
function () {
return client.keys()
}
)
.then(
function (keys) {
wrapKb = keys.wrapKb
kA = keys.kA
return client.forgotPassword()
}
)
.then(
function () {
return server.mailbox.waitForCode(email)
}
)
.then(
function (code) {
assert.throws(function() {
client.resetPassword(newPassword)
})
return resetPassword(client, code, newPassword, {sessionToken: false})
}
)
.then(
function (response) {
assert(!response.sessionToken, 'session token is not in response')
assert(!response.keyFetchToken, 'keyFetchToken token is not in response')
assert(!response.verified, 'verified is not in response')
}
)
.then(
function () {
return server.mailbox.waitForEmail(email)
}
)
.then(
function (emailData) {
var link = emailData.headers['x-link']
var query = url.parse(link, true).query
assert.ok(query.email, 'email is in the link')
}
)
.then(
function () {
// make sure we can still login after password reset
return Client.login(config.publicUrl, email, newPassword, server.mailbox, {keys:true})
}
)
.then(
function (x) {
client = x
return client.keys()
}
)
.then(
function (keys) {
assert.ok(Buffer.isBuffer(keys.wrapKb), 'yep, wrapKb')
assert.notDeepEqual(wrapKb, keys.wrapKb, 'wrapKb was reset')
assert.deepEqual(kA, keys.kA, 'kA was not reset')
assert.equal(client.kB.length, 32, 'kB exists, has the right length')
}
)
}
)
it(
'account reset with keys',
() => {
var email = server.uniqueEmail()
var password = 'allyourbasearebelongtous'
var newPassword = 'ez'
var wrapKb, kA, client
return Client.createAndVerify(config.publicUrl, email, password, server.mailbox, {keys:true})
.then(
function (x) {
client = x
}
)
.then(
function () {
return client.keys()
}
)
.then(
function (keys) {
wrapKb = keys.wrapKb
kA = keys.kA
return client.forgotPassword()
}
)
.then(
function () {
return server.mailbox.waitForCode(email)
}
)
.then(
function (code) {
assert.throws(function() {
client.resetPassword(newPassword)
})
return resetPassword(client, code, newPassword, {keys:true})
}
)
.then(
function (response) {
assert.ok(response.sessionToken, 'session token is in response')
assert.ok(response.keyFetchToken, 'keyFetchToken token is in response')
assert.equal(response.verified, true, 'verified is true')
}
)
.then(
function () {
return server.mailbox.waitForEmail(email)
}
)
.then(
function (emailData) {
var link = emailData.headers['x-link']
var query = url.parse(link, true).query
assert.ok(query.email, 'email is in the link')
}
)
.then(
function () {
// make sure we can still login after password reset
return Client.login(config.publicUrl, email, newPassword, server.mailbox, {keys:true})
}
)
.then(
function (x) {
client = x
return client.keys()
}
)
.then(
function (keys) {
assert.ok(Buffer.isBuffer(keys.wrapKb), 'yep, wrapKb')
assert.notDeepEqual(wrapKb, keys.wrapKb, 'wrapKb was reset')
assert.deepEqual(kA, keys.kA, 'kA was not reset')
assert.equal(client.kB.length, 32, 'kB exists, has the right length')
}
)
}
)
it(
'account reset w/o keys, with sessionToken',
() => {
var email = server.uniqueEmail()
var password = 'allyourbasearebelongtous'
var newPassword = 'ez'
var client
return Client.createAndVerify(config.publicUrl, email, password, server.mailbox)
.then(
function (x) {
client = x
}
)
.then(
function () {
return client.forgotPassword()
}
)
.then(
function () {
return server.mailbox.waitForCode(email)
}
)
.then(
function (code) {
assert.throws(function() {
client.resetPassword(newPassword)
})
return resetPassword(client, code, newPassword)
}
)
.then(
function (response) {
assert.ok(response.sessionToken, 'session token is in response')
assert(!response.keyFetchToken, 'keyFetchToken token is not in response')
assert.equal(response.verified, true, 'verified is true')
}
)
}
)
after(() => {
delete process.env.SIGNIN_CONFIRMATION_ENABLE
return TestServer.stop(server)
})
function resetPassword(client, code, newPassword, options) {
return client.verifyPasswordResetCode(code)
.then(function() {
return client.resetPassword(newPassword, {}, options)
})
}
})

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

@ -2,129 +2,137 @@
* 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')
'use strict'
const assert = require('insist')
var TestServer = require('../test_server')
const Client = require('../client')()
test(
'signin confirmation can be disabled',
function (t) {
var config = require('../../config').getProperties()
process.env.SIGNIN_CONFIRMATION_ENABLED = false
var server, email, client
var password = 'allyourbasearebelongtous'
describe('remote account signin verification enable', function() {
this.timeout(30000)
it(
'signin confirmation can be disabled',
() => {
process.env.SIGNIN_CONFIRMATION_ENABLED = false
var config = require('../../config').getProperties()
var server, email, client
var password = 'allyourbasearebelongtous'
TestServer.start(config)
.then(function main(serverObj) {
server = serverObj
email = server.uniqueEmail()
})
.then(function() {
return Client.createAndVerify(config.publicUrl, email, password, server.mailbox, {keys:true})
})
.then(
function (x) {
client = x
t.ok(client.authAt, 'authAt was set')
}
)
.then(
function () {
return client.emailStatus()
}
)
.then(
function (status) {
t.equal(status.verified, true, 'account is verified')
}
)
.then(
function () {
return client.login({keys:true})
}
)
.then(
function (response) {
t.notEqual(response.verificationMethod, 'email', 'verification method not set')
t.notEqual(response.verificationReason, 'login', 'verification reason not set')
}
)
.then(
function () {
return client.emailStatus()
}
)
.then(
function (status) {
t.equal(status.verified, true, 'account is verified')
}
)
.done(function() {
server.stop()
t.end()
})
}
)
return TestServer.start(config)
.then(function main(serverObj) {
server = serverObj
email = server.uniqueEmail()
})
.then(function() {
return Client.createAndVerify(config.publicUrl, email, password, server.mailbox, {keys:true})
})
.then(
function (x) {
client = x
assert.ok(client.authAt, 'authAt was set')
}
)
.then(
function () {
return client.emailStatus()
}
)
.then(
function (status) {
assert.equal(status.verified, true, 'account is verified')
}
)
.then(
function () {
return client.login({keys:true})
}
)
.then(
function (response) {
assert.notEqual(response.verificationMethod, 'email', 'verification method not set')
assert.notEqual(response.verificationReason, 'login', 'verification reason not set')
}
)
.then(
function () {
return client.emailStatus()
}
)
.then(
function (status) {
assert.equal(status.verified, true, 'account is verified')
}
)
.then(function() {
return TestServer.stop(server)
})
}
)
test(
'signin confirmation can be enabled',
function (t) {
process.env.SIGNIN_CONFIRMATION_ENABLED = true
process.env.SIGNIN_CONFIRMATION_RATE = 1.0
var config = require('../../config').getProperties()
var server, email, client
var password = 'allyourbasearebelongtous'
it(
'signin confirmation can be enabled',
() => {
process.env.SIGNIN_CONFIRMATION_ENABLED = true
process.env.SIGNIN_CONFIRMATION_RATE = 1.0
var config = require('../../config').getProperties()
var server, email, client
var password = 'allyourbasearebelongtous'
TestServer.start(config)
.then(function main(serverObj) {
server = serverObj
email = server.uniqueEmail()
})
.then(function() {
return Client.createAndVerify(config.publicUrl, email, password, server.mailbox, {keys:true})
})
.then(
function (x) {
client = x
t.ok(client.authAt, 'authAt was set')
}
)
.then(
function () {
return client.emailStatus()
}
)
.then(
function (status) {
t.equal(status.verified, true, 'account is verified')
}
)
.then(
function () {
return client.login({keys:true})
}
)
.then(
function (response) {
t.equal(response.verificationMethod, 'email', 'verification method set')
t.equal(response.verificationReason, 'login', 'verification reason set')
}
)
.then(
function () {
return client.emailStatus()
}
)
.then(
function (status) {
t.equal(status.verified, false, 'account is not verified')
t.equal(status.emailVerified, true, 'email is verified')
t.equal(status.sessionVerified, false, 'session is not verified')
}
)
.done(function() {
server.stop()
t.end()
})
}
)
TestServer.start(config)
.then(function main(serverObj) {
server = serverObj
email = server.uniqueEmail()
})
.then(function() {
return Client.createAndVerify(config.publicUrl, email, password, server.mailbox, {keys:true})
})
.then(
function (x) {
client = x
assert.ok(client.authAt, 'authAt was set')
}
)
.then(
function () {
return client.emailStatus()
}
)
.then(
function (status) {
assert.equal(status.verified, true, 'account is verified')
}
)
.then(
function () {
return client.login({keys:true})
}
)
.then(
function (response) {
assert.equal(response.verificationMethod, 'email', 'verification method set')
assert.equal(response.verificationReason, 'login', 'verification reason set')
}
)
.then(
function () {
return client.emailStatus()
}
)
.then(
function (status) {
assert.equal(status.verified, false, 'account is not verified')
assert.equal(status.emailVerified, true, 'email is verified')
assert.equal(status.sessionVerified, false, 'session is not verified')
}
)
.then(function() {
return TestServer.stop(server)
})
}
)
after(() => {
delete process.env.SIGNIN_CONFIRMATION_ENABLED
TestServer.stop()
})
})

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -2,18 +2,27 @@
* 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')
'use strict'
const assert = require('insist')
var TestServer = require('../test_server')
const Client = require('../client')()
var config = require('../../config').getProperties()
TestServer.start(config)
.then(function main(server) {
describe('remote account status', function() {
this.timeout(15000)
let server
before(() => {
return TestServer.start(config)
.then(s => {
server = s
})
})
test(
it(
'account status with existing account',
function (t) {
() => {
return Client.create(config.publicUrl, server.uniqueEmail(), 'password')
.then(
function (c) {
@ -22,15 +31,15 @@ TestServer.start(config)
)
.then(
function (response) {
t.ok(response.exists, 'account exists')
assert.ok(response.exists, 'account exists')
}
)
}
)
test(
it(
'account status includes locale when authenticated',
function (t) {
() => {
return Client.create(config.publicUrl, server.uniqueEmail(), 'password', { lang: 'en-US' })
.then(
function (c) {
@ -39,15 +48,15 @@ TestServer.start(config)
)
.then(
function (response) {
t.equal(response.locale, 'en-US', 'locale is stored')
assert.equal(response.locale, 'en-US', 'locale is stored')
}
)
}
)
test(
it(
'account status does not include locale when unauthenticated',
function (t) {
() => {
return Client.create(config.publicUrl, server.uniqueEmail(), 'password', { lang: 'en-US' })
.then(
function (c) {
@ -56,15 +65,15 @@ TestServer.start(config)
)
.then(
function (response) {
t.ok(!response.locale, 'locale is not present')
assert.ok(!response.locale, 'locale is not present')
}
)
}
)
test(
it(
'account status unauthenticated with no uid returns an error',
function (t) {
() => {
return Client.create(config.publicUrl, server.uniqueEmail(), 'password', { lang: 'en-US' })
.then(
function (c) {
@ -73,32 +82,32 @@ TestServer.start(config)
)
.then(
function () {
t.fail('should get an error')
assert(false, 'should get an error')
},
function (e) {
t.equal(e.code, 400, 'correct error status code')
t.equal(e.errno, 108, 'correct errno')
assert.equal(e.code, 400, 'correct error status code')
assert.equal(e.errno, 108, 'correct errno')
}
)
}
)
test(
it(
'account status with non-existing account',
function (t) {
() => {
var api = new Client.Api(config.publicUrl)
return api.accountStatus('0123456789ABCDEF0123456789ABCDEF')
.then(
function (response) {
t.ok(!response.exists, 'account does not exist')
assert.ok(!response.exists, 'account does not exist')
}
)
}
)
test(
it(
'account status by email with existing account',
function (t) {
() => {
var email = server.uniqueEmail()
return Client.create(config.publicUrl, email, 'password')
.then(
@ -108,15 +117,15 @@ TestServer.start(config)
)
.then(
function (response) {
t.ok(response.exists, 'account exists')
assert.ok(response.exists, 'account exists')
}
)
}
)
test(
it(
'account status by email with non-existing account',
function (t) {
() => {
var email = server.uniqueEmail()
return Client.create(config.publicUrl, email, 'password')
.then(
@ -127,15 +136,15 @@ TestServer.start(config)
)
.then(
function (response) {
t.ok(!response.exists, 'account does not exist')
assert.ok(!response.exists, 'account does not exist')
}
)
}
)
test(
it(
'account status by email with an invalid email',
function (t) {
() => {
var email = server.uniqueEmail()
return Client.create(config.publicUrl, email, 'password')
.then(
@ -146,20 +155,20 @@ TestServer.start(config)
)
.then(
function () {
t.fail('should not have successful request')
assert(false, 'should not have successful request')
},
function (err) {
t.equal(err.code, 400)
t.equal(err.errno, 107)
t.equal(err.message, 'Invalid parameter in request body')
assert.equal(err.code, 400)
assert.equal(err.errno, 107)
assert.equal(err.message, 'Invalid parameter in request body')
}
)
}
)
test(
it(
'account status by email works with unicode email address',
function (t) {
() => {
var email = server.uniqueUnicodeEmail()
return Client.create(config.publicUrl, email, 'password')
.then(
@ -169,17 +178,13 @@ TestServer.start(config)
)
.then(
function (response) {
t.ok(response.exists, 'account exists')
assert.ok(response.exists, 'account exists')
}
)
}
)
test(
'teardown',
function (t) {
server.stop()
t.end()
}
)
after(() => {
return TestServer.stop(server)
})
})

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

@ -2,18 +2,27 @@
* 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')
'use strict'
const assert = require('insist')
var TestServer = require('../test_server')
const Client = require('../client')()
var config = require('../../config').getProperties()
TestServer.start(config)
.then(function main(server) {
describe('remote account unlock', function() {
this.timeout(15000)
let server
before(() => {
return TestServer.start(config)
.then(s => {
server = s
})
})
test(
it(
'/account/lock is no longer supported',
function (t) {
() => {
return Client.create(config.publicUrl, server.uniqueEmail(), 'password')
.then(
function (c) {
@ -22,18 +31,18 @@ TestServer.start(config)
)
.then(
function () {
t.fail('should get an error')
assert(false, 'should get an error')
},
function (e) {
t.equal(e.code, 410, 'correct error status code')
assert.equal(e.code, 410, 'correct error status code')
}
)
}
)
test(
it(
'/account/unlock/resend_code is no longer supported',
function (t) {
() => {
return Client.create(config.publicUrl, server.uniqueEmail(), 'password')
.then(
function (c) {
@ -42,18 +51,18 @@ TestServer.start(config)
)
.then(
function () {
t.fail('should get an error')
assert(false, 'should get an error')
},
function (e) {
t.equal(e.code, 410, 'correct error status code')
assert.equal(e.code, 410, 'correct error status code')
}
)
}
)
test(
it(
'/account/unlock/verify_code is no longer supported',
function (t) {
() => {
return Client.create(config.publicUrl, server.uniqueEmail(), 'password')
.then(
function (c) {
@ -62,20 +71,16 @@ TestServer.start(config)
)
.then(
function () {
t.fail('should get an error')
assert(false, 'should get an error')
},
function (e) {
t.equal(e.code, 410, 'correct error status code')
assert.equal(e.code, 410, 'correct error status code')
}
)
}
)
test(
'teardown',
function (t) {
server.stop()
t.end()
}
)
after(() => {
return TestServer.stop(server)
})
})

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

@ -2,43 +2,48 @@
* 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/. */
process.env.PUBLIC_URL = 'http://127.0.0.1:9000/auth'
'use strict'
var test = require('../ptaptest')
const assert = require('insist')
var TestServer = require('../test_server')
const Client = require('../client')()
var P = require('../../lib/promise')
var request = require('request')
var request = P.promisify(require('request'))
var config = require('../../config').getProperties()
TestServer.start(config)
.then(function main(server) {
describe('remote base path', function() {
this.timeout(15000)
let server, config
before(() => {
process.env.PUBLIC_URL = 'http://127.0.0.1:9000/auth'
config = require('../../config').getProperties()
config.publicUrl = process.env.PUBLIC_URL
return TestServer.start(config)
.then(s => {
server = s
})
})
function testVersionRoute(path) {
return function (t) {
var d = P.defer()
request(config.publicUrl + path,
function (err, res, body) {
if (err) { d.reject(err) }
t.equal(res.statusCode, 200)
return () => {
return request(config.publicUrl + path)
.spread((res, body) => {
assert.equal(res.statusCode, 200)
var json = JSON.parse(body)
t.deepEqual(Object.keys(json), ['version', 'commit', 'source'])
t.equal(json.version, require('../../package.json').version, 'package version')
t.ok(json.source && json.source !== 'unknown', 'source repository')
assert.deepEqual(Object.keys(json), ['version', 'commit', 'source'])
assert.equal(json.version, require('../../package.json').version, 'package version')
assert.ok(json.source && json.source !== 'unknown', 'source repository')
// check that the git hash just looks like a hash
t.ok(json.commit.match(/^[0-9a-f]{40}$/), 'The git hash actually looks like one')
d.resolve(json)
}
)
return d.promise
assert.ok(json.commit.match(/^[0-9a-f]{40}$/), 'The git hash actually looks like one')
})
}
}
test(
it(
'alternate base path',
function (t) {
() => {
var email = Math.random() + '@example.com'
var password = 'ok'
// if this doesn't crash, we're all good
@ -46,38 +51,30 @@ TestServer.start(config)
}
)
test(
it(
'.well-known did not move',
function (t) {
var d = P.defer()
request('http://127.0.0.1:9000/.well-known/browserid',
function (err, res, body) {
if (err) { d.reject(err) }
t.equal(res.statusCode, 200)
() => {
return request('http://127.0.0.1:9000/.well-known/browserid')
.spread((res, body) => {
assert.equal(res.statusCode, 200)
var json = JSON.parse(body)
t.equal(json.authentication, '/.well-known/browserid/sign_in.html')
d.resolve(json)
}
)
return d.promise
assert.equal(json.authentication, '/.well-known/browserid/sign_in.html')
})
}
)
test(
it(
'"/" returns valid version information',
testVersionRoute('/')
)
test(
it(
'"/__version__" returns valid version information',
testVersionRoute('/__version__')
)
test(
'teardown',
function (t) {
server.stop()
t.end()
}
)
after(() => {
delete process.env.PUBLIC_URL
return TestServer.stop(server)
})
})

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

@ -2,7 +2,9 @@
* 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')
'use strict'
const assert = require('insist')
var TestServer = require('../test_server')
const Client = require('../client')()
var jwtool = require('fxa-jwtool')
@ -16,12 +18,19 @@ var publicKey = {
'e': '65537'
}
TestServer.start(config)
.then(function main(server) {
describe('remote certificate sign', function() {
this.timeout(15000)
let server
before(() => {
return TestServer.start(config)
.then(s => {
server = s
})
})
test(
it(
'certificate sign',
function (t) {
() => {
var email = server.uniqueEmail()
var password = 'allyourbasearebelongtous'
var client = null
@ -35,22 +44,22 @@ TestServer.start(config)
)
.then(
function (cert) {
t.equal(typeof(cert), 'string', 'cert exists')
assert.equal(typeof(cert), 'string', 'cert exists')
var payload = jwtool.verify(cert, pubSigKey.pem)
t.equal(payload.iss, config.domain, 'issuer is correct')
t.equal(payload.principal.email.split('@')[0], client.uid, 'cert has correct uid')
t.ok(payload['fxa-generation'] > 0, 'cert has non-zero generation number')
t.ok(new Date() - new Date(payload['fxa-lastAuthAt'] * 1000) < 1000 * 60 * 60, 'lastAuthAt is plausible')
t.equal(payload['fxa-verifiedEmail'], email, 'verifiedEmail is correct')
t.equal(payload['fxa-tokenVerified'], true, 'tokenVerified is correct')
assert.equal(payload.iss, config.domain, 'issuer is correct')
assert.equal(payload.principal.email.split('@')[0], client.uid, 'cert has correct uid')
assert.ok(payload['fxa-generation'] > 0, 'cert has non-zero generation number')
assert.ok(new Date() - new Date(payload['fxa-lastAuthAt'] * 1000) < 1000 * 60 * 60, 'lastAuthAt is plausible')
assert.equal(payload['fxa-verifiedEmail'], email, 'verifiedEmail is correct')
assert.equal(payload['fxa-tokenVerified'], true, 'tokenVerified is correct')
}
)
}
)
test(
it(
'certificate sign requires a verified account',
function (t) {
() => {
var email = server.uniqueEmail()
var password = 'allyourbasearebelongtous'
var client = null
@ -64,18 +73,18 @@ TestServer.start(config)
)
.then(
function (cert) {
t.fail('should not be able to sign with unverified account')
assert(false, 'should not be able to sign with unverified account')
},
function (err) {
t.equal(err.errno, 104, 'should get an unverifiedAccount error')
assert.equal(err.errno, 104, 'should get an unverifiedAccount error')
}
)
}
)
test(
it(
'/certificate/sign inputs',
function (t) {
() => {
var email = server.uniqueEmail()
var password = '123456'
var client = null
@ -88,97 +97,97 @@ TestServer.start(config)
}
)
.then(
t.fail,
() => assert(false),
function (err) {
t.equal(err.code, 400, 'string as publicKey')
assert.equal(err.code, 400, 'string as publicKey')
// empty object as publicKey
return client.sign({}, 1000)
}
)
.then(
t.fail,
() => assert(false),
function (err) {
t.equal(err.code, 400, 'empty object as publicKey')
assert.equal(err.code, 400, 'empty object as publicKey')
// invalid publicKey argument
return client.sign({ algorithm: 'RS', n: 'x', e: 'y', invalid: true }, 1000)
}
)
.then(
t.fail,
() => assert(false),
function (err) {
t.equal(err.code, 400, 'invalid publicKey argument')
assert.equal(err.code, 400, 'invalid publicKey argument')
// undefined duration
return client.sign({ algorithm: 'RS', n: 'x', e: 'y' }, undefined)
}
)
.then(
t.fail,
() => assert(false),
function (err) {
t.equal(err.code, 400, 'undefined duration')
assert.equal(err.code, 400, 'undefined duration')
// missing publicKey arguments (e)
return client.sign({ algorithm: 'RS', n: 'x' }, 1000)
}
)
.then(
t.fail,
() => assert(false),
function (err) {
t.equal(err.code, 400, 'missing publicKey arguments (e)')
assert.equal(err.code, 400, 'missing publicKey arguments (e)')
// missing publicKey arguments (n)
return client.sign({ algorithm: 'RS', e: 'x' }, 1000)
}
)
.then(
t.fail,
() => assert(false),
function (err) {
t.equal(err.code, 400, 'missing publicKey arguments (n)')
assert.equal(err.code, 400, 'missing publicKey arguments (n)')
// missing publicKey arguments (y)
return client.sign({ algorithm: 'DS', p: 'p', q: 'q', g: 'g' }, 1000)
}
)
.then(
t.fail,
() => assert(false),
function (err) {
t.equal(err.code, 400, 'missing publicKey arguments (y)')
assert.equal(err.code, 400, 'missing publicKey arguments (y)')
// missing publicKey arguments (p)
return client.sign({ algorithm: 'DS', y: 'y', q: 'q', g: 'g' }, 1000)
}
)
.then(
t.fail,
() => assert(false),
function (err) {
t.equal(err.code, 400, 'missing publicKey arguments (p)')
assert.equal(err.code, 400, 'missing publicKey arguments (p)')
// missing publicKey arguments (q)
return client.sign({ algorithm: 'DS', y: 'y', p: 'p', g: 'g' }, 1000)
}
)
.then(
t.fail,
() => assert(false),
function (err) {
t.equal(err.code, 400, 'missing publicKey arguments (q)')
assert.equal(err.code, 400, 'missing publicKey arguments (q)')
// missing publicKey arguments (g)
return client.sign({ algorithm: 'DS', y: 'y', p: 'p', q: 'q' }, 1000)
}
)
.then(
t.fail,
() => assert(false),
function (err) {
t.equal(err.code, 400, 'missing publicKey arguments (g)')
assert.equal(err.code, 400, 'missing publicKey arguments (g)')
// invalid algorithm
return client.sign({ algorithm: 'NSA' }, 1000)
}
)
.then(
t.fail,
() => assert(false),
function (err) {
t.equal(err.code, 400, 'invalid algorithm')
assert.equal(err.code, 400, 'invalid algorithm')
}
)
}
)
test(
it(
'no payload',
function (t) {
() => {
var email = server.uniqueEmail()
var password = 'allyourbasearebelongtous'
var duration = 1000 * 60 * 60 * 24 // 24 hours
@ -210,19 +219,15 @@ TestServer.start(config)
}
)
.then(
t.fail,
() => assert(false),
function (err) {
t.equal(err.errno, 109, 'Missing payload authentication')
assert.equal(err.errno, 109, 'Missing payload authentication')
}
)
}
)
test(
'teardown',
function (t) {
server.stop()
t.end()
}
)
after(() => {
return TestServer.stop(server)
})
})

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

@ -2,21 +2,30 @@
* 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('tap').test
'use strict'
const assert = require('insist')
var TestServer = require('../test_server')
const Client = require('../client')()
var P = require('../../lib/promise')
var config = require('../../config').getProperties()
process.env.VERIFIER_VERSION = '1'
TestServer.start(config)
.then(function main(server) {
describe('remote concurrect', function() {
this.timeout(15000)
let server
before(() => {
process.env.VERIFIER_VERSION = '1'
return TestServer.start(config)
.then(s => {
server = s
})
})
test(
it(
'concurrent create requests',
function (t) {
() => {
var email = server.uniqueEmail()
var password = 'abcdef'
// Two shall enter, only one shall survive!
@ -26,9 +35,9 @@ TestServer.start(config)
[r1, r2]
)
.then(
t.fail.bind(t, 'created both accounts'),
() => assert(false, 'created both accounts'),
function (err) {
t.equal(err.errno, 101, 'account exists')
assert.equal(err.errno, 101, 'account exists')
// Note that P.all fails fast when one of the requests fails,
// but we have to wait for *both* to complete before tearing
// down the test infrastructure. Bleh.
@ -46,11 +55,8 @@ TestServer.start(config)
}
)
test(
'teardown',
function (t) {
server.stop()
t.end()
}
)
after(() => {
delete process.env.VERIFIER_VERSION
return TestServer.stop(server)
})
})

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -2,7 +2,9 @@
* 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')
'use strict'
const assert = require('insist')
var TestServer = require('../test_server')
const Client = require('../client')()
var config = require('../../config').getProperties()
@ -10,16 +12,24 @@ var crypto = require('crypto')
var base64url = require('base64url')
var P = require('../../lib/promise')
// HACK: Force-enable devices.lastAccessTime in the spawned server process
process.env.LASTACCESSTIME_UPDATES_ENABLED = 'true'
process.env.LASTACCESSTIME_UPDATES_EMAIL_ADDRESSES = '.*'
process.env.LASTACCESSTIME_UPDATES_SAMPLE_RATE = '1'
describe('remote device', function() {
this.timeout(15000)
let server
before(() => {
// HACK: Force-enable devices.lastAccessTime in the spawned server process
process.env.LASTACCESSTIME_UPDATES_ENABLED = 'true'
process.env.LASTACCESSTIME_UPDATES_EMAIL_ADDRESSES = '.*'
process.env.LASTACCESSTIME_UPDATES_SAMPLE_RATE = '1'
TestServer.start(config)
.then(function main(server) {
test(
return TestServer.start(config)
.then(s => {
server = s
})
})
it(
'device registration after account creation',
function (t) {
() => {
var email = server.uniqueEmail()
var password = 'test password'
return Client.create(config.publicUrl, email, password)
@ -35,19 +45,19 @@ TestServer.start(config)
return client.devices()
.then(
function (devices) {
t.equal(devices.length, 0, 'devices returned no items')
assert.equal(devices.length, 0, 'devices returned no items')
return client.updateDevice(deviceInfo)
}
)
.then(
function (device) {
t.ok(device.id, 'device.id was set')
t.ok(device.createdAt > 0, 'device.createdAt was set')
t.equal(device.name, deviceInfo.name, 'device.name is correct')
t.equal(device.type, deviceInfo.type, 'device.type is correct')
t.equal(device.pushCallback, deviceInfo.pushCallback, 'device.pushCallback is correct')
t.equal(device.pushPublicKey, deviceInfo.pushPublicKey, 'device.pushPublicKey is correct')
t.equal(device.pushAuthKey, deviceInfo.pushAuthKey, 'device.pushAuthKey is correct')
assert.ok(device.id, 'device.id was set')
assert.ok(device.createdAt > 0, 'device.createdAt was set')
assert.equal(device.name, deviceInfo.name, 'device.name is correct')
assert.equal(device.type, deviceInfo.type, 'device.type is correct')
assert.equal(device.pushCallback, deviceInfo.pushCallback, 'device.pushCallback is correct')
assert.equal(device.pushPublicKey, deviceInfo.pushPublicKey, 'device.pushPublicKey is correct')
assert.equal(device.pushAuthKey, deviceInfo.pushAuthKey, 'device.pushAuthKey is correct')
}
)
.then(
@ -57,12 +67,12 @@ TestServer.start(config)
)
.then(
function (devices) {
t.equal(devices.length, 1, 'devices returned one item')
t.equal(devices[0].name, deviceInfo.name, 'devices returned correct name')
t.equal(devices[0].type, deviceInfo.type, 'devices returned correct type')
t.equal(devices[0].pushCallback, '', 'devices returned empty pushCallback')
t.equal(devices[0].pushPublicKey, '', 'devices returned correct pushPublicKey')
t.equal(devices[0].pushAuthKey, '', 'devices returned correct pushAuthKey')
assert.equal(devices.length, 1, 'devices returned one item')
assert.equal(devices[0].name, deviceInfo.name, 'devices returned correct name')
assert.equal(devices[0].type, deviceInfo.type, 'devices returned correct type')
assert.equal(devices[0].pushCallback, '', 'devices returned empty pushCallback')
assert.equal(devices[0].pushPublicKey, '', 'devices returned correct pushPublicKey')
assert.equal(devices[0].pushAuthKey, '', 'devices returned correct pushAuthKey')
return client.destroyDevice(devices[0].id)
}
)
@ -71,9 +81,9 @@ TestServer.start(config)
}
)
test(
it(
'device registration without optional parameters',
function (t) {
() => {
var email = server.uniqueEmail()
var password = 'test password'
return Client.create(config.publicUrl, email, password)
@ -86,19 +96,19 @@ TestServer.start(config)
return client.devices()
.then(
function (devices) {
t.equal(devices.length, 0, 'devices returned no items')
assert.equal(devices.length, 0, 'devices returned no items')
return client.updateDevice(deviceInfo)
}
)
.then(
function (device) {
t.ok(device.id, 'device.id was set')
t.ok(device.createdAt > 0, 'device.createdAt was set')
t.equal(device.name, deviceInfo.name, 'device.name is correct')
t.equal(device.type, deviceInfo.type, 'device.type is correct')
t.equal(device.pushCallback, undefined, 'device.pushCallback is undefined')
t.equal(device.pushPublicKey, undefined, 'device.pushPublicKey is undefined')
t.equal(device.pushAuthKey, undefined, 'device.pushAuthKey is undefined')
assert.ok(device.id, 'device.id was set')
assert.ok(device.createdAt > 0, 'device.createdAt was set')
assert.equal(device.name, deviceInfo.name, 'device.name is correct')
assert.equal(device.type, deviceInfo.type, 'device.type is correct')
assert.equal(device.pushCallback, undefined, 'device.pushCallback is undefined')
assert.equal(device.pushPublicKey, undefined, 'device.pushPublicKey is undefined')
assert.equal(device.pushAuthKey, undefined, 'device.pushAuthKey is undefined')
}
)
.then(
@ -108,12 +118,12 @@ TestServer.start(config)
)
.then(
function (devices) {
t.equal(devices.length, 1, 'devices returned one item')
t.equal(devices[0].name, deviceInfo.name, 'devices returned correct name')
t.equal(devices[0].type, deviceInfo.type, 'devices returned correct type')
t.equal(devices[0].pushCallback, null, 'devices returned undefined pushCallback')
t.equal(devices[0].pushPublicKey, null, 'devices returned undefined pushPublicKey')
t.equal(devices[0].pushAuthKey, null, 'devices returned undefined pushAuthKey')
assert.equal(devices.length, 1, 'devices returned one item')
assert.equal(devices[0].name, deviceInfo.name, 'devices returned correct name')
assert.equal(devices[0].type, deviceInfo.type, 'devices returned correct type')
assert.equal(devices[0].pushCallback, null, 'devices returned undefined pushCallback')
assert.equal(devices[0].pushPublicKey, null, 'devices returned undefined pushPublicKey')
assert.equal(devices[0].pushAuthKey, null, 'devices returned undefined pushAuthKey')
return client.destroyDevice(devices[0].id)
}
)
@ -122,9 +132,9 @@ TestServer.start(config)
}
)
test(
it(
'device registration without required name parameter',
function (t) {
() => {
var email = server.uniqueEmail()
var password = 'test password'
return Client.create(config.publicUrl, email, password)
@ -133,13 +143,13 @@ TestServer.start(config)
return client.updateDevice({ type: 'mobile' })
.then(
function (r) {
t.fail('request should have failed')
assert(false, 'request should have failed')
}
)
.catch(
function (err) {
t.equal(err.code, 400, 'err.code was 400')
t.equal(err.errno, 108, 'err.errno was 108')
assert.equal(err.code, 400, 'err.code was 400')
assert.equal(err.errno, 108, 'err.errno was 108')
}
)
}
@ -147,9 +157,9 @@ TestServer.start(config)
}
)
test(
it(
'device registration without required type parameter',
function (t) {
() => {
var email = server.uniqueEmail()
var password = 'test password'
return Client.create(config.publicUrl, email, password)
@ -158,13 +168,13 @@ TestServer.start(config)
return client.updateDevice({ name: 'test device' })
.then(
function () {
t.fail('request should have failed')
assert(false, 'request should have failed')
}
)
.catch(
function (err) {
t.equal(err.code, 400, 'err.code was 400')
t.equal(err.errno, 108, 'err.errno was 108')
assert.equal(err.code, 400, 'err.code was 400')
assert.equal(err.errno, 108, 'err.errno was 108')
}
)
}
@ -172,9 +182,9 @@ TestServer.start(config)
}
)
test(
it(
'device registration with unsupported characters in the name',
function (t) {
() => {
var email = server.uniqueEmail()
var password = 'test password'
return Client.create(config.publicUrl, email, password)
@ -188,14 +198,14 @@ TestServer.start(config)
return client.updateDevice(deviceInfo)
.then(
function () {
t.fail('request should have failed')
assert(false, 'request should have failed')
}
)
.catch(
function (err) {
t.equal(err.code, 400, 'err.code was 400')
t.equal(err.errno, 107, 'err.errno was 107')
t.equal(err.validation.keys[0], 'name', 'name was rejected')
assert.equal(err.code, 400, 'err.code was 400')
assert.equal(err.errno, 107, 'err.errno was 107')
assert.equal(err.validation.keys[0], 'name', 'name was rejected')
}
)
}
@ -203,9 +213,9 @@ TestServer.start(config)
}
)
test(
it(
'device registration from a different session',
function (t) {
() => {
var email = server.uniqueEmail()
var password = 'test password'
var deviceInfo = [
@ -234,10 +244,10 @@ TestServer.start(config)
)
.then(
function (devices) {
t.equal(devices.length, 1, 'devices returned one item')
t.equal(devices[0].isCurrentDevice, false, 'devices returned false isCurrentDevice')
t.equal(devices[0].name, deviceInfo[0].name, 'devices returned correct name')
t.equal(devices[0].type, deviceInfo[0].type, 'devices returned correct type')
assert.equal(devices.length, 1, 'devices returned one item')
assert.equal(devices[0].isCurrentDevice, false, 'devices returned false isCurrentDevice')
assert.equal(devices[0].name, deviceInfo[0].name, 'devices returned correct name')
assert.equal(devices[0].type, deviceInfo[0].type, 'devices returned correct type')
return client.updateDevice(deviceInfo[1])
}
)
@ -248,7 +258,7 @@ TestServer.start(config)
)
.then(
function (devices) {
t.equal(devices.length, 2, 'devices returned two items')
assert.equal(devices.length, 2, 'devices returned two items')
if (devices[0].name === deviceInfo[1].name) {
// database results are unordered, swap them if necessary
var swap = {}
@ -258,12 +268,12 @@ TestServer.start(config)
devices[1][key] = swap[key]
})
}
t.equal(devices[0].isCurrentDevice, false, 'devices returned false isCurrentDevice for first item')
t.equal(devices[0].name, deviceInfo[0].name, 'devices returned correct name for first item')
t.equal(devices[0].type, deviceInfo[0].type, 'devices returned correct type for first item')
t.equal(devices[1].isCurrentDevice, true, 'devices returned true isCurrentDevice for second item')
t.equal(devices[1].name, deviceInfo[1].name, 'devices returned correct name for second item')
t.equal(devices[1].type, deviceInfo[1].type, 'devices returned correct type for second item')
assert.equal(devices[0].isCurrentDevice, false, 'devices returned false isCurrentDevice for first item')
assert.equal(devices[0].name, deviceInfo[0].name, 'devices returned correct name for first item')
assert.equal(devices[0].type, deviceInfo[0].type, 'devices returned correct type for first item')
assert.equal(devices[1].isCurrentDevice, true, 'devices returned true isCurrentDevice for second item')
assert.equal(devices[1].name, deviceInfo[1].name, 'devices returned correct name for second item')
assert.equal(devices[1].type, deviceInfo[1].type, 'devices returned correct type for second item')
return P.all([
client.destroyDevice(devices[0].id),
client.destroyDevice(devices[1].id)
@ -275,9 +285,9 @@ TestServer.start(config)
}
)
test(
it(
'update device with callbackUrl but without keys resets the keys',
function (t) {
() => {
var email = server.uniqueEmail()
var password = 'test password'
var deviceInfo = {
@ -298,9 +308,9 @@ TestServer.start(config)
)
.then(
function (devices) {
t.equal(devices[0].pushCallback, deviceInfo.pushCallback, 'devices returned correct pushCallback')
t.equal(devices[0].pushPublicKey, deviceInfo.pushPublicKey, 'devices returned correct pushPublicKey')
t.equal(devices[0].pushAuthKey, deviceInfo.pushAuthKey, 'devices returned correct pushAuthKey')
assert.equal(devices[0].pushCallback, deviceInfo.pushCallback, 'devices returned correct pushCallback')
assert.equal(devices[0].pushPublicKey, deviceInfo.pushPublicKey, 'devices returned correct pushPublicKey')
assert.equal(devices[0].pushAuthKey, deviceInfo.pushAuthKey, 'devices returned correct pushAuthKey')
return client.updateDevice({
id: client.device.id,
pushCallback: 'https://bar/foo'
@ -314,9 +324,9 @@ TestServer.start(config)
)
.then(
function (devices) {
t.equal(devices[0].pushCallback, 'https://bar/foo', 'devices returned correct pushCallback')
t.equal(devices[0].pushPublicKey, '', 'devices returned newly empty pushPublicKey')
t.equal(devices[0].pushAuthKey, '', 'devices returned newly empty pushAuthKey')
assert.equal(devices[0].pushCallback, 'https://bar/foo', 'devices returned correct pushCallback')
assert.equal(devices[0].pushPublicKey, '', 'devices returned newly empty pushPublicKey')
assert.equal(devices[0].pushAuthKey, '', 'devices returned newly empty pushAuthKey')
}
)
}
@ -324,10 +334,10 @@ TestServer.start(config)
}
)
test(
it(
// Regression test for https://github.com/mozilla/fxa-auth-server/issues/1197
'devices list, sessionToken.lastAccessTime === 0 (regression test for #1197)',
function (t) {
() => {
var email = server.uniqueEmail()
var password = 'test password'
var deviceInfo = {
@ -347,12 +357,12 @@ TestServer.start(config)
)
.then(
function (devices) {
t.equal(devices.length, 1, 'devices returned one item')
t.strictEqual(devices[0].lastAccessTime, 0, 'devices returned correct lastAccessTime')
t.strictEqual(devices[0].lastAccessTimeFormatted, '',
assert.equal(devices.length, 1, 'devices returned one item')
assert.strictEqual(devices[0].lastAccessTime, 0, 'devices returned correct lastAccessTime')
assert.strictEqual(devices[0].lastAccessTimeFormatted, '',
'devices returned empty lastAccessTimeFormatted because lastAccesstime is 0')
t.equal(devices[0].name, deviceInfo.name, 'devices returned correct name')
t.equal(devices[0].type, deviceInfo.type, 'devices returned correct type')
assert.equal(devices[0].name, deviceInfo.name, 'devices returned correct name')
assert.equal(devices[0].type, deviceInfo.type, 'devices returned correct type')
return client.destroyDevice(devices[0].id)
}
)
@ -361,9 +371,9 @@ TestServer.start(config)
}
)
test(
it(
'devices list, sessionToken.lastAccessTime === -1',
function (t) {
() => {
var email = server.uniqueEmail()
var password = 'test password'
var deviceInfo = {
@ -383,12 +393,12 @@ TestServer.start(config)
)
.then(
function (devices) {
t.equal(devices.length, 1, 'devices returned one item')
t.ok(devices[0].lastAccessTime > 0, 'devices returned correct lastAccessTime')
t.strictEqual(devices[0].lastAccessTimeFormatted, 'a few seconds ago',
assert.equal(devices.length, 1, 'devices returned one item')
assert.ok(devices[0].lastAccessTime > 0, 'devices returned correct lastAccessTime')
assert.strictEqual(devices[0].lastAccessTimeFormatted, 'a few seconds ago',
'devices returned correct lastAccessTimeFormatted')
t.equal(devices[0].name, deviceInfo.name, 'devices returned correct name')
t.equal(devices[0].type, deviceInfo.type, 'devices returned correct type')
assert.equal(devices[0].name, deviceInfo.name, 'devices returned correct name')
assert.equal(devices[0].type, deviceInfo.type, 'devices returned correct type')
return client.destroyDevice(devices[0].id)
}
)
@ -397,9 +407,9 @@ TestServer.start(config)
}
)
test(
it(
'devices list, sessionToken.lastAccessTime === THE FUTURE',
function (t) {
() => {
var email = server.uniqueEmail()
var password = 'test password'
var deviceInfo = {
@ -420,13 +430,13 @@ TestServer.start(config)
)
.then(
function (devices) {
t.equal(devices.length, 1, 'devices returned one item')
t.ok(devices[0].lastAccessTime > 0, 'devices returned correct lastAccessTime')
t.ok(devices[0].lastAccessTime < theFuture, 'devices returned correct lastAccessTime')
t.strictEqual(devices[0].lastAccessTimeFormatted, 'a few seconds ago',
assert.equal(devices.length, 1, 'devices returned one item')
assert.ok(devices[0].lastAccessTime > 0, 'devices returned correct lastAccessTime')
assert.ok(devices[0].lastAccessTime < theFuture, 'devices returned correct lastAccessTime')
assert.strictEqual(devices[0].lastAccessTimeFormatted, 'a few seconds ago',
'devices returned correct lastAccessTimeFormatted')
t.equal(devices[0].name, deviceInfo.name, 'devices returned correct name')
t.equal(devices[0].type, deviceInfo.type, 'devices returned correct type')
assert.equal(devices[0].name, deviceInfo.name, 'devices returned correct name')
assert.equal(devices[0].type, deviceInfo.type, 'devices returned correct type')
return client.destroyDevice(devices[0].id)
}
)
@ -435,11 +445,11 @@ TestServer.start(config)
}
)
test(
'teardown',
function (t) {
server.stop()
t.end()
}
)
after(() => {
delete process.env.LASTACCESSTIME_UPDATES_ENABLED
delete process.env.LASTACCESSTIME_UPDATES_EMAIL_ADDRESSES
delete process.env.LASTACCESSTIME_UPDATES_SAMPLE_RATE
return TestServer.stop(server)
})
})

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

@ -2,19 +2,28 @@
* 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')
'use strict'
const assert = require('insist')
var TestServer = require('../test_server')
const Client = require('../client')()
var P = require('../../lib/promise')
var config = require('../../config').getProperties()
TestServer.start(config)
.then(function main(server) {
describe('remote email validity', function() {
this.timeout(15000)
let server
before(() => {
return TestServer.start(config)
.then(s => {
server = s
})
})
test(
it(
'/account/create with a variety of malformed email addresses',
function (t) {
() => {
var pwd = '123456'
var emails = [
@ -33,9 +42,9 @@ TestServer.start(config)
emails.forEach(function(email, i) {
emails[i] = Client.create(config.publicUrl, email, pwd)
.then(
t.fail,
assert.fail,
function (err) {
t.equal(err.code, 400, 'http 400 : malformed email is rejected')
assert.equal(err.code, 400, 'http 400 : malformed email is rejected')
}
)
})
@ -44,15 +53,15 @@ TestServer.start(config)
}
)
test(
it(
'/account/create with a variety of unusual but valid email addresses',
function (t) {
() => {
var pwd = '123456'
var emails = [
'tim@tim-example.net',
'a+b+c@example.com',
'#!?-@t-e-s-t.c-o-m',
'#!?-@t-e-s-assert.c-o-m',
String.fromCharCode(1234) + '@example.com',
'test@' + String.fromCharCode(5678) + '.com'
]
@ -61,11 +70,10 @@ TestServer.start(config)
emails[i] = Client.create(config.publicUrl, email, pwd)
.then(
function(c) {
t.pass('Email ' + email + ' is valid')
return c.destroyAccount()
},
function (err) {
t.fail('Email address ' + email + " should have been allowed, but it wasn't")
assert(false, 'Email address ' + email + " should have been allowed, but it wasn't")
}
)
})
@ -74,11 +82,7 @@ TestServer.start(config)
}
)
test(
'teardown',
function (t) {
server.stop()
t.end()
}
)
after(() => {
return TestServer.stop(server)
})
})

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

@ -2,24 +2,34 @@
* 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('tap').test
'use strict'
const assert = require('insist')
const Client = require('../client')()
var TestServer = require('../test_server')
var jwtool = require('fxa-jwtool')
var config = require('../../config').getProperties()
process.env.SIGNIN_CONFIRMATION_ENABLED = false
var pubSigKey = jwtool.JWK.fromFile(config.publicKeyFile)
TestServer.start(config)
.then(function main(server) {
describe('remote flow', function() {
this.timeout(15000)
let server
let email1
before(() => {
process.env.SIGNIN_CONFIRMATION_ENABLED = false
return TestServer.start(config)
.then(s => {
server = s
email1 = server.uniqueEmail()
})
})
var email1 = server.uniqueEmail()
test(
it(
'Create account flow',
function (t) {
() => {
var email = email1
var password = 'allyourbasearebelongtous'
var client = null
@ -38,10 +48,10 @@ TestServer.start(config)
)
.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')
assert.ok(Buffer.isBuffer(keys.kA), 'kA exists')
assert.ok(Buffer.isBuffer(keys.wrapKb), 'wrapKb exists')
assert.ok(Buffer.isBuffer(keys.kB), 'kB exists')
assert.equal(client.kB.length, 32, 'kB exists, has the right length')
}
)
.then(
@ -51,17 +61,17 @@ TestServer.start(config)
)
.then(
function (cert) {
t.equal(typeof(cert), 'string', 'cert exists')
assert.equal(typeof(cert), 'string', 'cert exists')
var payload = jwtool.verify(cert, pubSigKey.pem)
t.equal(payload.principal.email.split('@')[0], client.uid, 'cert has correct uid')
assert.equal(payload.principal.email.split('@')[0], client.uid, 'cert has correct uid')
}
)
}
)
test(
it(
'Login flow',
function (t) {
() => {
var email = email1
var password = 'allyourbasearebelongtous'
var client = null
@ -75,17 +85,17 @@ TestServer.start(config)
.then(
function (x) {
client = x
t.ok(client.authAt, 'authAt was set')
t.ok(client.uid, 'got a uid')
assert.ok(client.authAt, 'authAt was set')
assert.ok(client.uid, 'got a uid')
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')
assert.ok(Buffer.isBuffer(keys.kA), 'kA exists')
assert.ok(Buffer.isBuffer(keys.wrapKb), 'wrapKb exists')
assert.ok(Buffer.isBuffer(keys.kB), 'kB exists')
assert.equal(client.kB.length, 32, 'kB exists, has the right length')
}
)
.then(
@ -95,17 +105,14 @@ TestServer.start(config)
)
.then(
function (cert) {
t.equal(typeof(cert), 'string', 'cert exists')
assert.equal(typeof(cert), 'string', 'cert exists')
}
)
}
)
test(
'teardown',
function (t) {
server.stop()
t.end()
}
)
after(() => {
delete process.env.SIGNIN_CONFIRMATION_ENABLED
return TestServer.stop(server)
})
})

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

@ -2,113 +2,119 @@
* 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/. */
'use strict'
const assert = require('insist')
var P = require('bluebird')
var test = require('../ptaptest')
var TestServer = require('../test_server')
var request = P.promisify(require('request'))
test(
'Fails with no sha pins set',
function (t) {
process.env.HPKP_ENABLE = true
process.env.HPKP_PIN_SHA256 = []
var Server = require('../../lib/server')
var config = require('../../config').getProperties()
try{
Server.create({},{},config,{})
t.fail()
t.end()
} catch(e){
t.equal(e.message, 'ValidationError: child "sha256s" fails because ["sha256s" must contain at least 1 items]', 'assert server error if no sha passed')
t.end()
describe('remote hpkp', function() {
this.timeout(30000)
it(
'Fails with no sha pins set',
() => {
process.env.HPKP_ENABLE = true
process.env.HPKP_PIN_SHA256 = []
var Server = require('../../lib/server')
var config = require('../../config').getProperties()
assert.throws(() => {
Server.create({},{},config,{})
}, 'ValidationError: child "sha256s" fails because ["sha256s" must contain at least 1 items]', 'assert server error if no sha passed')
}
}
)
)
test(
'Does not send HPKP header when disabled',
function (t) {
process.env.HPKP_ENABLE = false
it(
'Does not send HPKP header when disabled',
() => {
process.env.HPKP_ENABLE = false
var config = require('../../config').getProperties()
var server
var config = require('../../config').getProperties()
var server
TestServer.start(config)
.then(function main(serverObj) {
server = serverObj
})
.then(function () {
return request({
url: config.publicUrl + '/'
return TestServer.start(config)
.then(function main(serverObj) {
server = serverObj
})
})
.spread(function (res) {
t.equal(res.headers['public-key-pins-report-only'], undefined, 'HPKP header not set')
})
.done(function () {
server.stop()
t.end()
})
}
)
test(
'Sends HPKP header',
function (t) {
process.env.HPKP_ENABLE = true
process.env.HPKP_REPORT_ONLY = false
process.env.HPKP_PIN_SHA256 = ['sha1=', 'sha2=']
var config = require('../../config').getProperties()
var server
TestServer.start(config)
.then(function main(serverObj) {
server = serverObj
})
.then(function () {
return request({
url: config.publicUrl + '/'
.then(function () {
return request({
url: config.publicUrl + '/'
})
})
})
.spread(function (res) {
var headerValue = 'pin-sha256="sha1="; pin-sha256="sha2="; max-age=1; includeSubdomains'
t.equal(res.headers['public-key-pins'], headerValue, 'HPKP header was set correctly')
})
.done(function () {
server.stop()
t.end()
})
}
)
test(
'Sends HPKP report header',
function (t) {
process.env.HPKP_ENABLE = true
process.env.HPKP_REPORT_ONLY = true
process.env.HPKP_PIN_SHA256 = ['sha1=', 'sha2=']
process.env.HPKP_REPORT_URI = 'http://example.com'
var config = require('../../config').getProperties()
var server
TestServer.start(config)
.then(function main(serverObj) {
server = serverObj
})
.then(function () {
return request({
url: config.publicUrl + '/'
.spread(function (res) {
assert.equal(res.headers['public-key-pins-report-only'], undefined, 'HPKP header not set')
})
})
.spread(function (res) {
var headerValue = 'pin-sha256="sha1="; pin-sha256="sha2="; max-age=1; includeSubdomains; report-uri="http://example.com"'
t.equal(res.headers['public-key-pins-report-only'], headerValue, 'HPKP report header was set correctly')
})
.done(function () {
server.stop()
t.end()
})
}
)
.then(function () {
return server.stop()
})
}
)
it(
'Sends HPKP header',
() => {
process.env.HPKP_ENABLE = true
process.env.HPKP_REPORT_ONLY = false
process.env.HPKP_PIN_SHA256 = ['sha1=', 'sha2=']
var config = require('../../config').getProperties()
var server
return TestServer.start(config)
.then(function main(serverObj) {
server = serverObj
})
.then(function () {
return request({
url: config.publicUrl + '/'
})
})
.spread(function (res) {
var headerValue = 'pin-sha256="sha1="; pin-sha256="sha2="; max-age=1; includeSubdomains'
assert.equal(res.headers['public-key-pins'], headerValue, 'HPKP header was set correctly')
})
.then(function () {
return server.stop()
})
}
)
it(
'Sends HPKP report header',
() => {
process.env.HPKP_ENABLE = true
process.env.HPKP_REPORT_ONLY = true
process.env.HPKP_PIN_SHA256 = ['sha1=', 'sha2=']
process.env.HPKP_REPORT_URI = 'http://example.com'
var config = require('../../config').getProperties()
var server
return TestServer.start(config)
.then(function main(serverObj) {
server = serverObj
})
.then(function () {
return request({
url: config.publicUrl + '/'
})
})
.spread(function (res) {
var headerValue = 'pin-sha256="sha1="; pin-sha256="sha2="; max-age=1; includeSubdomains; report-uri="http://example.com"'
assert.equal(res.headers['public-key-pins-report-only'], headerValue, 'HPKP report header was set correctly')
})
.then(function () {
return server.stop()
})
}
)
after(() => {
delete process.env.HPKP_ENABLE
delete process.env.HPKP_REPORT_ONLY
delete process.env.HPKP_PIN_SHA256
delete process.env.HPKP_REPORT_URI
return TestServer.stop()
})
})

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

@ -2,31 +2,37 @@
* 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')
'use strict'
const assert = require('insist')
var TestServer = require('../test_server')
const Client = require('../client')()
var P = require('../../lib/promise')
var hawk = require('hawk')
var request = require('request')
var request = P.promisify(require('request'))
var config = require('../../config').getProperties()
TestServer.start(config)
.then(function main(server) {
describe('remote misc', function() {
this.timeout(15000)
let server
before(() => {
return TestServer.start(config)
.then(s => {
server = s
})
})
function testVersionRoute(route) {
return function (t) {
request(config.publicUrl + route, function (err, res, body) {
t.ok(!err, 'No error fetching ' + route)
return () => {
return request(config.publicUrl + route).spread((res, body) => {
var json = JSON.parse(body)
t.deepEqual(Object.keys(json), ['version', 'commit', 'source'])
t.equal(json.version, require('../../package.json').version, 'package version')
t.ok(json.source && json.source !== 'unknown', 'source repository')
assert.deepEqual(Object.keys(json), ['version', 'commit', 'source'])
assert.equal(json.version, require('../../package.json').version, 'package version')
assert.ok(json.source && json.source !== 'unknown', 'source repository')
// check that the git hash just looks like a hash
t.ok(json.commit.match(/^[0-9a-f]{40}$/), 'The git hash actually looks like one')
t.end()
assert.ok(json.commit.match(/^[0-9a-f]{40}$/), 'The git hash actually looks like one')
})
}
}
@ -35,7 +41,7 @@ TestServer.start(config)
var randomAllowedOrigin = config.corsOrigin[Math.floor(Math.random() * config.corsOrigin.length)]
var expectedOrigin = withAllowedOrigin ? randomAllowedOrigin : undefined
return function(t) {
return () => {
var options = {
url: config.publicUrl + '/'
}
@ -44,87 +50,81 @@ TestServer.start(config)
'Origin': (withAllowedOrigin ? randomAllowedOrigin : 'http://notallowed')
}
}
request(options, function(err, res, body) {
t.equal(res.headers['access-control-allow-origin'], expectedOrigin, 'Access-Control-Allow-Origin header was set correctly')
t.end()
return request(options).spread((res, body) => {
assert.equal(res.headers['access-control-allow-origin'], expectedOrigin, 'Access-Control-Allow-Origin header was set correctly')
})
}
}
test(
it(
'unsupported api version',
function (t) {
request(config.publicUrl + '/v0/account/create', function (err, res) {
t.equal(res.statusCode, 410, 'http gone')
t.end()
() => {
return request(config.publicUrl + '/v0/account/create').spread((res) => {
assert.equal(res.statusCode, 410, 'http gone')
})
}
)
test(
it(
'/ returns version, git hash and source repo',
testVersionRoute('/')
)
test(
it(
'/__version__ returns version, git hash and source repo',
testVersionRoute('/__version__')
)
test(
it(
'returns no Access-Control-Allow-Origin with no Origin set',
testCORSHeader(undefined)
)
test(
it(
'returns correct Access-Control-Allow-Origin with whitelisted Origin',
testCORSHeader(true)
)
test(
it(
'returns no Access-Control-Allow-Origin with not whitelisted Origin',
testCORSHeader(false)
)
test(
it(
'/verify_email redirects',
function (t) {
() => {
var path = '/v1/verify_email?code=0000&uid=0000'
request(
return request(
{
url: config.publicUrl + path,
followRedirect: false
},
function (err, res, body) {
t.equal(res.statusCode, 302, 'redirected')
//t.equal(res.headers.location, config.contentServer.url + path)
t.end()
}
)
})
.spread((res, body) => {
assert.equal(res.statusCode, 302, 'redirected')
//assert.equal(res.headers.location, config.contentServer.url + path)
})
}
)
test(
it(
'/complete_reset_password redirects',
function (t) {
() => {
var path = '/v1/complete_reset_password?code=0000&email=a@b.c&token=0000'
request(
return request(
{
url: config.publicUrl + path,
followRedirect: false
},
function (err, res, body) {
t.equal(res.statusCode, 302, 'redirected')
//t.equal(res.headers.location, config.contentServer.url + path)
t.end()
}
)
})
.spread((res, body) => {
assert.equal(res.statusCode, 302, 'redirected')
//assert.equal(res.headers.location, config.contentServer.url + path)
})
}
)
test(
it(
'timestamp header',
function (t) {
() => {
var email = server.uniqueEmail()
var password = 'allyourbasearebelongtous'
var url = null
@ -144,7 +144,6 @@ TestServer.start(config)
)
.then(
function (token) {
var d = P.defer()
var method = 'GET'
var verify = {
credentials: token,
@ -153,48 +152,39 @@ TestServer.start(config)
var headers = {
Authorization: hawk.client.header(url, method, verify).field
}
request(
return request(
{
method: method,
url: url,
headers: headers,
json: true
},
function (err, res, body) {
if (err) {
d.reject(err)
} else {
var now = +new Date() / 1000
t.ok(res.headers.timestamp > now - 60, 'has timestamp header')
t.ok(res.headers.timestamp < now + 60, 'has timestamp header')
d.resolve()
}
}
)
return d.promise
})
.spread((res, body) => {
var now = +new Date() / 1000
assert.ok(res.headers.timestamp > now - 60, 'has timestamp header')
assert.ok(res.headers.timestamp < now + 60, 'has timestamp header')
})
}
)
}
)
test(
it(
'Strict-Transport-Security header',
function (t) {
request(
() => {
return request(
{
url: config.publicUrl + '/'
},
function (err, res, body) {
t.equal(res.headers['strict-transport-security'], 'max-age=15552000; includeSubDomains')
t.end()
}
)
})
.spread((res, body) => {
assert.equal(res.headers['strict-transport-security'], 'max-age=15552000; includeSubDomains')
})
}
)
test(
it(
'oversized payload',
function (t) {
() => {
var client = new Client(config.publicUrl)
return client.api.doRequest(
'POST',
@ -205,37 +195,37 @@ TestServer.start(config)
)
.then(
function (body) {
t.fail('request should have failed')
assert(false, 'request should have failed')
},
function (err) {
if (err.errno) {
t.equal(err.errno, 113, 'payload too large')
assert.equal(err.errno, 113, 'payload too large')
}
else {
// nginx returns an html response
t.ok(/413 Request Entity Too Large/.test(err), 'payload too large')
assert.ok(/413 Request Entity Too Large/.test(err), 'payload too large')
}
}
)
}
)
test(
it(
'random bytes',
function (t) {
() => {
var client = new Client(config.publicUrl)
return client.api.getRandomBytes()
.then(
function (x) {
t.equal(x.data.length, 64)
assert.equal(x.data.length, 64)
}
)
}
)
test(
it(
'fetch /.well-known/browserid support document',
function (t) {
() => {
var client = new Client(config.publicUrl)
function fetch(url) {
return client.api.doRequest('GET', config.publicUrl + url)
@ -243,12 +233,12 @@ TestServer.start(config)
return fetch('/.well-known/browserid')
.then(
function (doc) {
t.ok(doc.hasOwnProperty('public-key'), 'doc has public key')
t.ok(/^[0-9]+$/.test(doc['public-key'].n), 'n is base 10')
t.ok(/^[0-9]+$/.test(doc['public-key'].e), 'e is base 10')
t.ok(doc.hasOwnProperty('authentication'), 'doc has auth page')
t.ok(doc.hasOwnProperty('provisioning'), 'doc has provisioning page')
t.equal(doc.keys.length, 1)
assert.ok(doc.hasOwnProperty('public-key'), 'doc has public key')
assert.ok(/^[0-9]+$/.test(doc['public-key'].n), 'n is base 10')
assert.ok(/^[0-9]+$/.test(doc['public-key'].e), 'e is base 10')
assert.ok(doc.hasOwnProperty('authentication'), 'doc has auth page')
assert.ok(doc.hasOwnProperty('provisioning'), 'doc has provisioning page')
assert.equal(doc.keys.length, 1)
return doc
}
)
@ -257,7 +247,7 @@ TestServer.start(config)
return fetch(doc.authentication)
.then(
function (authPage) {
t.ok(authPage, 'auth page can be fetched')
assert.ok(authPage, 'auth page can be fetched')
return doc
}
)
@ -268,7 +258,7 @@ TestServer.start(config)
return fetch(doc.provisioning)
.then(
function (provPage) {
t.ok(provPage, 'provisioning page can be fetched')
assert.ok(provPage, 'provisioning page can be fetched')
return doc
}
)
@ -277,11 +267,7 @@ TestServer.start(config)
}
)
test(
'teardown',
function (t) {
server.stop()
t.end()
}
)
after(() => {
return TestServer.stop(server)
})
})

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

@ -2,9 +2,11 @@
* 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/. */
'use strict'
const assert = require('insist')
const Client = require('../client')()
var config = require('../../config').getProperties()
var test = require('../ptaptest')
var TestServer = require('../test_server')
var url = require('url')
@ -18,15 +20,22 @@ function getSessionTokenId(sessionTokenHex) {
)
}
process.env.SIGNIN_CONFIRMATION_ENABLED = true
process.env.SIGNIN_CONFIRMATION_RATE = 1.0
describe('remote password change', function() {
this.timeout(15000)
let server
before(() => {
process.env.SIGNIN_CONFIRMATION_ENABLED = true
process.env.SIGNIN_CONFIRMATION_RATE = 1.0
TestServer.start(config)
.then(function main(server) {
return TestServer.start(config)
.then(s => {
server = s
})
})
test(
it(
'password change, with unverified session',
function (t) {
() => {
var email = server.uniqueEmail()
var password = 'allyourbasearebelongtous'
var newPassword = 'foobar'
@ -54,7 +63,7 @@ TestServer.start(config)
)
.then(
function (status) {
t.equal(status.verified, true, 'account is verified')
assert.equal(status.verified, true, 'account is verified')
}
)
.then(
@ -82,9 +91,9 @@ TestServer.start(config)
.then(
function (status) {
// Verify correct status
t.equal(status.verified, false, 'account is unverified')
t.equal(status.emailVerified, true, 'account email is verified')
t.equal(status.sessionVerified, false, 'account session is unverified')
assert.equal(status.verified, false, 'account is unverified')
assert.equal(status.emailVerified, true, 'account email is verified')
assert.equal(status.sessionVerified, false, 'account session is unverified')
}
)
.then(
@ -100,9 +109,9 @@ TestServer.start(config)
.then(
function (response) {
// Verify correct change password response
t.notEqual(response.sessionToken, originalSessionToken, 'session token has changed')
t.ok(response.keyFetchToken, 'key fetch token returned')
t.notEqual(client.authPW.toString('hex'), firstAuthPW, 'password has changed')
assert.notEqual(response.sessionToken, originalSessionToken, 'session token has changed')
assert.ok(response.keyFetchToken, 'key fetch token returned')
assert.notEqual(client.authPW.toString('hex'), firstAuthPW, 'password has changed')
}
)
.then(
@ -113,10 +122,10 @@ TestServer.start(config)
.then(
function (emailData) {
var subject = emailData.headers['subject']
t.equal(subject, 'Your Firefox Account password has been changed', 'password email subject set correctly')
assert.equal(subject, 'Your Firefox Account password has been changed', 'password email subject set correctly')
var link = emailData.headers['x-link']
var query = url.parse(link, true).query
t.ok(query.email, 'email is in the link')
assert.ok(query.email, 'email is in the link')
}
)
.then(
@ -127,9 +136,9 @@ TestServer.start(config)
.then(
function (status) {
// Verify correct status
t.equal(status.verified, false, 'account is unverified')
t.equal(status.emailVerified, true, 'account email is verified')
t.equal(status.sessionVerified, false, 'account session is unverified')
assert.equal(status.verified, false, 'account is unverified')
assert.equal(status.emailVerified, true, 'account email is verified')
assert.equal(status.sessionVerified, false, 'account session is unverified')
}
)
.then(
@ -145,16 +154,16 @@ TestServer.start(config)
)
.then(
function (keys) {
t.deepEqual(keys.kB, kB, 'kB is preserved')
t.deepEqual(keys.kA, kA, 'kA is preserved')
assert.deepEqual(keys.kB, kB, 'kB is preserved')
assert.deepEqual(keys.kA, kA, 'kA is preserved')
}
)
}
)
test(
it(
'password change, with verified session',
function (t) {
() => {
var email = server.uniqueEmail()
var password = 'allyourbasearebelongtous'
var newPassword = 'foobar'
@ -182,7 +191,7 @@ TestServer.start(config)
)
.then(
function (status) {
t.equal(status.verified, true, 'account is verified')
assert.equal(status.verified, true, 'account is verified')
}
)
.then(
@ -197,9 +206,9 @@ TestServer.start(config)
)
.then(
function (response) {
t.notEqual(response.sessionToken, originalSessionToken, 'session token has changed')
t.ok(response.keyFetchToken, 'key fetch token returned')
t.notEqual(client.authPW.toString('hex'), firstAuthPW, 'password has changed')
assert.notEqual(response.sessionToken, originalSessionToken, 'session token has changed')
assert.ok(response.keyFetchToken, 'key fetch token returned')
assert.notEqual(client.authPW.toString('hex'), firstAuthPW, 'password has changed')
}
)
.then(
@ -210,10 +219,10 @@ TestServer.start(config)
.then(
function (emailData) {
var subject = emailData.headers['subject']
t.equal(subject, 'Your Firefox Account password has been changed', 'password email subject set correctly')
assert.equal(subject, 'Your Firefox Account password has been changed', 'password email subject set correctly')
var link = emailData.headers['x-link']
var query = url.parse(link, true).query
t.ok(query.email, 'email is in the link')
assert.ok(query.email, 'email is in the link')
}
)
.then(
@ -223,7 +232,7 @@ TestServer.start(config)
)
.then(
function (status) {
t.equal(status.verified, true, 'account is verified')
assert.equal(status.verified, true, 'account is verified')
}
)
.then(
@ -239,16 +248,16 @@ TestServer.start(config)
)
.then(
function (keys) {
t.deepEqual(keys.kB, kB, 'kB is preserved')
t.deepEqual(keys.kA, kA, 'kA is preserved')
assert.deepEqual(keys.kB, kB, 'kB is preserved')
assert.deepEqual(keys.kA, kA, 'kA is preserved')
}
)
}
)
test(
it(
'password change, with raw session data rather than session token id, return invalid token error',
function (t) {
() => {
var email = server.uniqueEmail()
var password = 'allyourbasearebelongtous'
var newPassword = 'foobar'
@ -268,7 +277,7 @@ TestServer.start(config)
)
.then(
function (status) {
t.equal(status.verified, true, 'account is verified')
assert.equal(status.verified, true, 'account is verified')
}
)
.then(
@ -278,21 +287,19 @@ TestServer.start(config)
)
.then(
function () {
t.fail()
}
)
.catch(
assert(false)
},
function (err) {
t.equal(err.errno, 110, 'Invalid token error')
t.equal(err.message, 'Invalid authentication token in request signature')
assert.equal(err.errno, 110, 'Invalid token error')
assert.equal(err.message, 'Invalid authentication token in request signature')
}
)
}
)
test(
it(
'password change w/o sessionToken',
function (t) {
() => {
var email = server.uniqueEmail()
var password = 'allyourbasearebelongtous'
var newPassword = 'foobar'
@ -319,9 +326,9 @@ TestServer.start(config)
)
.then(
function (response) {
t.notOk(response.sessionToken, 'no session token returned')
t.notOk(response.keyFetchToken, 'no key fetch token returned')
t.notEqual(client.authPW.toString('hex'), firstAuthPW, 'password has changed')
assert(!response.sessionToken, 'no session token returned')
assert(!response.keyFetchToken, 'no key fetch token returned')
assert.notEqual(client.authPW.toString('hex'), firstAuthPW, 'password has changed')
}
)
.then(
@ -332,10 +339,10 @@ TestServer.start(config)
.then(
function (emailData) {
var subject = emailData.headers['subject']
t.equal(subject, 'Your Firefox Account password has been changed', 'password email subject set correctly')
assert.equal(subject, 'Your Firefox Account password has been changed', 'password email subject set correctly')
var link = emailData.headers['x-link']
var query = url.parse(link, true).query
t.ok(query.email, 'email is in the link')
assert.ok(query.email, 'email is in the link')
}
)
.then(
@ -351,16 +358,16 @@ TestServer.start(config)
)
.then(
function (keys) {
t.deepEqual(keys.kB, kB, 'kB is preserved')
t.deepEqual(keys.kA, kA, 'kA is preserved')
assert.deepEqual(keys.kB, kB, 'kB is preserved')
assert.deepEqual(keys.kA, kA, 'kA is preserved')
}
)
}
)
test(
it(
'wrong password on change start',
function (t) {
() => {
var email = server.uniqueEmail()
var password = 'allyourbasearebelongtous'
var client = null
@ -378,19 +385,17 @@ TestServer.start(config)
}
)
.then(
t.fail,
() => assert(false),
function (err) {
t.equal(err.errno, 103, 'invalid password')
assert.equal(err.errno, 103, 'invalid password')
}
)
}
)
test(
'teardown',
function (t) {
server.stop()
t.end()
}
)
after(() => {
delete process.env.SIGNIN_CONFIRMATION_ENABLED
delete process.env.SIGNIN_CONFIRMATION_RATE
return TestServer.stop(server)
})
})

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

@ -2,7 +2,9 @@
* 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('tap').test
'use strict'
const assert = require('insist')
var url = require('url')
const Client = require('../client')()
var TestServer = require('../test_server')
@ -10,14 +12,21 @@ var crypto = require('crypto')
var base64url = require('base64url')
var config = require('../../config').getProperties()
process.env.SIGNIN_CONFIRMATION_ENABLED = false
TestServer.start(config)
.then(function main(server) {
describe('remote password forgot', function() {
this.timeout(15000)
let server
before(() => {
process.env.SIGNIN_CONFIRMATION_ENABLED = false
return TestServer.start(config)
.then(s => {
server = s
})
})
test(
it(
'forgot password',
function (t) {
() => {
var email = server.uniqueEmail()
var password = 'allyourbasearebelongtous'
var newPassword = 'ez'
@ -45,7 +54,7 @@ TestServer.start(config)
)
.then(
function (code) {
t.throws(function() { client.resetPassword(newPassword) })
assert.throws(function() { client.resetPassword(newPassword) })
return resetPassword(client, code, newPassword)
}
)
@ -58,7 +67,7 @@ TestServer.start(config)
function (emailData) {
var link = emailData.headers['x-link']
var query = url.parse(link, true).query
t.ok(query.email, 'email is in the link')
assert.ok(query.email, 'email is in the link')
}
)
.then(
@ -68,10 +77,10 @@ TestServer.start(config)
)
.then(
function (keys) {
t.ok(Buffer.isBuffer(keys.wrapKb), 'yep, wrapKb')
t.notDeepEqual(wrapKb, keys.wrapKb, 'wrapKb was reset')
t.deepEqual(kA, keys.kA, 'kA was not reset')
t.equal(client.kB.length, 32, 'kB exists, has the right length')
assert.ok(Buffer.isBuffer(keys.wrapKb), 'yep, wrapKb')
assert.notDeepEqual(wrapKb, keys.wrapKb, 'wrapKb was reset')
assert.deepEqual(kA, keys.kA, 'kA was not reset')
assert.equal(client.kB.length, 32, 'kB exists, has the right length')
}
)
.then( // make sure we can still login after password reset
@ -88,9 +97,9 @@ TestServer.start(config)
}
)
test(
it(
'forgot password limits verify attempts',
function (t) {
() => {
var code = null
var email = server.uniqueEmail()
var password = 'hothamburger'
@ -125,7 +134,7 @@ TestServer.start(config)
)
.then(
function (c) {
t.equal(code, c, 'same code as before')
assert.equal(code, c, 'same code as before')
}
)
.then(
@ -135,11 +144,11 @@ TestServer.start(config)
)
.then(
function () {
t.fail('reset password with bad code')
assert(false, 'reset password with bad code')
},
function (err) {
t.equal(err.tries, 2, 'used a try')
t.equal(err.message, 'Invalid verification code', 'bad attempt 1')
assert.equal(err.tries, 2, 'used a try')
assert.equal(err.message, 'Invalid verification code', 'bad attempt 1')
}
)
.then(
@ -149,11 +158,11 @@ TestServer.start(config)
)
.then(
function () {
t.fail('reset password with bad code')
assert(false, 'reset password with bad code')
},
function (err) {
t.equal(err.tries, 1, 'used a try')
t.equal(err.message, 'Invalid verification code', 'bad attempt 2')
assert.equal(err.tries, 1, 'used a try')
assert.equal(err.message, 'Invalid verification code', 'bad attempt 2')
}
)
.then(
@ -163,11 +172,11 @@ TestServer.start(config)
)
.then(
function () {
t.fail('reset password with bad code')
assert(false, 'reset password with bad code')
},
function (err) {
t.equal(err.tries, 0, 'used a try')
t.equal(err.message, 'Invalid verification code', 'bad attempt 3')
assert.equal(err.tries, 0, 'used a try')
assert.equal(err.message, 'Invalid verification code', 'bad attempt 3')
}
)
.then(
@ -177,18 +186,18 @@ TestServer.start(config)
)
.then(
function () {
t.fail('reset password with invalid token')
assert(false, 'reset password with invalid token')
},
function (err) {
t.equal(err.message, 'Invalid authentication token in request signature', 'token is now invalid')
assert.equal(err.message, 'Invalid authentication token in request signature', 'token is now invalid')
}
)
}
)
test(
it(
'recovery email link',
function (t) {
() => {
var email = server.uniqueEmail()
var password = 'something'
var client = null
@ -221,19 +230,19 @@ TestServer.start(config)
function (emailData) {
var link = emailData.headers['x-link']
var query = url.parse(link, true).query
t.ok(query.token, 'uid is in link')
t.ok(query.code, 'code is in link')
t.equal(query.redirectTo, options.redirectTo, 'redirectTo is in link')
t.equal(query.service, options.service, 'service is in link')
t.equal(query.email, email, 'email is in link')
assert.ok(query.token, 'uid is in link')
assert.ok(query.code, 'code is in link')
assert.equal(query.redirectTo, options.redirectTo, 'redirectTo is in link')
assert.equal(query.service, options.service, 'service is in link')
assert.equal(query.email, email, 'email is in link')
}
)
}
)
test(
it(
'password forgot status with valid token',
function (t) {
() => {
var email = server.uniqueEmail()
var password = 'something'
return Client.create(config.publicUrl, email, password)
@ -247,8 +256,8 @@ TestServer.start(config)
)
.then(
function (x) {
t.equal(x.tries, 3, 'three tries remaining')
t.ok(x.ttl > 0 && x.ttl <= (60 * 60), 'ttl is ok')
assert.equal(x.tries, 3, 'three tries remaining')
assert.ok(x.ttl > 0 && x.ttl <= (60 * 60), 'ttl is ok')
}
)
}
@ -256,23 +265,23 @@ TestServer.start(config)
}
)
test(
it(
'password forgot status with invalid token',
function (t) {
() => {
var client = new Client(config.publicUrl)
return client.api.passwordForgotStatus('0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF')
.then(
t.fail,
() => assert(false),
function (err) {
t.equal(err.errno, 110, 'invalid token')
assert.equal(err.errno, 110, 'invalid token')
}
)
}
)
test(
it(
'/password/forgot/verify_code should set an unverified account as verified',
function (t) {
() => {
var email = server.uniqueEmail()
var password = 'something'
var client = null
@ -285,7 +294,7 @@ TestServer.start(config)
)
.then(
function (status) {
t.equal(status.verified, false, 'email unverified')
assert.equal(status.verified, false, 'email unverified')
}
)
.then(
@ -315,15 +324,15 @@ TestServer.start(config)
)
.then(
function (status) {
t.equal(status.verified, true, 'account unverified')
assert.equal(status.verified, true, 'account unverified')
}
)
}
)
test(
it(
'forgot password with service query parameter',
function (t) {
() => {
var email = server.uniqueEmail()
var options = {
redirectTo: 'https://sync.' + config.smtp.redirectDomain,
@ -346,14 +355,14 @@ TestServer.start(config)
.then(function (emailData) {
var link = emailData.headers['x-link']
var query = url.parse(link, true).query
t.equal(query.service, options.serviceQuery, 'service is in link')
assert.equal(query.service, options.serviceQuery, 'service is in link')
})
}
)
test(
it(
'forgot password, then get device list',
function (t) {
() => {
var email = server.uniqueEmail()
var newPassword = 'foo'
var client
@ -377,7 +386,7 @@ TestServer.start(config)
)
.then(
function (devices) {
t.equal(devices.length, 1, 'devices list contains 1 item')
assert.equal(devices.length, 1, 'devices list contains 1 item')
}
)
.then(
@ -407,24 +416,22 @@ TestServer.start(config)
)
.then(
function (devices) {
t.equal(devices.length, 0, 'devices list is empty')
assert.equal(devices.length, 0, 'devices list is empty')
}
)
}
)
test(
'teardown',
function (t) {
server.stop()
t.end()
}
)
})
after(() => {
delete process.env.SIGNIN_CONFIRMATION_ENABLED
return TestServer.stop(server)
})
function resetPassword(client, code, newPassword, options) {
return client.verifyPasswordResetCode(code)
.then(function() {
return client.resetPassword(newPassword, {}, options)
})
}
function resetPassword(client, code, newPassword, options) {
return client.verifyPasswordResetCode(code)
.then(function() {
return client.resetPassword(newPassword, {}, options)
})
}
})

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

@ -2,14 +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/. */
var tap = require('tap')
var test = tap.test
'use strict'
const assert = require('insist')
var P = require('../../lib/promise')
var uuid = require('uuid')
var crypto = require('crypto')
var base64url = require('base64url')
var proxyquire = require('proxyquire')
var log = { trace: console.log, info: console.log } // eslint-disable-line no-console
var log = { trace() {}, info() {} }
var config = require('../../config').getProperties()
var TestServer = require('../test_server')
@ -52,115 +53,113 @@ var mockLog = {
}
}
var dbServer
var dbConn = TestServer.start(config)
.then(
function (server) {
dbServer = server
describe('remote push db', function() {
this.timeout(15000)
let dbServer, db
before(() => {
return TestServer.start(config)
.then(s => {
dbServer = s
return DB.connect(config[config.db.backend])
}
)
})
.then(x => {
db = x
})
})
test(
'push db tests',
function (t) {
var sessionTokenId
var deviceInfo = {
id: crypto.randomBytes(16),
name: 'my push device',
type: 'mobile',
pushCallback: 'https://foo/bar',
pushPublicKey: base64url(Buffer.concat([new Buffer('\x04'), crypto.randomBytes(64)])),
pushAuthKey: base64url(crypto.randomBytes(16))
}
// two tests below, first for unknown 400 level error the device push info will stay the same
// second, for a known 400 error we reset the device
var mocksKnown400 = {
'web-push': {
sendNotification: function (endpoint, params) {
var err = new Error('Failed 400 level')
err.statusCode = 410
return P.reject(err)
it(
'push db tests',
() => {
var sessionTokenId
var deviceInfo = {
id: crypto.randomBytes(16),
name: 'my push device',
type: 'mobile',
pushCallback: 'https://foo/bar',
pushPublicKey: base64url(Buffer.concat([new Buffer('\x04'), crypto.randomBytes(64)])),
pushAuthKey: base64url(crypto.randomBytes(16))
}
// two tests below, first for unknown 400 level error the device push info will stay the same
// second, for a known 400 error we reset the device
var mocksKnown400 = {
'web-push': {
sendNotification: function (endpoint, params) {
var err = new Error('Failed 400 level')
err.statusCode = 410
return P.reject(err)
}
}
}
}
var mocksUnknown400 = {
'web-push': {
sendNotification: function (endpoint, params) {
var err = new Error('Failed 429 level')
err.statusCode = 429
return P.reject(err)
var mocksUnknown400 = {
'web-push': {
sendNotification: function (endpoint, params) {
var err = new Error('Failed 429 level')
err.statusCode = 429
return P.reject(err)
}
}
}
}
dbConn.then(function (db) {
return db.createAccount(ACCOUNT)
.then(function() {
return db.emailRecord(ACCOUNT.email)
})
.then(function(emailRecord) {
emailRecord.createdAt = Date.now()
return db.createSessionToken(emailRecord, SESSION_TOKEN_UA)
})
.then(function() {
return db.emailRecord(ACCOUNT.email)
})
.then(function(emailRecord) {
emailRecord.createdAt = Date.now()
return db.createSessionToken(emailRecord, SESSION_TOKEN_UA)
})
.then(function (sessionToken) {
sessionTokenId = sessionToken.tokenId
return db.createDevice(ACCOUNT.uid, sessionTokenId, deviceInfo)
})
.then(function (device) {
t.equal(device.name, deviceInfo.name)
t.equal(device.pushCallback, deviceInfo.pushCallback)
t.equal(device.pushPublicKey, deviceInfo.pushPublicKey)
t.equal(device.pushAuthKey, deviceInfo.pushAuthKey)
})
.then(function () {
return db.devices(ACCOUNT.uid)
})
.then(function (sessionToken) {
sessionTokenId = sessionToken.tokenId
return db.createDevice(ACCOUNT.uid, sessionTokenId, deviceInfo)
})
.then(function (device) {
assert.equal(device.name, deviceInfo.name)
assert.equal(device.pushCallback, deviceInfo.pushCallback)
assert.equal(device.pushPublicKey, deviceInfo.pushPublicKey)
assert.equal(device.pushAuthKey, deviceInfo.pushAuthKey)
})
.then(function () {
return db.devices(ACCOUNT.uid)
})
.then(function () {
var pushWithUnknown400 = proxyquire('../../lib/push', mocksUnknown400)(mockLog, db, {})
return pushWithUnknown400.pushToAllDevices(ACCOUNT.uid, 'accountVerify')
})
.then(function () {
return db.devices(ACCOUNT.uid)
})
.then(function (devices) {
var device = devices[0]
t.equal(device.name, deviceInfo.name)
t.equal(device.pushCallback, deviceInfo.pushCallback)
t.equal(device.pushPublicKey, deviceInfo.pushPublicKey, 'device.pushPublicKey is correct')
t.equal(device.pushAuthKey, deviceInfo.pushAuthKey, 'device.pushAuthKey is correct')
})
.then(function () {
var pushWithUnknown400 = proxyquire('../../lib/push', mocksUnknown400)(mockLog, db, {})
return pushWithUnknown400.pushToAllDevices(ACCOUNT.uid, 'accountVerify')
})
.then(function () {
return db.devices(ACCOUNT.uid)
})
.then(function (devices) {
var device = devices[0]
assert.equal(device.name, deviceInfo.name)
assert.equal(device.pushCallback, deviceInfo.pushCallback)
assert.equal(device.pushPublicKey, deviceInfo.pushPublicKey, 'device.pushPublicKey is correct')
assert.equal(device.pushAuthKey, deviceInfo.pushAuthKey, 'device.pushAuthKey is correct')
})
.then(function () {
var pushWithKnown400 = proxyquire('../../lib/push', mocksKnown400)(mockLog, db, {})
return pushWithKnown400.pushToAllDevices(ACCOUNT.uid, 'accountVerify')
})
.then(function () {
return db.devices(ACCOUNT.uid)
})
.then(function (devices) {
var device = devices[0]
t.equal(device.name, deviceInfo.name)
t.equal(device.pushCallback, '')
t.equal(device.pushPublicKey, '', 'device.pushPublicKey is correct')
t.equal(device.pushAuthKey, '', 'device.pushAuthKey is correct')
t.end()
})
})
}
)
.then(function () {
var pushWithKnown400 = proxyquire('../../lib/push', mocksKnown400)(mockLog, db, {})
return pushWithKnown400.pushToAllDevices(ACCOUNT.uid, 'accountVerify')
})
.then(function () {
return db.devices(ACCOUNT.uid)
})
.then(function (devices) {
var device = devices[0]
assert.equal(device.name, deviceInfo.name)
assert.equal(device.pushCallback, '')
assert.equal(device.pushPublicKey, '', 'device.pushPublicKey is correct')
assert.equal(device.pushAuthKey, '', 'device.pushAuthKey is correct')
})
}
)
test(
'teardown',
function (t) {
return dbConn.then(function(db) {
return db.close()
}).then(function() {
return dbServer.stop()
}).then(function () {
t.end()
})
}
)
after(() => {
return P.all([
TestServer.stop(dbServer),
db.close()
])
})
})

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

@ -2,147 +2,154 @@
* 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')
'use strict'
const assert = require('insist')
const Client = require('../client')()
var TestServer = require('../test_server')
var config = require('../../config').getProperties()
process.env.SIGNIN_CONFIRMATION_ENABLED = true
process.env.SIGNIN_CONFIRMATION_RATE = 1.0
describe('remote recovery email resend code', function() {
this.timeout(15000)
let server
before(() => {
process.env.SIGNIN_CONFIRMATION_ENABLED = true
process.env.SIGNIN_CONFIRMATION_RATE = 1.0
TestServer.start(config)
.then(function main(server) {
test(
'sign-in verification resend email verify code',
function (t) {
var email = server.uniqueEmail()
var password = 'something'
var verifyEmailCode = ''
var client = null
var options = {
redirectTo: 'https://sync.' + config.smtp.redirectDomain,
service: 'sync',
resume: 'resumeToken',
keys: true
}
return Client.create(config.publicUrl, email, password, server.mailbox, options)
.then(
function (c) {
client = c
return Client.login(config.publicUrl, email, password, server.mailbox, options)
}
)
.then(
function () {
return server.mailbox.waitForCode(email)
}
)
.then(
function (code) {
verifyEmailCode = code
return client.requestVerifyEmail()
}
)
.then(
function () {
return server.mailbox.waitForCode(email)
}
)
.then(
function (code) {
t.equal(code, verifyEmailCode, 'code equal to verify email code')
return client.verifyEmail(code)
}
)
.then(
function () {
return client.emailStatus()
}
)
.then(
function (status) {
t.equal(status.verified, true, 'account is verified')
t.equal(status.emailVerified, true, 'account email is verified')
t.equal(status.sessionVerified, true, 'account session is verified')
}
)
}
)
test(
'sign-in verification resend login verify code',
function (t) {
var email = server.uniqueEmail()
var password = 'something'
var verifyEmailCode = ''
var client2 = null
var options = {
redirectTo: 'https://sync.' + config.smtp.redirectDomain,
service: 'sync',
resume: 'resumeToken',
keys: true
}
return Client.createAndVerify(config.publicUrl, email, password, server.mailbox, options)
.then(
function () {
// Attempt to login from new location
return Client.login(config.publicUrl, email, password, server.mailbox, options)
}
)
.then(
function (c) {
client2 = c
}
)
.then(
function () {
return client2.login(options)
}
)
.then(
function () {
return server.mailbox.waitForCode(email)
}
)
.then(
function (code) {
verifyEmailCode = code
return client2.requestVerifyEmail()
}
)
.then(
function () {
return server.mailbox.waitForCode(email)
}
)
.then(
function (code) {
t.equal(code, verifyEmailCode, 'code equal to verify email code')
return client2.verifyEmail(code)
}
)
.then(
function () {
return client2.emailStatus()
}
)
.then(
function (status) {
t.equal(status.verified, true, 'account is verified')
t.equal(status.emailVerified, true, 'account email is verified')
t.equal(status.sessionVerified, true, 'account session is verified')
}
)
}
)
test(
'teardown',
function (t) {
server.stop()
t.end()
}
)
return TestServer.start(config)
.then(s => {
server = s
})
})
it(
'sign-in verification resend email verify code',
() => {
var email = server.uniqueEmail()
var password = 'something'
var verifyEmailCode = ''
var client = null
var options = {
redirectTo: 'https://sync.' + config.smtp.redirectDomain,
service: 'sync',
resume: 'resumeToken',
keys: true
}
return Client.create(config.publicUrl, email, password, server.mailbox, options)
.then(
function (c) {
client = c
return Client.login(config.publicUrl, email, password, server.mailbox, options)
}
)
.then(
function () {
return server.mailbox.waitForCode(email)
}
)
.then(
function (code) {
verifyEmailCode = code
return client.requestVerifyEmail()
}
)
.then(
function () {
return server.mailbox.waitForCode(email)
}
)
.then(
function (code) {
assert.equal(code, verifyEmailCode, 'code equal to verify email code')
return client.verifyEmail(code)
}
)
.then(
function () {
return client.emailStatus()
}
)
.then(
function (status) {
assert.equal(status.verified, true, 'account is verified')
assert.equal(status.emailVerified, true, 'account email is verified')
assert.equal(status.sessionVerified, true, 'account session is verified')
}
)
}
)
it(
'sign-in verification resend login verify code',
() => {
var email = server.uniqueEmail()
var password = 'something'
var verifyEmailCode = ''
var client2 = null
var options = {
redirectTo: 'https://sync.' + config.smtp.redirectDomain,
service: 'sync',
resume: 'resumeToken',
keys: true
}
return Client.createAndVerify(config.publicUrl, email, password, server.mailbox, options)
.then(
function () {
// Attempt to login from new location
return Client.login(config.publicUrl, email, password, server.mailbox, options)
}
)
.then(
function (c) {
client2 = c
}
)
.then(
function () {
return client2.login(options)
}
)
.then(
function () {
return server.mailbox.waitForCode(email)
}
)
.then(
function (code) {
verifyEmailCode = code
return client2.requestVerifyEmail()
}
)
.then(
function () {
return server.mailbox.waitForCode(email)
}
)
.then(
function (code) {
assert.equal(code, verifyEmailCode, 'code equal to verify email code')
return client2.verifyEmail(code)
}
)
.then(
function () {
return client2.emailStatus()
}
)
.then(
function (status) {
assert.equal(status.verified, true, 'account is verified')
assert.equal(status.emailVerified, true, 'account email is verified')
assert.equal(status.sessionVerified, true, 'account session is verified')
}
)
}
)
after(() => {
delete process.env.SIGNIN_CONFIRMATION_ENABLED
delete process.env.SIGNIN_CONFIRMATION_RATE
return TestServer.stop(server)
})
})

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

@ -2,19 +2,28 @@
* 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')
'use strict'
const assert = require('insist')
var url = require('url')
const Client = require('../client')()
var TestServer = require('../test_server')
var config = require('../../config').getProperties()
TestServer.start(config)
.then(function main(server) {
describe('remote recovery email verify', function() {
this.timeout(15000)
let server
before(() => {
return TestServer.start(config)
.then(s => {
server = s
})
})
test(
it(
'create account verify with incorrect code',
function (t) {
() => {
var email = server.uniqueEmail()
var password = 'allyourbasearebelongtous'
var client = null
@ -31,7 +40,7 @@ TestServer.start(config)
)
.then(
function (status) {
t.equal(status.verified, false, 'new account is not verified')
assert.equal(status.verified, false, 'new account is not verified')
}
)
.then(
@ -41,10 +50,10 @@ TestServer.start(config)
)
.then(
function () {
t.fail('verified email with bad code')
assert(false, 'verified email with bad code')
},
function (err) {
t.equal(err.message.toString(), 'Invalid verification code', 'bad attempt')
assert.equal(err.message.toString(), 'Invalid verification code', 'bad attempt')
}
)
.then(
@ -54,15 +63,15 @@ TestServer.start(config)
)
.then(
function (status) {
t.equal(status.verified, false, 'account not verified')
assert.equal(status.verified, false, 'account not verified')
}
)
}
)
test(
it(
'verification email link',
function (t) {
() => {
var email = server.uniqueEmail()
var password = 'something'
var client = null // eslint-disable-line no-unused-vars
@ -85,20 +94,16 @@ TestServer.start(config)
function (emailData) {
var link = emailData.headers['x-link']
var query = url.parse(link, true).query
t.ok(query.uid, 'uid is in link')
t.ok(query.code, 'code is in link')
t.equal(query.redirectTo, options.redirectTo, 'redirectTo is in link')
t.equal(query.service, options.service, 'service is in link')
assert.ok(query.uid, 'uid is in link')
assert.ok(query.code, 'code is in link')
assert.equal(query.redirectTo, options.redirectTo, 'redirectTo is in link')
assert.equal(query.service, options.service, 'service is in link')
}
)
}
)
test(
'teardown',
function (t) {
server.stop()
t.end()
}
)
after(() => {
return TestServer.stop(server)
})
})

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

@ -2,18 +2,27 @@
* 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('tap').test
'use strict'
const assert = require('insist')
var TestServer = require('../test_server')
const Client = require('../client')()
var config = require('../../config').getProperties()
TestServer.start(config)
.then(function main(server) {
describe('remote session destroy', function() {
this.timeout(15000)
let server
before(() => {
return TestServer.start(config)
.then(s => {
server = s
})
})
test(
it(
'session destroy',
function (t) {
() => {
var email = server.uniqueEmail()
var password = 'foobar'
var client = null
@ -33,25 +42,25 @@ TestServer.start(config)
)
.then(
function () {
t.equal(client.sessionToken, null, 'session token deleted')
assert.equal(client.sessionToken, null, 'session token deleted')
client.sessionToken = sessionToken
return client.sessionStatus()
}
)
.then(
function (status) {
t.fail('got status with destroyed session')
assert(false, 'got status with destroyed session')
},
function (err) {
t.equal(err.errno, 110, 'session is invalid')
assert.equal(err.errno, 110, 'session is invalid')
}
)
}
)
test(
it(
'session status with valid token',
function (t) {
() => {
var email = server.uniqueEmail()
var password = 'testx'
var uid = null
@ -69,31 +78,27 @@ TestServer.start(config)
)
.then(
function (x) {
t.deepEqual(x, { uid: uid }, 'good status')
assert.deepEqual(x, { uid: uid }, 'good status')
}
)
}
)
test(
it(
'session status with invalid token',
function (t) {
() => {
var client = new Client(config.publicUrl)
return client.api.sessionStatus('0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF')
.then(
t.fail,
() => assert(false),
function (err) {
t.equal(err.errno, 110, 'invalid token')
assert.equal(err.errno, 110, 'invalid token')
}
)
}
)
test(
'teardown',
function (t) {
server.stop()
t.end()
}
)
after(() => {
return TestServer.stop(server)
})
})

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

@ -2,41 +2,41 @@
* 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')
'use strict'
const assert = require('insist')
var TestServer = require('../test_server')
var P = require('../../lib/promise')
var request = require('request')
var request = P.promisify(require('request'))
var path = require('path')
process.env.OLD_PUBLIC_KEY_FILE = path.resolve(__dirname, '../../config/public-key.json')
var config = require('../../config').getProperties()
describe('remote sign key', function() {
this.timeout(15000)
let server
before(() => {
process.env.OLD_PUBLIC_KEY_FILE = path.resolve(__dirname, '../../config/public-key.json')
var config = require('../../config').getProperties()
return TestServer.start(config)
.then(s => {
server = s
})
})
TestServer.start(config)
.then(function main(server) {
test(
it(
'.well-known/browserid has keys',
function (t) {
var d = P.defer()
request('http://127.0.0.1:9000/.well-known/browserid',
function (err, res, body) {
if (err) { d.reject(err) }
t.equal(res.statusCode, 200)
() => {
return request('http://127.0.0.1:9000/.well-known/browserid')
.spread((res, body) => {
assert.equal(res.statusCode, 200)
var json = JSON.parse(body)
t.equal(json.authentication, '/.well-known/browserid/sign_in.html')
t.equal(json.keys.length, 2)
d.resolve(json)
}
)
return d.promise
assert.equal(json.authentication, '/.well-known/browserid/sign_in.html')
assert.equal(json.keys.length, 2)
})
}
)
test(
'teardown',
function (t) {
server.stop()
t.end()
}
)
after(() => {
delete process.env.OLD_PUBLIC_KEY_FILE
return TestServer.stop(server)
})
})

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

@ -2,21 +2,30 @@
* 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')
'use strict'
const assert = require('insist')
var TestServer = require('../test_server')
const Client = require('../client')()
process.env.PASSWORD_CHANGE_TOKEN_TTL = '1'
var config = require('../../config').getProperties()
function fail() { throw new Error() }
TestServer.start(config)
.then(function main(server) {
describe('remote token expiry', function() {
this.timeout(15000)
let server, config
before(() => {
process.env.PASSWORD_CHANGE_TOKEN_TTL = '1'
config = require('../../config').getProperties()
test(
return TestServer.start(config)
.then(s => {
server = s
})
})
it(
'token expiry',
function (t) {
() => {
// FYI config.tokenLifetimes.passwordChangeToken = 1
var email = Math.random() + '@example.com'
var password = 'ok'
@ -29,17 +38,14 @@ TestServer.start(config)
.then(
fail,
function (err) {
t.equal(err.errno, 110, 'invalid token')
assert.equal(err.errno, 110, 'invalid token')
}
)
}
)
test(
'teardown',
function (t) {
server.stop()
t.end()
}
)
after(() => {
delete process.env.PASSWORD_CHANGE_TOKEN_TTL
return TestServer.stop(server)
})
})

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

@ -2,10 +2,11 @@
* 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 tap = require('tap')
var test = tap.test
'use strict'
const assert = require('insist')
var uuid = require('uuid')
var log = { trace: console.log, info: console.log } // eslint-disable-line no-console
var log = { trace() {}, info() {} }
var config = require('../../config').getProperties()
var TestServer = require('../test_server')
@ -41,55 +42,51 @@ function createTestAccount() {
var mockLog = require('../mocks').mockLog
var dbServer, reminderConfig
var dbConn = TestServer.start(config)
.then(
function (server) {
dbServer = server
reminderConfig = process.env.VERIFICATION_REMINDER_RATE
process.env.VERIFICATION_REMINDER_RATE = 1
return DB.connect(config[config.db.backend])
}
)
describe('remote verification reminder db', function() {
this.timeout(15000)
test(
'create',
function (t) {
var thisMockLog = mockLog({
increment: function (name) {
t.equal(name, 'verification-reminders.created')
}
})
let dbServer, reminderConfig, db
before(() => {
return TestServer.start(config)
.then(s => {
dbServer = s
reminderConfig = process.env.VERIFICATION_REMINDER_RATE
process.env.VERIFICATION_REMINDER_RATE = 1
return DB.connect(config[config.db.backend])
})
.then(x => {
db = x
})
})
it(
'create',
() => {
var thisMockLog = mockLog({
increment: function (name) {
assert.equal(name, 'verification-reminders.created')
}
})
dbConn.then(function (db) {
var account = createTestAccount()
var reminder = { uid: account.uid.toString('hex') }
var verificationReminder = require('../../lib/verification-reminders')(thisMockLog, db)
return verificationReminder.create(reminder).then(
function () {
t.end()
},
function () {
t.fail()
return verificationReminder.create(reminder)
}
)
it(
'delete',
() => {
var thisMockLog = mockLog({
increment: function (name) {
if (name === 'verification-reminders.deleted') {
assert.ok(true, 'correct log message')
}
)
})
}
)
test(
'delete',
function (t) {
var thisMockLog = mockLog({
increment: function (name) {
if (name === 'verification-reminders.deleted') {
t.ok(true, 'correct log message')
}
}
})
})
dbConn.then(function (db) {
var verificationReminder = require('../../lib/verification-reminders')(thisMockLog, db)
var account = createTestAccount()
var reminder = { uid: account.uid.toString('hex') }
@ -98,25 +95,16 @@ test(
.then(function () {
return verificationReminder.delete(reminder)
})
.then(function () {
t.end()
}, function (err) {
t.notOk(err)
})
})
}
)
}
)
test(
'teardown',
function (t) {
return dbConn.then(function(db) {
return db.close()
}).then(function() {
return dbServer.stop()
}).then(function () {
process.env.VERIFICATION_REMINDER_RATE = reminderConfig
t.end()
})
}
)
after(() => {
return TestServer.stop(dbServer)
.then(() => {
return db.close()
})
.then(function () {
process.env.VERIFICATION_REMINDER_RATE = reminderConfig
})
})
})

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

@ -2,17 +2,16 @@
* 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('tap').test
'use strict'
const assert = require('insist')
var TestServer = require('../test_server')
const Client = require('../client')()
var createDBServer = require('fxa-auth-db-mysql')
var log = { trace: console.log } // eslint-disable-line no-console
var log = { trace() {} }
var config = require('../../config').getProperties()
process.env.VERIFIER_VERSION = '0'
process.env.SIGNIN_CONFIRMATION_ENABLED = false
var Token = require('../../lib/tokens')(log)
var DB = require('../../lib/db')(
config,
@ -25,107 +24,121 @@ var DB = require('../../lib/db')(
Token.PasswordChangeToken
)
createDBServer().then(
function (db_server) {
db_server.listen(config.httpdb.url.split(':')[2])
db_server.on('error', function () {})
describe('remote verifier upgrade', function() {
this.timeout(30000)
var email = Math.random() + '@example.com'
var password = 'ok'
var uid = null
test(
'upgrading verifierVersion upgrades the account on password change',
function (t) {
return TestServer.start(config)
.then(
function main(server) {
return Client.create(config.publicUrl, email, password, { preVerified: true, keys: true })
.then(
function (c) {
uid = Buffer(c.uid, 'hex')
return server.stop()
}
)
}
)
.then(
function () {
return DB.connect(config[config.db.backend])
.then(
function (db) {
return db.account(uid)
.then(
function (account) {
t.equal(account.verifierVersion, 0, 'wrong version')
}
)
.then(
function () {
return db.close()
}
)
}
)
}
)
.then(
function () {
process.env.VERIFIER_VERSION = '1'
return TestServer.start(config)
}
)
.then(
function (server) {
var client
return Client.login(config.publicUrl, email, password, server.mailbox)
.then(
function (x) {
client = x
return client.keys()
}
)
.then(
function () {
return client.changePassword(password)
}
)
.then(
function () {
return server.stop()
}
)
}
)
.then(
function () {
return DB.connect(config[config.db.backend])
.then(
function (db) {
return db.account(uid)
.then(
function (account) {
t.equal(account.verifierVersion, 1, 'wrong upgrade version')
}
)
.then(
function () {
return db.close()
}
)
}
)
}
)
.then(
function () {
try {
db_server.close()
} catch (e) {
// This connection may already be dead if a real mysql server is
// already bound to :8000.
}
}
)
})
before(() => {
process.env.VERIFIER_VERSION = '0'
process.env.SIGNIN_CONFIRMATION_ENABLED = false
})
it(
'upgrading verifierVersion upgrades the account on password change',
() => {
return createDBServer().then(function (db_server) {
db_server.listen(config.httpdb.url.split(':')[2])
db_server.on('error', function () {})
var email = Math.random() + '@example.com'
var password = 'ok'
var uid = null
return TestServer.start(config)
.then(
function main(server) {
return Client.create(config.publicUrl, email, password, { preVerified: true, keys: true })
.then(
function (c) {
uid = Buffer(c.uid, 'hex')
return server.stop()
}
)
}
)
.then(
function () {
return DB.connect(config[config.db.backend])
.then(
function (db) {
return db.account(uid)
.then(
function (account) {
assert.equal(account.verifierVersion, 0, 'wrong version')
}
)
.then(
function () {
return db.close()
}
)
}
)
}
)
.then(
function () {
process.env.VERIFIER_VERSION = '1'
return TestServer.start(config)
}
)
.then(
function (server) {
var client
return Client.login(config.publicUrl, email, password, server.mailbox)
.then(
function (x) {
client = x
return client.keys()
}
)
.then(
function () {
return client.changePassword(password)
}
)
.then(
function () {
return server.stop()
}
)
}
)
.then(
function () {
return DB.connect(config[config.db.backend])
.then(
function (db) {
return db.account(uid)
.then(
function (account) {
assert.equal(account.verifierVersion, 1, 'wrong upgrade version')
}
)
.then(
function () {
return db.close()
}
)
}
)
}
)
.then(
function () {
try {
db_server.close()
} catch (e) {
// This connection may already be dead if a real mysql server is
// already bound to :8000.
}
}
)
})
}
)
after(() => {
delete process.env.VERIFIER_VERSION
delete process.env.SIGNIN_CONFIRMATION_ENABLED
})
})

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

@ -2,6 +2,8 @@
* 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/. */
'use strict'
var cp = require('child_process')
var crypto = require('crypto')
var P = require('../lib/promise')
@ -9,14 +11,20 @@ var request = require('request')
var mailbox = require('./mailbox')
var createDBServer = require('fxa-auth-db-mysql')
let currentServer
/* eslint-disable no-console */
function TestServer(config, printLogs) {
this.printLogs = printLogs === false ? false : true
currentServer = this
if (printLogs === undefined) {
printLogs = (process.env.REMOTE_TEST_LOGS === 'true')
}
this.printLogs = printLogs
this.config = config
this.server = null
this.mail = null
this.oauth = null
this.mailbox = mailbox(config.smtp.api.host, config.smtp.api.port)
this.mailbox = mailbox(config.smtp.api.host, config.smtp.api.port, this.printLogs)
}
function waitLoop(testServer, url, cb) {
@ -30,30 +38,49 @@ function waitLoop(testServer, url, cb) {
return cb(err)
}
if (!testServer.server) {
console.log('starting...')
if (testServer.printLogs) {
console.log('starting...')
}
testServer.start()
}
console.log('waiting...')
if (testServer.printLogs) {
console.log('waiting...')
}
return setTimeout(waitLoop.bind(null, testServer, url, cb), 100)
} else if (res.statusCode !== 200) {
cb(body)
}
cb()
}
)
}
function processKill(p, kid, signal) {
return new P((resolve, reject) => {
p.on('exit', (code, sig) => {
resolve()
})
p.kill(signal)
})
}
TestServer.start = function (config, printLogs) {
var d = P.defer()
createDBServer().then(
function (db) {
db.listen(config.httpdb.url.split(':')[2])
db.on('error', function () {})
var testServer = new TestServer(config, printLogs)
testServer.db = db
waitLoop(testServer, config.publicUrl, function (err) {
return err ? d.reject(err) : d.resolve(testServer)
})
}
)
TestServer.stop().then(() => {
return createDBServer().then(
function (db) {
db.listen(config.httpdb.url.split(':')[2])
db.on('error', function () {})
var testServer = new TestServer(config, printLogs)
testServer.db = db
waitLoop(testServer, config.publicUrl, function (err) {
return err ? d.reject(err) : d.resolve(testServer)
})
}
)
}).done(null, err => {
d.reject(err)
})
return d.promise
}
@ -101,14 +128,30 @@ TestServer.prototype.start = function () {
}
}
TestServer.stop = function (maybeServer) {
if (maybeServer) {
return maybeServer.stop()
} else if (currentServer) {
return currentServer.stop()
} else {
return P.resolve()
}
}
TestServer.prototype.stop = function () {
currentServer = undefined
try { this.db.close() } catch (e) {}
if (this.server) {
this.server.kill('SIGINT')
this.mail.kill()
let doomed = [
processKill(this.server, 'server', 'SIGINT'),
processKill(this.mail, 'mail')
]
if (this.oauth) {
this.oauth.kill()
doomed.push(processKill(this.oauth, 'oauth'))
}
return P.all(doomed)
} else {
return P.resolve()
}
}