Becuase:
- After upgrading sentry we were seeing test timeouts
- There were some kinda weird patterns in place for these tests anyways
- Test server wasn't getting cleaned up
- Test server start up is taking longer (presumably because of sentry upgrade?)

This Commit:
- Moves before an after blocks next to each other so setup and tear down are easy to follow
- Makes sure test server start / stop is done in before each blocks
- Increase test timeouts to 60s when test server is used
- Switches to async/await for before and after blocks, which feels less error prone
This commit is contained in:
dschom 2024-09-25 11:31:38 -07:00
Родитель 83dc649b52
Коммит 03940753cd
Не найден ключ, соответствующий данной подписи
46 изменённых файлов: 2063 добавлений и 1927 удалений

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

@ -288,15 +288,39 @@ async function run(config) {
log: log,
async close() {
log.info('shutdown');
await server.stop();
statsd.close();
try {
await server.stop();
} catch (e) {
log.warn('shutdown', {
message: 'Server did not shut down cleanly. ' + e.message,
});
}
try {
statsd.close();
} catch (e) {
log.warn('shutdown', {
message: 'Statsd did not shut down cleanly. ' + e.message,
});
}
try {
senders.email.stop();
} catch (e) {
// XXX: simplesmtp module may quit early and set socket to `false`, stopping it may fail
log.warn('shutdown', { message: 'Mailer client already disconnected' });
log.warn('shutdown', {
message: 'senders.email did not shut down cleanly. ' + e.message
});
}
try {
await database.close();
} catch (e) {
log.warn('shutdown', {
message: 'Database connection did not shutdown cleanly. ' + e.message,
});
}
await database.close();
},
};
}

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

@ -18,7 +18,7 @@ TestServer.start(config, false).then((server) => {
{ stdio: 'inherit' }
);
cp.on('close', (code) => {
server.stop();
cp.on('close', async (code) => {
await server.stop();
});
});

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

@ -95,26 +95,23 @@ module.exports = (config) => {
this.unwrapBKeyVersion2 = unwrapBKeyVersion2;
};
Client.create = function (origin, email, password, options = {}) {
Client.create = async function (origin, email, password, options = {}) {
const c = new Client(origin, options);
if (options.version === 'V2') {
return (async function () {
await c.setupCredentials(email, password);
await c.setupCredentialsV2(email, password);
await c.setupCredentials(email, password);
await c.setupCredentialsV2(email, password);
c.generateNewWrapKb();
c.deriveWrapKbVersion2FromKb();
c.generateNewWrapKb();
c.deriveWrapKbVersion2FromKb();
await c.createV2();
return c;
})();
const result = await c.createV2();
return result;
} else {
await c.setupCredentials(email, password);
const result = await c.create(options);
return result;
}
return c.setupCredentials(email, password).then(() => {
return c.create(options);
});
};
Client.login = function (origin, email, password, options) {

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

@ -16,8 +16,6 @@ const sinon = require('sinon');
const db = require('../../lib/oauth/db');
const encrypt = require('fxa-shared/auth/encrypt');
const config = testServer.config;
let Server;
let Server2;
const unique = require('../../lib/oauth/unique');
const util = require('../../lib/oauth/util');
@ -128,35 +126,6 @@ function authParams(params, options) {
return defaults;
}
function newToken(payload = {}, options = {}) {
var ttl = payload.ttl || MAX_TTL_S;
delete payload.ttl;
mockAssertion().reply(200, options.verifierResponse || VERIFY_GOOD);
return Server.api
.post({
url: '/authorization',
payload: authParams(payload, options),
})
.then(function (res) {
assert.equal(res.statusCode, 200);
assertSecurityHeaders(res);
return Server.api.post({
url: '/token',
payload: {
client_id: options.clientId || clientId,
client_secret: options.codeVerifier
? undefined
: options.secret || secret,
code: res.result.code,
code_verifier: options.codeVerifier,
ppid_seed: options.ppidSeed,
resource: options.resource,
ttl: ttl,
},
});
});
}
function assertInvalidRequestParam(result, param) {
assert.equal(result.code, 400);
assert.equal(result.errno, 109);
@ -187,36 +156,65 @@ function basicAuthHeader(clientId, secret) {
}
describe('#integration - /v1', function () {
this.timeout(60000);
const VERIFY_FAILURE = '{"status": "failure"}';
let sandbox;
let Server;
function newToken(payload = {}, options = {}) {
var ttl = payload.ttl || MAX_TTL_S;
delete payload.ttl;
mockAssertion().reply(200, options.verifierResponse || VERIFY_GOOD);
return Server.api
.post({
url: '/authorization',
payload: authParams(payload, options),
})
.then(function (res) {
assert.equal(res.statusCode, 200);
assertSecurityHeaders(res);
return Server.api.post({
url: '/token',
payload: {
client_id: options.clientId || clientId,
client_secret: options.codeVerifier
? undefined
: options.secret || secret,
code: res.result.code,
code_verifier: options.codeVerifier,
ppid_seed: options.ppidSeed,
resource: options.resource,
ttl: ttl,
},
});
});
}
before(async function () {
this.timeout(30000);
const start = Date.now();
console.log('!!! 1', Date.now() - start);
Server = await testServer.start();
return Promise.all([
genAssertion(USERID + config.get('oauthServer.browserid.issuer')).then(
function (ass) {
AN_ASSERTION = ass;
}
),
db.ping().then(function () {
client = clientByName('Mocha');
clientId = client.id;
assert.equal(encrypt.hash(secret).toString('hex'), client.hashedSecret);
assert.equal(
encrypt.hash(secretPrevious).toString('hex'),
client.hashedSecretPrevious
);
badSecret = Buffer.from(secret, 'hex').slice();
badSecret[badSecret.length - 1] ^= 1;
badSecret = badSecret.toString('hex');
}),
]);
console.log('!!! 2', Date.now() - start);
AN_ASSERTION = await genAssertion(
USERID + config.get('oauthServer.browserid.issuer')
);
await db.ping();
console.log('!!! 3', Date.now() - start);
client = clientByName('Mocha');
clientId = client.id;
assert.equal(encrypt.hash(secret).toString('hex'), client.hashedSecret);
assert.equal(
encrypt.hash(secretPrevious).toString('hex'),
client.hashedSecretPrevious
);
badSecret = Buffer.from(secret, 'hex').slice();
badSecret[badSecret.length - 1] ^= 1;
badSecret = badSecret.toString('hex');
console.log('!!! 4', Date.now() - start);
});
after(async function () {
await Server?.close();
await Server2?.close();
await Server.close();
});
beforeEach(() => {
@ -2860,37 +2858,57 @@ describe('#integration - /v1', function () {
assert.equal(res.result.errno, 115);
});
it('should not reject expired tokens from pocket clients', async function () {
describe('expired tokens from pocket clients', () => {
const clientId = '749818d3f2e7857f';
config.set('oauthServer.expiration.accessTokenExpiryEpoch', undefined);
Server2 = await testServer.start();
let res = await newToken(
{
ttl: 1,
},
{
clientId,
}
);
let accessTokenExpiryEpoch;
assert.equal(res.statusCode, 200);
assertSecurityHeaders(res);
assert.equal(res.result.expires_in, 1);
sandbox.useFakeTimers({
now: Date.now() + 1000 * 60 * 60, // 1 hr in future
shouldAdvanceTime: true,
before(async () => {
await Server.close();
accessTokenExpiryEpoch = config.get(
'oauthServer.expiration.accessTokenExpiryEpoch'
);
config.set('oauthServer.expiration.accessTokenExpiryEpoch', undefined);
Server = await testServer.start();
});
res = await Server2.api.post({
url: '/verify',
payload: {
token: res.result.access_token,
},
after(async () => {
await Server.close();
config.set(
'oauthServer.expiration.accessTokenExpiryEpoch',
accessTokenExpiryEpoch
);
Server = await testServer.start();
});
assert.equal(res.statusCode, 200);
assertSecurityHeaders(res);
it('should not reject expired tokens from pocket clients', async function () {
let res = await newToken(
{
ttl: 1,
},
{
clientId,
}
);
assert.equal(res.statusCode, 200);
assertSecurityHeaders(res);
assert.equal(res.result.expires_in, 1);
sandbox.useFakeTimers({
now: Date.now() + 1000 * 60 * 60, // 1 hr in future
shouldAdvanceTime: true,
});
res = await Server.api.post({
url: '/verify',
payload: {
token: res.result.access_token,
},
});
assert.equal(res.statusCode, 200);
assertSecurityHeaders(res);
});
});
describe('response', function () {

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

@ -11,11 +11,10 @@ const crypto = require('crypto');
const rimraf = require('rimraf');
describe('#integration - the signing-key management scripts', function () {
this.timeout(60000);
let runScript;
let workDir, keyFile, newKeyFile, oldKeyFile;
this.timeout(30000);
beforeEach(() => {
const uniqName = crypto.randomBytes(8).toString('hex');
workDir = path.join(os.tmpdir(), `fxa-oauth-server-tests-${uniqName}`);

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

@ -21,9 +21,10 @@ const {
// Note, intentionally not indenting for code review.
[{ version: '' }, { version: 'V2' }].forEach((testOptions) => {
describe(`#integration${testOptions.version} - remote account create`, function () {
this.timeout(50000);
this.timeout(60000);
let server;
before(async () => {
before(async function () {
config.subscriptions = {
enabled: true,
stripeApiKey: 'fake_key',
@ -54,6 +55,10 @@ const {
return server;
});
after(async function () {
await TestServer.stop(server);
});
it('unverified account fail when getting keys', () => {
const email = server.uniqueEmail();
const password = 'allyourbasearebelongtous';
@ -989,9 +994,7 @@ const {
assert.equal(kB2, originalKb);
});
after(async () => {
await TestServer.stop(server);
});
async function login(email, password, version = '') {
return await Client.login(config.publicUrl, email, password, {

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

@ -15,13 +15,16 @@ const otplib = require('otplib');
[{version:""},{version:"V2"}].forEach((testOptions) => {
describe(`#integration${testOptions.version} - remote account create with sign-up code`, function () {
this.timeout(15000);
this.timeout(60000);
const password = '4L6prUdlLNfxGIoj';
let server, client, email, emailStatus, emailData;
before(async () => {
server = await TestServer.start(config);
return server;
});
after(async function () {
await TestServer.stop(server);
});
it('create and verify sync account', async () => {
@ -231,9 +234,7 @@ describe(`#integration${testOptions.version} - remote account create with sign-u
});
});
after(() => {
return TestServer.stop(server);
});
});
});

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

@ -13,13 +13,15 @@ const config = require('../../config').default.getProperties();
// Note, intentionally not indenting for code review.
[{ version: '' }, { version: 'V2' }].forEach((testOptions) => {
describe(`#integration${testOptions.version} - remote account destroy`, function () {
this.timeout(15000);
this.timeout(60000);
let server;
before(() => {
return TestServer.start(config).then((s) => {
server = s;
});
before(async function () {
server = await TestServer.start(config);
});
after(async function () {
await TestServer.stop(server);
});
it('account destroy', () => {
@ -115,8 +117,6 @@ const config = require('../../config').default.getProperties();
});
});
after(() => {
return TestServer.stop(server);
});
});
});

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

@ -21,7 +21,7 @@ const key = {
[{version:""},{version:"V2"}].forEach((testOptions) => {
describe(`#integration${testOptions.version} - remote account locale`, function () {
this.timeout(15000);
this.timeout(60000);
let server;
before(async () => {

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

@ -17,13 +17,15 @@ const config = require('../../config').default.getProperties();
describe(`#integration${testOptions.version} - remote account login`, () => {
let server;
before(function () {
this.timeout(15000);
before(async function () {
this.timeout(60000);
config.securityHistory.ipProfiling.allowedRecency = 0;
config.signinConfirmation.skipForNewAccounts.enabled = false;
return TestServer.start(config).then((s) => {
server = s;
});
server = await TestServer.start(config);
});
after(async function () {
await TestServer.stop(server);
});
it('the email is returned in the error on Incorrect password errors', () => {
@ -37,7 +39,12 @@ describe(`#integration${testOptions.version} - remote account login`, () => {
testOptions
)
.then(() => {
return Client.login(config.publicUrl, email, `${password}x`, testOptions);
return Client.login(
config.publicUrl,
email,
`${password}x`,
testOptions
);
})
.then(
() => assert(false),
@ -50,8 +57,7 @@ describe(`#integration${testOptions.version} - remote account login`, () => {
});
it('the email is returned in the error on Incorrect email case errors with correct password', () => {
if (testOptions.version === "V2") {
if (testOptions.version === 'V2') {
// Important!!! This test is no longer applicable for V2 passwords.
// V1 passwords are encoded with a salt that includes the users
// email address. As a result, if the user enters a their email
@ -72,7 +78,12 @@ describe(`#integration${testOptions.version} - remote account login`, () => {
testOptions
)
.then(() => {
return Client.login(config.publicUrl, loginEmail, password, testOptions);
return Client.login(
config.publicUrl,
loginEmail,
password,
testOptions
);
})
.then(
() => assert(false),
@ -110,7 +121,10 @@ describe(`#integration${testOptions.version} - remote account login`, () => {
testOptions
)
.then((c) => {
return Client.login(config.publicUrl, email, password, { ...testOptions, keys: false });
return Client.login(config.publicUrl, email, password, {
...testOptions,
keys: false,
});
})
.then((c) => {
assert.equal(c.keyFetchToken, null, 'should not have keyFetchToken');
@ -199,7 +213,7 @@ describe(`#integration${testOptions.version} - remote account login`, () => {
)
.then(() => {
return Client.login(config.publicUrl, email, 'foo', {
... testOptions,
...testOptions,
metricsContext: {
flowId:
'0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef',
@ -329,7 +343,7 @@ describe(`#integration${testOptions.version} - remote account login`, () => {
server.mailbox,
{
...testOptions,
keys: true
keys: true,
}
)
.then(() => {
@ -386,8 +400,6 @@ describe(`#integration${testOptions.version} - remote account login`, () => {
});
});
after(() => {
return TestServer.stop(server);
});
});
});

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

@ -17,7 +17,7 @@ const CLIENT_ID = config.oauthServer.clients.find(
[{version:""},{version:"V2"}].forEach((testOptions) => {
describe(`#integration${testOptions.version} - fetch user profile data`, function () {
this.timeout(15000);
this.timeout(60000);
let server, client, email, password;
before(async () => {

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

@ -14,13 +14,16 @@ const config = require('../../config').default.getProperties();
[{ version: '' }, { version: 'V2' }].forEach((testOptions) => {
describe(`#integration${testOptions.version} - remote account reset`, function () {
this.timeout(15000);
this.timeout(60000);
let server;
config.signinConfirmation.skipForNewAccounts.enabled = true;
before(() => {
return TestServer.start(config).then((s) => {
server = s;
});
before(async function () {
server = await TestServer.start(config);
});
after(async function () {
await TestServer.stop(server);
});
it('account reset w/o sessionToken', async () => {
@ -255,10 +258,6 @@ const config = require('../../config').default.getProperties();
assert.ok(cert1['fxa-keysChangedAt'] < cert2['fxa-keysChangedAt']);
});
after(() => {
return TestServer.stop(server);
});
async function resetPassword(client, code, newPassword, options) {
await client.verifyPasswordResetCode(code);
return await client.resetPassword(newPassword, {}, options);

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

@ -24,14 +24,17 @@ const mocks = require('../mocks');
// Note, intentionally not indenting for code review.
[{ version: '' }, { version: 'V2' }].forEach((testOptions) => {
describe(`#integration${testOptions.version} - remote account signin verification`, function () {
this.timeout(30000);
this.timeout(60000);
let server;
before(() => {
before(async () => {
config.securityHistory.ipProfiling.allowedRecency = 0;
config.signinConfirmation.skipForNewAccounts.enabled = false;
return TestServer.start(config).then((s) => {
server = s;
});
server = await TestServer.start(config);
});
after(async () => {
await TestServer.stop(server);
});
it('account signin without keys does not set challenge', () => {
@ -810,8 +813,6 @@ const mocks = require('../mocks');
});
});
after(() => {
return TestServer.stop(server);
});
});
});

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

@ -14,16 +14,23 @@ const config = require('../../config').default.getProperties();
[{version:""},{version:"V2"}].forEach((testOptions) => {
describe(`#integration${testOptions.version} - remote account status`, function () {
this.timeout(15000);
this.timeout(60000);
let server;
before(() => {
return TestServer.start(config).then((s) => {
server = s;
});
before(async () => {
server = await TestServer.start(config);
});
after(async () => {
await TestServer.stop(server);
});
it('account status with existing account', () => {
return Client.create(config.publicUrl, server.uniqueEmail(), 'password', testOptions)
return Client.create(
config.publicUrl,
server.uniqueEmail(),
'password',
testOptions
)
.then((c) => {
return c.api.accountStatus(c.uid);
})
@ -139,9 +146,7 @@ describe(`#integration${testOptions.version} - remote account status`, function
});
});
after(() => {
return TestServer.stop(server);
});
});
});

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

@ -14,16 +14,23 @@ const config = require('../../config').default.getProperties();
[{version:""},{version:"V2"}].forEach((testOptions) => {
describe(`#integration${testOptions.version} - remote account unlock`, function () {
this.timeout(15000);
this.timeout(60000);
let server;
before(() => {
return TestServer.start(config).then((s) => {
server = s;
});
before(async () => {
server = await TestServer.start(config);
});
after(async () => {
await TestServer.stop(server);
});
it('/account/lock is no longer supported', () => {
return Client.create(config.publicUrl, server.uniqueEmail(), 'password', testOptions)
return Client.create(
config.publicUrl,
server.uniqueEmail(),
'password',
testOptions
)
.then((c) => {
return c.lockAccount();
})
@ -38,7 +45,12 @@ describe(`#integration${testOptions.version} - remote account unlock`, function
});
it('/account/unlock/resend_code is no longer supported', () => {
return Client.create(config.publicUrl, server.uniqueEmail(), 'password', testOptions)
return Client.create(
config.publicUrl,
server.uniqueEmail(),
'password',
testOptions
)
.then((c) => {
return c.resendAccountUnlockCode('en');
})
@ -53,7 +65,12 @@ describe(`#integration${testOptions.version} - remote account unlock`, function
});
it('/account/unlock/verify_code is no longer supported', () => {
return Client.create(config.publicUrl, server.uniqueEmail(), 'password', testOptions)
return Client.create(
config.publicUrl,
server.uniqueEmail(),
'password',
testOptions
)
.then((c) => {
return c.verifyAccountUnlockCode('bigscaryuid', 'bigscarycode');
})
@ -67,9 +84,7 @@ describe(`#integration${testOptions.version} - remote account unlock`, function
);
});
after(() => {
return TestServer.stop(server);
});
});
});

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

@ -21,7 +21,7 @@ const PUBLIC_CLIENT_ID = '3c49430b43dfba77';
[{version:""},{version:"V2"}].forEach((testOptions) => {
describe(`#integration${testOptions.version} - attached clients listing`, function () {
this.timeout(15000);
this.timeout(60000);
let server, oauthServerDb;
before(async () => {
config.lastAccessTimeUpdates = {
@ -34,6 +34,11 @@ describe(`#integration${testOptions.version} - attached clients listing`, functi
oauthServerDb = require('../../lib/oauth/db');
});
after(async () => {
await TestServer.stop(server);
testUtils.restoreStdoutWrite();
});
it('correctly lists a variety of attached clients', async () => {
const email = server.uniqueEmail();
const password = 'test password';
@ -119,7 +124,12 @@ describe(`#integration${testOptions.version} - attached clients listing`, functi
await tokens.SessionToken.fromHex(client.sessionToken)
).id;
const client2 = await Client.login(config.publicUrl, email, password, testOptions);
const client2 = await Client.login(
config.publicUrl,
email,
password,
testOptions
);
const device = await client2.updateDevice({
name: 'test',
type: 'desktop',
@ -151,7 +161,12 @@ describe(`#integration${testOptions.version} - attached clients listing`, functi
await tokens.SessionToken.fromHex(client.sessionToken)
).id;
const client2 = await Client.login(config.publicUrl, email, password, testOptions);
const client2 = await Client.login(
config.publicUrl,
email,
password,
testOptions
);
const otherSessionTokenId = (
await tokens.SessionToken.fromHex(client2.sessionToken)
).id;
@ -207,10 +222,7 @@ describe(`#integration${testOptions.version} - attached clients listing`, functi
assert.equal(allClients[0].refreshTokenId, null);
});
after(async () => {
await TestServer.stop(server);
testUtils.restoreStdoutWrite();
});
});
});

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

@ -13,15 +13,17 @@ const superagent = require('superagent');
[{version:""},{version:"V2"}].forEach((testOptions) => {
describe(`#integration${testOptions.version} - remote base path`, function () {
this.timeout(15000);
this.timeout(60000);
let server, config;
before(() => {
before(async () => {
config = require('../../config').default.getProperties();
config.publicUrl = 'http://localhost:9000/auth';
return TestServer.start(config).then((s) => {
server = s;
});
server = await TestServer.start(config);
});
after(async () => {
await TestServer.stop(server);
});
function testVersionRoute(path) {
@ -76,9 +78,7 @@ describe(`#integration${testOptions.version} - remote base path`, function () {
testVersionRoute('/__version__')
);
after(() => {
return TestServer.stop(server);
});
});
});

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

@ -21,12 +21,14 @@ const publicKey = {
[{ version: '' }, { version: 'V2' }].forEach((testOptions) => {
describe(`#integration${testOptions.version} - remote certificate sign`, function () {
this.timeout(15000);
this.timeout(60000);
let server;
before(() => {
return TestServer.start(config).then((s) => {
server = s;
});
before(async () => {
server = await TestServer.start(config);
});
after(async () => {
await TestServer.stop(server);
});
it('certificate sign', () => {
@ -316,8 +318,6 @@ const publicKey = {
);
});
after(() => {
return TestServer.stop(server);
});
});
});

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

@ -13,13 +13,16 @@ const config = require('../../config').default.getProperties();
[{version:""},{version:"V2"}].forEach((testOptions) => {
describe(`#integration${testOptions.version} - remote concurrent`, function () {
this.timeout(15000);
this.timeout(60000);
let server;
before(() => {
before(async () => {
config.verifierVersion = 1;
return TestServer.start(config).then((s) => {
server = s;
});
server = await TestServer.start(config);
});
after(async () => {
await TestServer.stop(server);
});
it('concurrent create requests', () => {
@ -38,9 +41,7 @@ describe(`#integration${testOptions.version} - remote concurrent`, function () {
});
});
after(() => {
return TestServer.stop(server);
});
});
});

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

@ -15,6 +15,7 @@ const UnblockCode = require('../../lib/crypto/random').base32(
);
const uuid = require('uuid');
const { normalizeEmail } = require('fxa-shared').email.helpers;
const ioredis = require('ioredis');
const log = { debug() {}, trace() {}, info() {}, error() {} };
@ -70,26 +71,24 @@ const zeroBuffer32 = Buffer.from(
let account, secondEmail;
describe(`#integration - remote db`, function () {
this.timeout(20000);
this.timeout(60000);
let dbServer, db, redis;
before(() => {
redis = require('ioredis').createClient({
before(async () => {
redis = ioredis.createClient({
host: config.redis.host,
port: config.redis.port,
password: config.redis.password,
prefix: config.redis.sessionTokens.prefix,
enable_offline_queue: false,
});
dbServer = await TestServer.start(config);
db = await DB.connect(config);
});
return TestServer.start(config)
.then((s) => {
dbServer = s;
return DB.connect(config);
})
.then((x) => {
db = x;
});
after(async () => {
await TestServer.stop(dbServer);
await db.close();
});
beforeEach(() => {
@ -1613,10 +1612,4 @@ describe(`#integration - remote db`, function () {
});
});
});
after(() => {
return TestServer.stop(dbServer).then(() => {
return db && db.close();
});
});
});

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

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

@ -35,7 +35,7 @@ const UNKNOWN_REFRESH_TOKEN =
[{version:""},{version:"V2"}].forEach((testOptions) => {
describe(`#integration${testOptions.version} - remote device with refresh tokens`, function () {
this.timeout(15000);
this.timeout(60000);
let client;
let db;
let email;
@ -44,20 +44,14 @@ describe(`#integration${testOptions.version} - remote device with refresh tokens
let refreshToken;
let server;
before(() => {
before(async () => {
config.lastAccessTimeUpdates = lastAccessTimeUpdates;
const DB = require('../../lib/db')(config, log, Token);
testUtils.disableLogs();
return TestServer.start(config, false)
.then((s) => {
server = s;
return DB.connect(config);
})
.then((x) => {
db = x;
oauthServerDb = require('../../lib/oauth/db');
});
server = await TestServer.start(config, false);
db = await DB.connect(config);
oauthServerDb = require('../../lib/oauth/db');
});
after(async () => {

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

@ -13,12 +13,14 @@ const config = require('../../config').default.getProperties();
[{version:""},{version:"V2"}].forEach((testOptions) => {
describe(`#integration${testOptions.version} - remote email validity`, function () {
this.timeout(15000);
this.timeout(60000);
let server;
before(() => {
return TestServer.start(config).then((s) => {
server = s;
});
before(async () => {
server = await TestServer.start(config);
});
after(async () => {
await TestServer.stop(server);
});
it('/account/create with a variety of malformed email addresses', () => {
@ -77,9 +79,7 @@ describe(`#integration${testOptions.version} - remote email validity`, function
return Promise.all(emails);
});
after(() => {
return TestServer.stop(server);
});
});

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

@ -15,15 +15,17 @@ const pubSigKey = JWTool.JWK.fromFile(config.publicKeyFile);
[{ version: '' }, { version: 'V2' }].forEach((testOptions) => {
describe(`#integration${testOptions.version} - remote flow`, function () {
this.timeout(15000);
this.timeout(60000);
let server;
let email1;
config.signinConfirmation.skipForNewAccounts.enabled = true;
before(() => {
return TestServer.start(config).then((s) => {
server = s;
email1 = server.uniqueEmail();
});
before(async () => {
server = await TestServer.start(config);
email1 = server.uniqueEmail();
});
after(async () => {
await TestServer.stop(server);
});
it('Create account flow', () => {
@ -112,8 +114,6 @@ const pubSigKey = JWTool.JWK.fromFile(config.publicKeyFile);
});
});
after(() => {
return TestServer.stop(server);
});
});
});

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

@ -14,12 +14,13 @@ const config = require('../../config').default.getProperties();
[{ version: '' }, { version: 'V2' }].forEach((testOptions) => {
describe(`#integration${testOptions.version} - remote misc`, function () {
this.timeout(15000);
this.timeout(60000);
let server;
before(() => {
return TestServer.start(config).then((s) => {
server = s;
});
before(async () => {
server = await TestServer.start(config);
});
after(async () => {
await TestServer.stop(server);
});
function testVersionRoute(route) {
@ -286,8 +287,6 @@ const config = require('../../config').default.getProperties();
});
});
after(() => {
return TestServer.stop(server);
});
});
});

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

@ -23,7 +23,7 @@ const MOCK_CODE_CHALLENGE = 'YPhkZqm08uTfwjNSiYcx80-NPT9Zn94kHboQW97KyV0';
[{version:""},{version:"V2"}].forEach((testOptions) => {
describe(`#integration${testOptions.version} - /oauth/ session token scope`, function () {
this.timeout(15000);
this.timeout(60000);
let client;
let email;
let password;

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

@ -25,7 +25,7 @@ const { decodeJWT } = testUtils;
[{ version: '' }, { version: 'V2' }].forEach((testOptions) => {
describe(`#integration${testOptions.version} - /oauth/ routes`, function () {
this.timeout(15000);
this.timeout(60000);
let client;
let email;
let password;

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

@ -20,15 +20,17 @@ function getSessionTokenId(sessionTokenHex) {
[{ version: '' }, { version: 'V2' }].forEach((testOptions) => {
describe(`#integration${testOptions.version} - remote password change`, function () {
this.timeout(15000);
this.timeout(60000);
let server;
before(() => {
before(async () => {
config.securityHistory.ipProfiling.allowedRecency = 0;
config.signinConfirmation.skipForNewAccounts.enabled = false;
return TestServer.start(config).then((s) => {
server = s;
});
server = await TestServer.start(config);
});
after(async () => {
await TestServer.stop(server);
});
it('password change, with unverified session', () => {
@ -544,8 +546,6 @@ function getSessionTokenId(sessionTokenHex) {
);
});
after(() => {
return TestServer.stop(server);
});
});
});

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

@ -2,450 +2,449 @@
* 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';
'use strict';
const chai = require('chai');
const chaiAsPromised = require('chai-as-promised');
const url = require('url');
const Client = require('../client')();
const TestServer = require('../test_server');
const crypto = require('crypto');
const base64url = require('base64url');
const chai = require('chai');
const chaiAsPromised = require('chai-as-promised');
const url = require('url');
const Client = require('../client')();
const TestServer = require('../test_server');
const crypto = require('crypto');
const base64url = require('base64url');
const config = require('../../config').default.getProperties();
const mocks = require('../mocks');
const config = require('../../config').default.getProperties();
const mocks = require('../mocks');
chai.use(chaiAsPromised);
const { assert } = chai;
chai.use(chaiAsPromised);
const { assert } = chai;
[{ version: '' }, { version: 'V2' }].forEach((testOptions) => {
describe(`#integration${testOptions.version} - remote password forgot`, function () {
this.timeout(15000);
let server;
before(() => {
config.securityHistory.ipProfiling.allowedRecency = 0;
config.signinConfirmation.skipForNewAccounts.enabled = true;
return TestServer.start(config).then((s) => {
server = s;
});
[{ version: '' }, { version: 'V2' }].forEach((testOptions) => {
describe(`#integration${testOptions.version} - remote password forgot`, function () {
this.timeout(15000);
let server;
before(async () => {
config.securityHistory.ipProfiling.allowedRecency = 0;
config.signinConfirmation.skipForNewAccounts.enabled = true;
server = await TestServer.start(config)
});
after(async () => {
await TestServer.stop(server);
});
it('forgot password', () => {
const email = server.uniqueEmail();
const password = 'allyourbasearebelongtous';
const newPassword = 'ez';
let wrapKb = null;
let kA = null;
let client = null;
const options = {
...testOptions,
keys: true,
metricsContext: mocks.generateMetricsContext(),
};
return Client.createAndVerify(
config.publicUrl,
email,
password,
server.mailbox,
options
)
.then((x) => {
client = x;
return client.keys();
})
.then((keys) => {
wrapKb = keys.wrapKb;
kA = keys.kA;
return client.forgotPassword();
})
.then(() => {
return server.mailbox.waitForEmail(email);
})
.then((emailData) => {
assert.equal(
emailData.headers['x-flow-begin-time'],
options.metricsContext.flowBeginTime,
'flow begin time set'
);
assert.equal(
emailData.headers['x-flow-id'],
options.metricsContext.flowId,
'flow id set'
);
assert.equal(emailData.headers['x-template-name'], 'recovery');
return emailData.headers['x-recovery-code'];
})
.then((code) => {
assert.isRejected(client.resetPassword(newPassword));
return resetPassword(client, code, newPassword, undefined, options);
})
.then(() => {
return server.mailbox.waitForEmail(email);
})
.then((emailData) => {
const link = emailData.headers['x-link'];
const query = url.parse(link, true).query;
assert.ok(query.email, 'email is in the link');
it('forgot password', () => {
const email = server.uniqueEmail();
const password = 'allyourbasearebelongtous';
const newPassword = 'ez';
let wrapKb = null;
let kA = null;
let client = null;
const options = {
...testOptions,
keys: true,
metricsContext: mocks.generateMetricsContext(),
};
return Client.createAndVerify(
config.publicUrl,
email,
password,
server.mailbox,
options
)
.then((x) => {
client = x;
return client.keys();
})
.then((keys) => {
wrapKb = keys.wrapKb;
kA = keys.kA;
return client.forgotPassword();
})
.then(() => {
return server.mailbox.waitForEmail(email);
})
.then((emailData) => {
assert.equal(
emailData.headers['x-flow-begin-time'],
options.metricsContext.flowBeginTime,
'flow begin time set'
);
assert.equal(
emailData.headers['x-flow-id'],
options.metricsContext.flowId,
'flow id set'
);
assert.equal(emailData.headers['x-template-name'], 'recovery');
return emailData.headers['x-recovery-code'];
})
.then((code) => {
assert.isRejected(client.resetPassword(newPassword));
return resetPassword(client, code, newPassword, undefined, options);
})
.then(() => {
return server.mailbox.waitForEmail(email);
})
.then((emailData) => {
const link = emailData.headers['x-link'];
const query = url.parse(link, true).query;
assert.ok(query.email, 'email is in the link');
assert.equal(
emailData.headers['x-flow-begin-time'],
options.metricsContext.flowBeginTime,
'flow begin time set'
);
assert.equal(
emailData.headers['x-flow-id'],
options.metricsContext.flowId,
'flow id set'
);
assert.equal(emailData.headers['x-template-name'], 'passwordReset');
})
.then(() => {
return upgradeCredentials(email, newPassword);
})
.then(
// make sure we can still login after password reset
() => {
return Client.login(config.publicUrl, email, newPassword, {
...testOptions,
keys: true,
});
}
)
.then((x) => {
client = x;
return client.keys();
})
.then((keys) => {
assert.equal(typeof keys.wrapKb, 'string', 'yep, wrapKb');
assert.notEqual(wrapKb, keys.wrapKb, 'wrapKb was reset');
assert.equal(kA, keys.kA, 'kA was not reset');
assert.equal(typeof client.kB, 'string');
assert.equal(client.kB.length, 64, 'kB exists, has the right length');
});
});
assert.equal(
emailData.headers['x-flow-begin-time'],
options.metricsContext.flowBeginTime,
'flow begin time set'
);
assert.equal(
emailData.headers['x-flow-id'],
options.metricsContext.flowId,
'flow id set'
);
assert.equal(emailData.headers['x-template-name'], 'passwordReset');
})
.then(() => {
return upgradeCredentials(email, newPassword);
})
.then(
// make sure we can still login after password reset
() => {
return Client.login(config.publicUrl, email, newPassword, {
...testOptions,
keys: true,
});
}
)
.then((x) => {
client = x;
return client.keys();
})
.then((keys) => {
assert.equal(typeof keys.wrapKb, 'string', 'yep, wrapKb');
assert.notEqual(wrapKb, keys.wrapKb, 'wrapKb was reset');
assert.equal(kA, keys.kA, 'kA was not reset');
assert.equal(typeof client.kB, 'string');
assert.equal(client.kB.length, 64, 'kB exists, has the right length');
});
});
it('forgot password limits verify attempts', () => {
let code = null;
const email = server.uniqueEmail();
const password = 'hothamburger';
let client = null;
return Client.createAndVerify(
config.publicUrl,
email,
password,
server.mailbox,
testOptions
)
.then(() => {
client = new Client(config.publicUrl, testOptions);
client.email = email;
return client.forgotPassword();
})
.then(() => {
return server.mailbox.waitForCode(email);
})
.then((c) => {
code = c;
})
.then(() => {
return client.reforgotPassword();
})
.then(() => {
return server.mailbox.waitForCode(email);
})
.then((c) => {
assert.equal(code, c, 'same code as before');
})
.then(() => {
return resetPassword(
client,
'00000000000000000000000000000000',
'password'
);
})
.then(
() => {
assert(false, 'reset password with bad code');
},
(err) => {
assert.equal(err.tries, 2, 'used a try');
assert.equal(
err.message,
'Invalid confirmation code',
'bad attempt 1'
);
}
)
.then(() => {
return resetPassword(
client,
'00000000000000000000000000000000',
'password'
);
})
.then(
() => {
assert(false, 'reset password with bad code');
},
(err) => {
assert.equal(err.tries, 1, 'used a try');
assert.equal(
err.message,
'Invalid confirmation code',
'bad attempt 2'
);
}
)
.then(() => {
return resetPassword(
client,
'00000000000000000000000000000000',
'password'
);
})
.then(
() => {
assert(false, 'reset password with bad code');
},
(err) => {
assert.equal(err.tries, 0, 'used a try');
assert.equal(
err.message,
'Invalid confirmation code',
'bad attempt 3'
);
}
)
.then(() => {
return resetPassword(
client,
'00000000000000000000000000000000',
'password'
);
})
.then(
() => {
assert(false, 'reset password with invalid token');
},
(err) => {
assert.equal(
err.message,
'Invalid authentication token: Missing authentication',
'token is now invalid'
);
}
);
});
it('forgot password limits verify attempts', () => {
let code = null;
const email = server.uniqueEmail();
const password = 'hothamburger';
let client = null;
return Client.createAndVerify(
config.publicUrl,
email,
password,
server.mailbox,
testOptions
)
.then(() => {
client = new Client(config.publicUrl, testOptions);
client.email = email;
return client.forgotPassword();
})
.then(() => {
return server.mailbox.waitForCode(email);
})
.then((c) => {
code = c;
})
.then(() => {
return client.reforgotPassword();
})
.then(() => {
return server.mailbox.waitForCode(email);
})
.then((c) => {
assert.equal(code, c, 'same code as before');
})
.then(() => {
return resetPassword(
client,
'00000000000000000000000000000000',
'password'
);
})
.then(
() => {
assert(false, 'reset password with bad code');
},
(err) => {
assert.equal(err.tries, 2, 'used a try');
assert.equal(
err.message,
'Invalid confirmation code',
'bad attempt 1'
);
}
)
.then(() => {
return resetPassword(
client,
'00000000000000000000000000000000',
'password'
);
})
.then(
() => {
assert(false, 'reset password with bad code');
},
(err) => {
assert.equal(err.tries, 1, 'used a try');
assert.equal(
err.message,
'Invalid confirmation code',
'bad attempt 2'
);
}
)
.then(() => {
return resetPassword(
client,
'00000000000000000000000000000000',
'password'
);
})
.then(
() => {
assert(false, 'reset password with bad code');
},
(err) => {
assert.equal(err.tries, 0, 'used a try');
assert.equal(
err.message,
'Invalid confirmation code',
'bad attempt 3'
);
}
)
.then(() => {
return resetPassword(
client,
'00000000000000000000000000000000',
'password'
);
})
.then(
() => {
assert(false, 'reset password with invalid token');
},
(err) => {
assert.equal(
err.message,
'Invalid authentication token: Missing authentication',
'token is now invalid'
);
}
);
});
it('recovery email link', () => {
const email = server.uniqueEmail();
const password = 'something';
let client = null;
const options = {
...testOptions,
redirectTo: `https://sync.${config.smtp.redirectDomain}/`,
service: 'sync',
};
return Client.create(config.publicUrl, email, password, options)
.then((c) => {
client = c;
})
.then(() => {
return server.mailbox.waitForEmail(email);
})
.then(() => {
return client.forgotPassword();
})
.then(() => {
return server.mailbox.waitForEmail(email);
})
.then((emailData) => {
const link = emailData.headers['x-link'];
const query = url.parse(link, true).query;
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');
});
});
it('recovery email link', () => {
const email = server.uniqueEmail();
const password = 'something';
let client = null;
const options = {
...testOptions,
redirectTo: `https://sync.${config.smtp.redirectDomain}/`,
service: 'sync',
};
return Client.create(config.publicUrl, email, password, options)
.then((c) => {
client = c;
})
.then(() => {
return server.mailbox.waitForEmail(email);
})
.then(() => {
return client.forgotPassword();
})
.then(() => {
return server.mailbox.waitForEmail(email);
})
.then((emailData) => {
const link = emailData.headers['x-link'];
const query = url.parse(link, true).query;
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');
});
});
it('password forgot status with valid token', () => {
const email = server.uniqueEmail();
const password = 'something';
return Client.create(config.publicUrl, email, password, testOptions).then(
(c) => {
return c
.forgotPassword()
.then(() => {
return c.api.passwordForgotStatus(c.passwordForgotToken);
})
.then((x) => {
assert.equal(x.tries, 3, 'three tries remaining');
assert.ok(
x.ttl > 0 && x.ttl <= config.tokenLifetimes.passwordForgotToken,
'ttl is ok'
);
});
}
);
});
it('password forgot status with valid token', () => {
const email = server.uniqueEmail();
const password = 'something';
return Client.create(config.publicUrl, email, password, testOptions).then(
(c) => {
return c
.forgotPassword()
.then(() => {
return c.api.passwordForgotStatus(c.passwordForgotToken);
})
.then((x) => {
assert.equal(x.tries, 3, 'three tries remaining');
assert.ok(
x.ttl > 0 && x.ttl <= config.tokenLifetimes.passwordForgotToken,
'ttl is ok'
);
});
}
);
});
it('password forgot status with invalid token', () => {
const client = new Client(config.publicUrl, testOptions);
return client.api
.passwordForgotStatus(
'0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF'
)
.then(
() => assert(false),
(err) => {
assert.equal(err.errno, 110, 'invalid token');
}
);
});
it('password forgot status with invalid token', () => {
const client = new Client(config.publicUrl, testOptions);
return client.api
.passwordForgotStatus(
'0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF'
)
.then(
() => assert(false),
(err) => {
assert.equal(err.errno, 110, 'invalid token');
}
);
});
it('/password/forgot/verify_code should set an unverified account as verified', () => {
const email = server.uniqueEmail();
const password = 'something';
let client = null;
return Client.create(config.publicUrl, email, password, testOptions)
.then((c) => {
client = c;
})
.then(() => {
return client.emailStatus();
})
.then((status) => {
assert.equal(status.verified, false, 'email unverified');
})
.then(() => {
return server.mailbox.waitForCode(email); // ignore this code
})
.then(() => {
return client.forgotPassword();
})
.then(() => {
return server.mailbox.waitForCode(email);
})
.then((code) => {
return client.verifyPasswordResetCode(code);
})
.then(() => {
return client.emailStatus();
})
.then((status) => {
assert.equal(status.verified, true, 'account unverified');
});
});
it('/password/forgot/verify_code should set an unverified account as verified', () => {
const email = server.uniqueEmail();
const password = 'something';
let client = null;
return Client.create(config.publicUrl, email, password, testOptions)
.then((c) => {
client = c;
})
.then(() => {
return client.emailStatus();
})
.then((status) => {
assert.equal(status.verified, false, 'email unverified');
})
.then(() => {
return server.mailbox.waitForCode(email); // ignore this code
})
.then(() => {
return client.forgotPassword();
})
.then(() => {
return server.mailbox.waitForCode(email);
})
.then((code) => {
return client.verifyPasswordResetCode(code);
})
.then(() => {
return client.emailStatus();
})
.then((status) => {
assert.equal(status.verified, true, 'account unverified');
});
});
it('forgot password with service query parameter', () => {
const email = server.uniqueEmail();
const options = {
...testOptions,
redirectTo: `https://sync.${config.smtp.redirectDomain}/`,
serviceQuery: 'sync',
};
let client;
return Client.create(config.publicUrl, email, 'wibble', options)
.then((c) => {
client = c;
})
.then(() => {
return server.mailbox.waitForEmail(email);
})
.then(() => {
return client.forgotPassword();
})
.then(() => {
return server.mailbox.waitForEmail(email);
})
.then((emailData) => {
const link = emailData.headers['x-link'];
const query = url.parse(link, true).query;
assert.equal(
query.service,
options.serviceQuery,
'service is in link'
);
});
});
it('forgot password with service query parameter', () => {
const email = server.uniqueEmail();
const options = {
...testOptions,
redirectTo: `https://sync.${config.smtp.redirectDomain}/`,
serviceQuery: 'sync',
};
let client;
return Client.create(config.publicUrl, email, 'wibble', options)
.then((c) => {
client = c;
})
.then(() => {
return server.mailbox.waitForEmail(email);
})
.then(() => {
return client.forgotPassword();
})
.then(() => {
return server.mailbox.waitForEmail(email);
})
.then((emailData) => {
const link = emailData.headers['x-link'];
const query = url.parse(link, true).query;
assert.equal(
query.service,
options.serviceQuery,
'service is in link'
);
});
});
it('forgot password, then get device list', () => {
const email = server.uniqueEmail();
const newPassword = 'foo';
let client;
return Client.createAndVerify(
config.publicUrl,
email,
'bar',
server.mailbox,
testOptions
)
.then((c) => {
client = c;
return client.updateDevice({
name: 'baz',
type: 'mobile',
pushCallback: 'https://updates.push.services.mozilla.com/qux',
pushPublicKey: mocks.MOCK_PUSH_KEY,
pushAuthKey: base64url(crypto.randomBytes(16)),
});
})
.then(() => {
return client.devices();
})
.then((devices) => {
assert.equal(devices.length, 1, 'devices list contains 1 item');
})
.then(() => {
return client.forgotPassword();
})
.then(() => {
return server.mailbox.waitForCode(email);
})
.then((code) => {
return resetPassword(client, code, newPassword);
})
.then(() => {
return upgradeCredentials(email, newPassword);
})
.then(() => {
return Client.login(
config.publicUrl,
email,
newPassword,
testOptions
);
})
.then((client) => {
return client.devices();
})
.then((devices) => {
assert.equal(devices.length, 0, 'devices list is empty');
});
});
it('forgot password, then get device list', () => {
const email = server.uniqueEmail();
const newPassword = 'foo';
let client;
return Client.createAndVerify(
config.publicUrl,
email,
'bar',
server.mailbox,
testOptions
)
.then((c) => {
client = c;
return client.updateDevice({
name: 'baz',
type: 'mobile',
pushCallback: 'https://updates.push.services.mozilla.com/qux',
pushPublicKey: mocks.MOCK_PUSH_KEY,
pushAuthKey: base64url(crypto.randomBytes(16)),
});
})
.then(() => {
return client.devices();
})
.then((devices) => {
assert.equal(devices.length, 1, 'devices list contains 1 item');
})
.then(() => {
return client.forgotPassword();
})
.then(() => {
return server.mailbox.waitForCode(email);
})
.then((code) => {
return resetPassword(client, code, newPassword);
})
.then(() => {
return upgradeCredentials(email, newPassword);
})
.then(() => {
return Client.login(
config.publicUrl,
email,
newPassword,
testOptions
);
})
.then((client) => {
return client.devices();
})
.then((devices) => {
assert.equal(devices.length, 0, 'devices list is empty');
});
});
after(() => {
return TestServer.stop(server);
});
async function resetPassword(client, code, newPassword, headers, options) {
await client.verifyPasswordResetCode(code, headers, options);
await client.resetPassword(newPassword, {}, options);
}
async function resetPassword(client, code, newPassword, headers, options) {
await client.verifyPasswordResetCode(code, headers, options);
await client.resetPassword(newPassword, {}, options);
}
async function upgradeCredentials(email, newPassword) {
if (testOptions.version === 'V2') {
await Client.upgradeCredentials(config.publicUrl, email, newPassword, {
version: '',
key: true,
});
}
}
});
});
async function upgradeCredentials(email, newPassword) {
if (testOptions.version === 'V2') {
await Client.upgradeCredentials(config.publicUrl, email, newPassword, {
version: '',
key: true,
});
}
}
});
});

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

@ -50,18 +50,17 @@ const mockLog = {
};
describe(`#integration - remote push db`, function () {
this.timeout(15000);
this.timeout(60000);
let dbServer, db;
before(() => {
return TestServer.start(config)
.then((s) => {
dbServer = s;
return DB.connect(config);
})
.then((x) => {
db = x;
});
before(async () => {
dbServer = await TestServer.start(config);
db = await DB.connect(config);
});
after(async () => {
await TestServer.stop(dbServer);
await db.close();
});
it('push db tests', () => {
@ -179,7 +178,5 @@ describe(`#integration - remote push db`, function () {
});
});
after(() => {
return Promise.all([TestServer.stop(dbServer), db.close()]);
});
});

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

@ -14,6 +14,8 @@ const BASE_36 = require('../../lib/routes/validators').BASE_36;
[{version:""},{version:"V2"}].forEach((testOptions) => {
describe(`#integration${testOptions.version} - remote backup authentication codes`, function () {
this.timeout(60000);
let server, client, email, recoveryCodes;
const recoveryCodeCount = 9;
const password = 'pssssst';
@ -22,19 +24,21 @@ describe(`#integration${testOptions.version} - remote backup authentication code
flowId: '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef',
};
this.timeout(10000);
otplib.authenticator.options = {
encoding: 'hex',
window: 10,
};
before(() => {
before(async () => {
config.totp.recoveryCodes.count = recoveryCodeCount;
config.totp.recoveryCodes.notifyLowCount = recoveryCodeCount - 2;
return TestServer.start(config).then((s) => {
server = s;
});
server = await TestServer.start(config);
});
after(async () => {
await TestServer.stop(server);
});
beforeEach(() => {
@ -248,9 +252,7 @@ describe(`#integration${testOptions.version} - remote backup authentication code
});
});
after(() => {
return TestServer.stop(server);
});
});
});

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

@ -15,14 +15,16 @@ const password = 'allyourbasearebelongtous',
[ {version:""}, {version:"V2"}].forEach((testOptions) => {
describe(`#integration${testOptions.version} - remote change email`, function () {
this.timeout(30000);
this.timeout(60000);
before(() => {
before(async () => {
config = require('../../config').default.getProperties();
config.securityHistory.ipProfiling = {};
return TestServer.start(config).then((s) => {
server = s;
});
server = await TestServer.start(config);
});
after(async () => {
await TestServer.stop(server);
});
beforeEach(() => {
@ -147,22 +149,29 @@ describe(`#integration${testOptions.version} - remote change email`, function ()
.then((res) => {
assert.ok(res, 'ok response');
if (testOptions.version === "V2") {
if (testOptions.version === 'V2') {
// Note for V2 we can login with new primary email. The password is not encrypted with
// the original email, so this now works!
return Client.login(config.publicUrl, secondEmail, password, testOptions);
}
else {
// Verify account can login with new primary email
return Client.login(config.publicUrl, secondEmail, password, testOptions).then(
() => {
assert.fail(
new Error(
'Should have returned correct email for user to login'
)
);
}
return Client.login(
config.publicUrl,
secondEmail,
password,
testOptions
);
} else {
// Verify account can login with new primary email
return Client.login(
config.publicUrl,
secondEmail,
password,
testOptions
).then(() => {
assert.fail(
new Error(
'Should have returned correct email for user to login'
)
);
});
}
})
.catch((err) => {
@ -175,7 +184,7 @@ describe(`#integration${testOptions.version} - remote change email`, function ()
return Client.login(config.publicUrl, err.email, password, {
originalLoginEmail: secondEmail,
...testOptions
...testOptions,
});
})
.then((res) => {
@ -190,7 +199,7 @@ describe(`#integration${testOptions.version} - remote change email`, function ()
assert.ok(res, 'ok response');
return Client.login(config.publicUrl, email, password, {
originalLoginEmail: secondEmail,
...testOptions
...testOptions,
});
})
.then((res) => {
@ -201,7 +210,7 @@ describe(`#integration${testOptions.version} - remote change email`, function ()
assert.ok(res, 'ok response');
return Client.login(config.publicUrl, email, newPassword, {
originalLoginEmail: secondEmail,
...testOptions
...testOptions,
});
})
.then((res) => {
@ -240,18 +249,23 @@ describe(`#integration${testOptions.version} - remote change email`, function ()
assert.ok(res, 'ok response');
})
.then(() => {
if (testOptions.version === "V2") {
return Client.upgradeCredentials(config.publicUrl, email, newPassword, {
originalLoginEmail: secondEmail,
version: '',
keys: true
});
if (testOptions.version === 'V2') {
return Client.upgradeCredentials(
config.publicUrl,
email,
newPassword,
{
originalLoginEmail: secondEmail,
version: '',
keys: true,
}
);
}
})
.then(() => {
return Client.login(config.publicUrl, email, newPassword, {
originalLoginEmail: secondEmail,
...testOptions
...testOptions,
});
})
.then((res) => {
@ -269,7 +283,7 @@ describe(`#integration${testOptions.version} - remote change email`, function ()
.then(() => {
return Client.login(config.publicUrl, email, newPassword, {
originalLoginEmail: secondEmail,
...testOptions
...testOptions,
})
.then(() => {
assert.fail(
@ -356,7 +370,7 @@ describe(`#integration${testOptions.version} - remote change email`, function ()
const res = await Client.login(config.publicUrl, client1Email, password, {
originalLoginEmail: client2Email,
...testOptions
...testOptions,
});
assert.ok(res, 'ok response');
@ -399,13 +413,17 @@ describe(`#integration${testOptions.version} - remote change email`, function ()
});
it('can login', () => {
if (testOptions.version === "V2") {
if (testOptions.version === 'V2') {
// Note that with V2 logins, you can actually use the secondary email to login. This is
// due to the fact the salt is now independent of the original email.
return Client.login(config.publicUrl, secondEmail, password, testOptions).then((res) => {
assert.exists(res.sessionToken)
})
return Client.login(
config.publicUrl,
secondEmail,
password,
testOptions
).then((res) => {
assert.exists(res.sessionToken);
});
}
// Verify account can still login with new primary email
@ -425,7 +443,7 @@ describe(`#integration${testOptions.version} - remote change email`, function ()
return Client.login(config.publicUrl, err.email, password, {
originalLoginEmail: secondEmail,
...testOptions
...testOptions,
});
})
.then((res) => {
@ -436,7 +454,7 @@ describe(`#integration${testOptions.version} - remote change email`, function ()
it('can change password', () => {
return Client.login(config.publicUrl, email, password, {
originalLoginEmail: secondEmail,
...testOptions
...testOptions,
})
.then((res) => {
client = res;
@ -446,7 +464,7 @@ describe(`#integration${testOptions.version} - remote change email`, function ()
assert.ok(res, 'ok response');
return Client.login(config.publicUrl, email, newPassword, {
originalLoginEmail: secondEmail,
...testOptions
...testOptions,
});
})
.then((res) => {
@ -471,18 +489,23 @@ describe(`#integration${testOptions.version} - remote change email`, function ()
assert.ok(res, 'ok response');
})
.then(() => {
if (testOptions.version === "V2") {
return Client.upgradeCredentials(config.publicUrl, email, newPassword, {
originalLoginEmail: secondEmail,
version: '',
keys:true
});
if (testOptions.version === 'V2') {
return Client.upgradeCredentials(
config.publicUrl,
email,
newPassword,
{
originalLoginEmail: secondEmail,
version: '',
keys: true,
}
);
}
})
.then(() => {
return Client.login(config.publicUrl, email, newPassword, {
originalLoginEmail: secondEmail,
...testOptions
...testOptions,
});
})
.then((res) => {
@ -494,7 +517,7 @@ describe(`#integration${testOptions.version} - remote change email`, function ()
return client.destroyAccount().then(() => {
return Client.login(config.publicUrl, email, newPassword, {
originalLoginEmail: secondEmail,
...testOptions
...testOptions,
})
.then(() => {
assert.fail(
@ -509,9 +532,7 @@ describe(`#integration${testOptions.version} - remote change email`, function ()
});
});
after(() => {
return TestServer.stop(server);
});
function resetPassword(client, code, newPassword, headers, options) {
return client.verifyPasswordResetCode(code, headers, options).then(() => {

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

@ -15,16 +15,18 @@ const password = 'allyourbasearebelongtous';
[{version:""},{version:"V2"}].forEach((testOptions) => {
describe(`#integration${testOptions.version} - remote emails`, function () {
this.timeout(30000);
this.timeout(60000);
before(() => {
before(async function () {
config = require('../../config').default.getProperties();
config.securityHistory.ipProfiling = {};
config.signinConfirmation.skipForNewAccounts.enabled = false;
return TestServer.start(config).then((s) => {
server = s;
});
server = await TestServer.start(config);
});
after(async () => {
await TestServer.stop(server);
});
beforeEach(() => {
@ -572,10 +574,16 @@ describe(`#integration${testOptions.version} - remote emails`, function () {
assert.equal(emailData.cc[0].address, secondEmail);
})
.then(() => {
if (testOptions.version === "V2") {
return Client.upgradeCredentials(config.publicUrl, email, 'password1', {version:'', keys:true})
if (testOptions.version === 'V2') {
return Client.upgradeCredentials(
config.publicUrl,
email,
'password1',
{ version: '', keys: true }
);
}
}).then((x) => {
})
.then((x) => {
if (x) {
client = x;
}
@ -627,53 +635,56 @@ describe(`#integration${testOptions.version} - remote emails`, function () {
});
});
it('receives new device sign-in email', () => {
config.signinConfirmation.skipForNewAccounts.enabled = true;
return TestServer.start(config)
.then((s) => {
server = s;
email = server.uniqueEmail();
secondEmail = server.uniqueEmail();
thirdEmail = server.uniqueEmail();
return Client.createAndVerify(
config.publicUrl,
email,
password,
server.mailbox,
testOptions
);
})
.then((x) => {
client = x;
return client.createEmail(secondEmail);
})
.then(() => {
return server.mailbox.waitForCode(secondEmail);
})
.then((code) => {
return client
.verifySecondaryEmailWithCode(code, secondEmail)
.then(() => {
// Clear add secondary email notification
return server.mailbox.waitForEmail(email);
});
})
.then(() => {
// Create unverified email
return client.createEmail(thirdEmail);
})
.then(() => {
return client.login({ keys: true });
})
.then(() => {
return server.mailbox.waitForEmail(email);
})
.then((emailData) => {
const templateName = emailData['headers']['x-template-name'];
assert.equal(templateName, 'newDeviceLogin');
assert.equal(emailData.cc.length, 1);
assert.equal(emailData.cc[0].address, secondEmail);
});
describe('new device signin', function () {
let skipForNewAccountsEnabled;
before(async function () {
// Stop currently running server, and create new config
await TestServer.stop(server);
skipForNewAccountsEnabled =
config.signinConfirmation.skipForNewAccounts.enabled;
config.signinConfirmation.skipForNewAccounts.enabled = true;
server = await TestServer.start(config);
});
after(async function () {
// Restore server to previous config
await TestServer.stop(server);
config.signinConfirmation.skipForNewAccounts.enabled =
skipForNewAccountsEnabled;
server = await TestServer.start(config);
});
it('receives new device sign-in email', async function () {
email = server.uniqueEmail();
secondEmail = server.uniqueEmail();
thirdEmail = server.uniqueEmail();
const client = await Client.createAndVerify(
config.publicUrl,
email,
password,
server.mailbox,
testOptions
);
await client.createEmail(secondEmail);
const code = await server.mailbox.waitForCode(secondEmail);
await client.verifySecondaryEmailWithCode(code, secondEmail);
// Clear add secondary email notification
await server.mailbox.waitForEmail(email);
// Create unverified email
await client.createEmail(thirdEmail);
// Login again
await client.login({ keys: true });
const emailData = await server.mailbox.waitForEmail(email);
// Check for new device lgoin email
const templateName = emailData['headers']['x-template-name'];
assert.equal(templateName, 'newDeviceLogin');
assert.equal(emailData.cc.length, 1);
assert.equal(emailData.cc[0].address, secondEmail);
});
});
});
@ -799,7 +810,12 @@ describe(`#integration${testOptions.version} - remote emails`, function () {
return server.mailbox.waitForEmail(email);
})
.then(() => {
return Client.create(config.publicUrl, secondEmail, password, testOptions)
return Client.create(
config.publicUrl,
secondEmail,
password,
testOptions
)
.then(assert.fail)
.catch((err) => {
assert.equal(err.errno, 144, 'return correct errno');
@ -931,9 +947,7 @@ describe(`#integration${testOptions.version} - remote emails`, function () {
});
});
after(() => {
return TestServer.stop(server);
});
function resetPassword(client, code, newPassword, headers, options) {
return client.verifyPasswordResetCode(code, headers, options).then(() => {

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

@ -13,15 +13,16 @@ const config = require('../../config').default.getProperties();
[{version:""},{version:"V2"}].forEach((testOptions) => {
describe(`#integration${testOptions.version} - remote recovery email resend code`, function () {
this.timeout(15000);
this.timeout(60000);
let server;
before(() => {
before(async () => {
config.securityHistory.ipProfiling.allowedRecency = 0;
config.signinConfirmation.skipForNewAccounts.enabled = false;
server = await TestServer.start(config);
});
return TestServer.start(config).then((s) => {
server = s;
});
after(async () => {
await TestServer.stop(server);
});
it('sign-in verification resend email verify code', () => {
@ -36,25 +37,13 @@ describe(`#integration${testOptions.version} - remote recovery email resend code
resume: 'resumeToken',
keys: true,
};
return Client.create(
config.publicUrl,
email,
password,
options
)
return Client.create(config.publicUrl, email, password, options)
.then((c) => {
client = c;
// Clear first account create email and login again
return server.mailbox
.waitForEmail(email)
.then(() =>
Client.login(
config.publicUrl,
email,
password,
options
)
)
.then(() => Client.login(config.publicUrl, email, password, options))
.then((c) => (client = c));
})
.then(() => server.mailbox.waitForCode(email))
@ -100,12 +89,7 @@ describe(`#integration${testOptions.version} - remote recovery email resend code
)
.then(() => {
// Attempt to login from new location
return Client.login(
config.publicUrl,
email,
password,
options
);
return Client.login(config.publicUrl, email, password, options);
})
.then((c) => {
client2 = c;
@ -162,12 +146,7 @@ describe(`#integration${testOptions.version} - remote recovery email resend code
server.mailbox,
options
),
Client.create(
config.publicUrl,
secondEmail,
password,
options
),
Client.create(config.publicUrl, secondEmail, password, options),
])
.then((res) => {
// Login with `email` and attempt to resend verification code for `secondEmail`
@ -232,9 +211,7 @@ describe(`#integration${testOptions.version} - remote recovery email resend code
});
});
after(() => {
return TestServer.stop(server);
});
});
})

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

@ -14,12 +14,14 @@ const config = require('../../config').default.getProperties();
[{version:""},{version:"V2"}].forEach((testOptions) => {
describe(`#integration${testOptions.version} - remote recovery email verify`, function () {
this.timeout(15000);
this.timeout(60000);
let server;
before(() => {
return TestServer.start(config).then((s) => {
server = s;
});
before(async () => {
server = await TestServer.start(config);
});
after(async () => {
await TestServer.stop(server);
});
it('create account verify with incorrect code', () => {
@ -85,9 +87,7 @@ describe(`#integration${testOptions.version} - remote recovery email verify`, fu
});
});
after(() => {
return TestServer.stop(server);
});
});
});

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

@ -2,385 +2,395 @@
* 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';
'use strict';
const { assert } = require('chai');
const config = require('../../config').default.getProperties();
const crypto = require('crypto');
const TestServer = require('../test_server');
const Client = require('../client')();
const { JWTool } = require('@fxa/vendored/jwtool');
const { assert } = require('chai');
const config = require('../../config').default.getProperties();
const crypto = require('crypto');
const TestServer = require('../test_server');
const Client = require('../client')();
const { JWTool } = require('@fxa/vendored/jwtool');
[{ version: '' }, { version: 'V2' }].forEach((testOptions) => {
describe(`#integration${testOptions.version} - remote recovery keys`, function () {
this.timeout(10000);
[{ version: '' }, { version: 'V2' }].forEach((testOptions) => {
describe(`#integration${testOptions.version} - remote recovery keys`, function () {
this.timeout(60000);
let server, client, email;
const password = '(-.-)Zzz...';
let server, client, email;
const password = '(-.-)Zzz...';
let recoveryKeyId;
let recoveryData;
let keys;
let recoveryKeyId;
let recoveryData;
let keys;
function createMockRecoveryKey() {
// The auth-server does not care about the encryption details of the recovery data.
// To simplify things, we can mock out some random bits to be stored. Check out
// /docs/recovery_keys.md for more details on the encryption that a client
// could perform.
const recoveryCode = crypto.randomBytes(16).toString('hex');
const recoveryKeyId = crypto.randomBytes(16).toString('hex');
const recoveryKey = crypto.randomBytes(16).toString('hex');
const recoveryData = crypto.randomBytes(32).toString('hex');
function createMockRecoveryKey() {
// The auth-server does not care about the encryption details of the recovery data.
// To simplify things, we can mock out some random bits to be stored. Check out
// /docs/recovery_keys.md for more details on the encryption that a client
// could perform.
const recoveryCode = crypto.randomBytes(16).toString('hex');
const recoveryKeyId = crypto.randomBytes(16).toString('hex');
const recoveryKey = crypto.randomBytes(16).toString('hex');
const recoveryData = crypto.randomBytes(32).toString('hex');
return Promise.resolve({
recoveryCode,
recoveryData,
recoveryKeyId,
recoveryKey,
});
}
return Promise.resolve({
recoveryCode,
recoveryData,
recoveryKeyId,
recoveryKey,
});
}
before(() => {
return TestServer.start(config).then((s) => (server = s));
});
before(async () => {
server = await TestServer.start(config);
});
beforeEach(() => {
email = server.uniqueEmail();
return Client.createAndVerify(
config.publicUrl,
email,
password,
server.mailbox,
{
...testOptions,
keys: true,
}
)
.then((x) => {
client = x;
assert.ok(client.authAt, 'authAt was set');
after(async () => {
await TestServer.stop(server);
});
return client.keys();
})
.then((result) => {
keys = result;
beforeEach(() => {
email = server.uniqueEmail();
return Client.createAndVerify(
config.publicUrl,
email,
password,
server.mailbox,
{
...testOptions,
keys: true,
}
)
.then((x) => {
client = x;
assert.ok(client.authAt, 'authAt was set');
return createMockRecoveryKey(client.uid, keys.kB).then((result) => {
recoveryKeyId = result.recoveryKeyId;
recoveryData = result.recoveryData;
// Should create account recovery key
return client
.createRecoveryKey(result.recoveryKeyId, result.recoveryData)
.then((res) => assert.ok(res, 'empty response'))
.then(() => server.mailbox.waitForEmail(email))
.then((emailData) => {
assert.equal(
emailData.headers['x-template-name'],
'postAddAccountRecovery'
);
});
});
});
});
return client.keys();
})
.then((result) => {
keys = result;
it('should get account recovery key', () => {
return getAccountResetToken(client, server, email)
.then(() => client.getRecoveryKey(recoveryKeyId))
.then((res) => {
assert.equal(res.recoveryData, recoveryData, 'recoveryData returned');
});
});
return createMockRecoveryKey(client.uid, keys.kB).then((result) => {
recoveryKeyId = result.recoveryKeyId;
recoveryData = result.recoveryData;
// Should create account recovery key
return client
.createRecoveryKey(result.recoveryKeyId, result.recoveryData)
.then((res) => assert.ok(res, 'empty response'))
.then(() => server.mailbox.waitForEmail(email))
.then((emailData) => {
assert.equal(
emailData.headers['x-template-name'],
'postAddAccountRecovery'
);
});
});
});
});
it('should fail to get unknown account recovery key', () => {
return getAccountResetToken(client, server, email)
.then(() => client.getRecoveryKey('abce1234567890'))
.then(assert.fail, (err) => {
assert.equal(err.errno, 159, 'account recovery key is not valid');
});
});
it('should get account recovery key', () => {
return getAccountResetToken(client, server, email)
.then(() => client.getRecoveryKey(recoveryKeyId))
.then((res) => {
assert.equal(
res.recoveryData,
recoveryData,
'recoveryData returned'
);
});
});
async function checkPayloadV2(mutate, restore) {
await getAccountResetToken(client, server, email);
await client.getRecoveryKey(recoveryKeyId);
let err;
try {
mutate();
await client.api.accountResetWithRecoveryKeyV2(
client.accountResetToken,
client.authPW,
client.authPWVersion2,
client.wrapKb,
client.wrapKbVersion2,
client.clientSalt,
recoveryKeyId,
undefined,
{}
);
} catch (error) {
err = error;
} finally {
restore();
}
it('should fail to get unknown account recovery key', () => {
return getAccountResetToken(client, server, email)
.then(() => client.getRecoveryKey('abce1234567890'))
.then(assert.fail, (err) => {
assert.equal(err.errno, 159, 'account recovery key is not valid');
});
});
assert.exists(err);
assert.equal(err.errno, 107, 'invalid param');
}
async function checkPayloadV2(mutate, restore) {
await getAccountResetToken(client, server, email);
await client.getRecoveryKey(recoveryKeyId);
let err;
try {
mutate();
await client.api.accountResetWithRecoveryKeyV2(
client.accountResetToken,
client.authPW,
client.authPWVersion2,
client.wrapKb,
client.wrapKbVersion2,
client.clientSalt,
recoveryKeyId,
undefined,
{}
);
} catch (error) {
err = error;
} finally {
restore();
}
it('should fail if wrapKb is missing and authPWVersion2 is provided', async function () {
if (testOptions.version !== 'V2') {
return this.skip();
}
const temp = client.wrapKb;
await checkPayloadV2(
() => {
client.unwrapBKey = undefined;
client.wrapKb = undefined;
},
() => {
client.wrapKb = temp;
}
);
});
assert.exists(err);
assert.equal(err.errno, 107, 'invalid param');
}
it('should fail if wrapKbVersion2 is missing and authPWVersion2 is provided', async function () {
if (testOptions.version !== 'V2') {
return this.skip();
}
it('should fail if wrapKb is missing and authPWVersion2 is provided', async function () {
if (testOptions.version !== 'V2') {
return this.skip();
}
const temp = client.wrapKb;
await checkPayloadV2(
() => {
client.unwrapBKey = undefined;
client.wrapKb = undefined;
},
() => {
client.wrapKb = temp;
}
);
});
const temp = client.wrapKbVersion2;
await checkPayloadV2(
() => {
client.wrapKbVersion2 = undefined;
},
() => {
client.wrapKbVersion2 = temp;
}
);
});
it('should fail if wrapKbVersion2 is missing and authPWVersion2 is provided', async function () {
if (testOptions.version !== 'V2') {
return this.skip();
}
it('should fail if clientSalt is missing and authPWVersion2 is provided', async function () {
if (testOptions.version !== 'V2') {
return this.skip();
}
const temp = client.clientSalt;
await checkPayloadV2(
() => {
client.clientSalt = undefined;
},
() => {
client.clientSalt = temp;
}
);
});
const temp = client.wrapKbVersion2;
await checkPayloadV2(
() => {
client.wrapKbVersion2 = undefined;
},
() => {
client.wrapKbVersion2 = temp;
}
);
});
it('should fail if recoveryKeyId is missing', function () {
if (testOptions.version === 'V2') {
return this.skip();
}
it('should fail if clientSalt is missing and authPWVersion2 is provided', async function () {
if (testOptions.version !== 'V2') {
return this.skip();
}
const temp = client.clientSalt;
await checkPayloadV2(
() => {
client.clientSalt = undefined;
},
() => {
client.clientSalt = temp;
}
);
});
return getAccountResetToken(client, server, email)
.then(() => client.getRecoveryKey(recoveryKeyId))
.then((res) =>
assert.equal(res.recoveryData, recoveryData, 'recoveryData returned')
)
.then(() =>
client.resetAccountWithRecoveryKey(
'newpass',
keys.kB,
undefined,
{},
{ keys: true }
)
)
.then(assert.fail, (err) => {
assert.equal(err.errno, 107, 'invalid param');
});
});
it('should fail if recoveryKeyId is missing', function () {
if (testOptions.version === 'V2') {
return this.skip();
}
it('should fail if wrapKb is missing', function () {
if (testOptions.version === 'V2') {
return this.skip();
}
return getAccountResetToken(client, server, email)
.then(() => client.getRecoveryKey(recoveryKeyId))
.then((res) =>
assert.equal(res.recoveryData, recoveryData, 'recoveryData returned')
)
.then(() =>
client.resetAccountWithRecoveryKey(
'newpass',
keys.kB,
undefined,
{},
{ keys: true }
)
)
.then(assert.fail, (err) => {
assert.equal(err.errno, 107, 'invalid param');
});
});
return getAccountResetToken(client, server, email)
.then(() => client.getRecoveryKey(recoveryKeyId))
.then((res) =>
assert.equal(res.recoveryData, recoveryData, 'recoveryData returned')
)
.then(() =>
client.resetAccountWithRecoveryKey(
'newpass',
keys.kB,
recoveryKeyId,
{},
{ keys: true, undefinedWrapKb: true }
)
)
.then(assert.fail, (err) => {
assert.equal(err.errno, 107, 'invalid param');
});
});
it('should fail if wrapKb is missing', function () {
if (testOptions.version === 'V2') {
return this.skip();
}
it('should reset password while keeping kB', async () => {
await getAccountResetToken(client, server, email);
let res = await client.getRecoveryKey(recoveryKeyId);
assert.equal(res.recoveryData, recoveryData, 'recoveryData returned');
return getAccountResetToken(client, server, email)
.then(() => client.getRecoveryKey(recoveryKeyId))
.then((res) =>
assert.equal(res.recoveryData, recoveryData, 'recoveryData returned')
)
.then(() =>
client.resetAccountWithRecoveryKey(
'newpass',
keys.kB,
recoveryKeyId,
{},
{ keys: true, undefinedWrapKb: true }
)
)
.then(assert.fail, (err) => {
assert.equal(err.errno, 107, 'invalid param');
});
});
const duration = 1000 * 60 * 60 * 24; // 24 hours
const publicKey = {
algorithm: 'RS',
n: '4759385967235610503571494339196749614544606692567785790953934768202714280652973091341316862993582789079872007974809511698859885077002492642203267408776123',
e: '65537',
};
const cert1 = JWTool.unverify(
await client.sign(publicKey, duration)
).payload;
it('should reset password while keeping kB', async () => {
await getAccountResetToken(client, server, email);
let res = await client.getRecoveryKey(recoveryKeyId);
assert.equal(res.recoveryData, recoveryData, 'recoveryData returned');
res = await client.resetAccountWithRecoveryKey(
'newpass',
keys.kB,
recoveryKeyId,
{},
{ keys: true }
);
assert.equal(res.uid, client.uid, 'uid returned');
assert.ok(res.sessionToken, 'sessionToken return');
const duration = 1000 * 60 * 60 * 24; // 24 hours
const publicKey = {
algorithm: 'RS',
n: '4759385967235610503571494339196749614544606692567785790953934768202714280652973091341316862993582789079872007974809511698859885077002492642203267408776123',
e: '65537',
};
const cert1 = JWTool.unverify(
await client.sign(publicKey, duration)
).payload;
const emailData = await server.mailbox.waitForEmail(email);
assert.equal(
emailData.headers['x-template-name'],
'passwordResetAccountRecovery',
'correct template sent'
);
res = await client.resetAccountWithRecoveryKey(
'newpass',
keys.kB,
recoveryKeyId,
{},
{ keys: true }
);
assert.equal(res.uid, client.uid, 'uid returned');
assert.ok(res.sessionToken, 'sessionToken return');
res = await client.keys();
assert.equal(res.kA, keys.kA, 'kA are equal returned');
assert.equal(res.kB, keys.kB, 'kB are equal returned');
const emailData = await server.mailbox.waitForEmail(email);
assert.equal(
emailData.headers['x-template-name'],
'passwordResetAccountRecovery',
'correct template sent'
);
// Login with new password and check to see kB hasn't changed
const c = await Client.login(config.publicUrl, email, 'newpass', {
...testOptions,
keys: true,
});
assert.ok(c.sessionToken, 'sessionToken returned');
res = await c.keys();
assert.equal(res.kA, keys.kA, 'kA are equal returned');
assert.equal(res.kB, keys.kB, 'kB are equal returned');
res = await client.keys();
assert.equal(res.kA, keys.kA, 'kA are equal returned');
assert.equal(res.kB, keys.kB, 'kB are equal returned');
const cert2 = JWTool.unverify(await c.sign(publicKey, duration)).payload;
// Login with new password and check to see kB hasn't changed
const c = await Client.login(config.publicUrl, email, 'newpass', {
...testOptions,
keys: true,
});
assert.ok(c.sessionToken, 'sessionToken returned');
res = await c.keys();
assert.equal(res.kA, keys.kA, 'kA are equal returned');
assert.equal(res.kB, keys.kB, 'kB are equal returned');
assert.equal(cert1['fxa-uid'], cert2['fxa-uid']);
assert.ok(cert1['fxa-generation'] < cert2['fxa-generation']);
assert.equal(cert1['fxa-keysChangedAt'], cert2['fxa-keysChangedAt']);
});
const cert2 = JWTool.unverify(await c.sign(publicKey, duration)).payload;
it('should delete account recovery key', () => {
return client.deleteRecoveryKey().then((res) => {
assert.ok(res, 'empty response');
return client
.getRecoveryKeyExists()
.then((result) => {
assert.equal(result.exists, false, 'account recovery key deleted');
})
.then(() => server.mailbox.waitForEmail(email))
.then((emailData) => {
assert.equal(
emailData.headers['x-template-name'],
'postRemoveAccountRecovery'
);
});
});
});
assert.equal(cert1['fxa-uid'], cert2['fxa-uid']);
assert.ok(cert1['fxa-generation'] < cert2['fxa-generation']);
assert.equal(cert1['fxa-keysChangedAt'], cert2['fxa-keysChangedAt']);
});
it('should fail to create account recovery key when one already exists', () => {
return createMockRecoveryKey(client.uid, keys.kB).then((result) => {
recoveryKeyId = result.recoveryKeyId;
recoveryData = result.recoveryData;
return client
.createRecoveryKey(result.recoveryKeyId, result.recoveryData)
.then(assert.fail, (err) => {
assert.equal(err.errno, 161, 'correct errno');
});
});
});
it('should delete account recovery key', () => {
return client.deleteRecoveryKey().then((res) => {
assert.ok(res, 'empty response');
return client
.getRecoveryKeyExists()
.then((result) => {
assert.equal(result.exists, false, 'account recovery key deleted');
})
.then(() => server.mailbox.waitForEmail(email))
.then((emailData) => {
assert.equal(
emailData.headers['x-template-name'],
'postRemoveAccountRecovery'
);
});
});
});
describe('check account recovery key status', () => {
describe('with sessionToken', () => {
it('should return true if account recovery key exists and enabled', () => {
return client.getRecoveryKeyExists().then((res) => {
assert.equal(res.exists, true, 'account recovery key exists');
});
});
it('should fail to create account recovery key when one already exists', () => {
return createMockRecoveryKey(client.uid, keys.kB).then((result) => {
recoveryKeyId = result.recoveryKeyId;
recoveryData = result.recoveryData;
return client
.createRecoveryKey(result.recoveryKeyId, result.recoveryData)
.then(assert.fail, (err) => {
assert.equal(err.errno, 161, 'correct errno');
});
});
});
it("should return false if account recovery key doesn't exist", () => {
email = server.uniqueEmail();
return Client.createAndVerify(
config.publicUrl,
email,
password,
server.mailbox,
{
...testOptions,
keys: true,
}
)
.then((c) => {
client = c;
return client.getRecoveryKeyExists();
})
.then((res) => {
assert.equal(
res.exists,
false,
'account recovery key doesnt exists'
);
});
});
describe('check account recovery key status', () => {
describe('with sessionToken', () => {
it('should return true if account recovery key exists and enabled', () => {
return client.getRecoveryKeyExists().then((res) => {
assert.equal(res.exists, true, 'account recovery key exists');
});
});
it('should return false if account recovery key exist but not enabled', async () => {
const email2 = server.uniqueEmail();
const client2 = await Client.createAndVerify(
config.publicUrl,
email2,
password,
server.mailbox,
{
...testOptions,
keys: true,
}
);
const recoveryKeyMock = await createMockRecoveryKey(
client2.uid,
keys.kB
);
let res = await client2.createRecoveryKey(
recoveryKeyMock.recoveryKeyId,
recoveryKeyMock.recoveryData,
false
);
assert.deepEqual(res, {});
it("should return false if account recovery key doesn't exist", () => {
email = server.uniqueEmail();
return Client.createAndVerify(
config.publicUrl,
email,
password,
server.mailbox,
{
...testOptions,
keys: true,
}
)
.then((c) => {
client = c;
return client.getRecoveryKeyExists();
})
.then((res) => {
assert.equal(
res.exists,
false,
'account recovery key doesnt exists'
);
});
});
res = await client2.getRecoveryKeyExists();
assert.equal(res.exists, false, 'account recovery key doesnt exists');
});
});
});
it('should return false if account recovery key exist but not enabled', async () => {
const email2 = server.uniqueEmail();
const client2 = await Client.createAndVerify(
config.publicUrl,
email2,
password,
server.mailbox,
{
...testOptions,
keys: true,
}
);
const recoveryKeyMock = await createMockRecoveryKey(
client2.uid,
keys.kB
);
let res = await client2.createRecoveryKey(
recoveryKeyMock.recoveryKeyId,
recoveryKeyMock.recoveryData,
false
);
assert.deepEqual(res, {});
after(() => {
return TestServer.stop(server);
});
});
res = await client2.getRecoveryKeyExists();
assert.equal(
res.exists,
false,
'account recovery key doesnt exists'
);
});
});
});
function getAccountResetToken(client, server, email) {
return client
.forgotPassword()
.then(() => server.mailbox.waitForCode(email))
.then((code) =>
client.verifyPasswordResetCode(
code,
{},
{ accountResetWithRecoveryKey: true }
)
);
}
});
});
function getAccountResetToken(client, server, email) {
return client
.forgotPassword()
.then(() => server.mailbox.waitForCode(email))
.then((code) =>
client.verifyPasswordResetCode(
code,
{},
{ accountResetWithRecoveryKey: true }
)
);
}
});

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

@ -2,121 +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';
'use strict';
const { assert } = require('chai');
const Client = require('../client')();
const TestServer = require('../test_server');
const config = require('../../config').default.getProperties();
const { assert } = require('chai');
const Client = require('../client')();
const TestServer = require('../test_server');
const config = require('../../config').default.getProperties();
function resetPassword(client, code, newPassword, options) {
return client.verifyPasswordResetCode(code).then(() => {
return client.resetPassword(newPassword, {}, options);
});
}
function resetPassword(client, code, newPassword, options) {
return client.verifyPasswordResetCode(code).then(() => {
return client.resetPassword(newPassword, {}, options);
});
}
function delay(seconds) {
return new Promise((resolve) => setTimeout(resolve, seconds * 1000));
}
function delay(seconds) {
return new Promise((resolve) => setTimeout(resolve, seconds * 1000));
}
[{ version: '' }, { version: 'V2' }].forEach((testOptions) => {
describe(`#integration${testOptions.version} - remote securityEvents`, () => {
let server;
[{ version: '' }, { version: 'V2' }].forEach((testOptions) => {
describe(`#integration${testOptions.version} - remote securityEvents`, function () {
this.timeout(60000);
let server;
before(function () {
this.timeout(15000);
config.securityHistory.ipProfiling.allowedRecency = 0;
config.signinConfirmation.skipForNewAccounts.enabled = false;
return TestServer.start(config).then((s) => {
server = s;
});
});
before(async function () {
config.securityHistory.ipProfiling.allowedRecency = 0;
config.signinConfirmation.skipForNewAccounts.enabled = false;
server = await TestServer.start(config);
});
it('returns securityEvents on creating and login into an account', () => {
const email = server.uniqueEmail();
const password = 'abcdef';
let client;
after(async () => {
await TestServer.stop(server);
});
return Client.createAndVerify(
config.publicUrl,
email,
password,
server.mailbox,
testOptions
)
.then((x) => {
client = x;
return client.login().then(() => {
return delay(1).then(() => {
return client.securityEvents();
});
});
})
.then((events) => {
assert.equal(events.length, 2);
assert.equal(events[0].name, 'account.login');
assert.isBelow(events[0].createdAt, new Date().getTime());
assert.equal(events[0].verified, false);
it('returns securityEvents on creating and login into an account', () => {
const email = server.uniqueEmail();
const password = 'abcdef';
let client;
assert.equal(events[1].name, 'account.create');
assert.isBelow(events[1].createdAt, new Date().getTime());
assert.equal(events[1].verified, true);
});
});
return Client.createAndVerify(
config.publicUrl,
email,
password,
server.mailbox,
testOptions
)
.then((x) => {
client = x;
return client.login().then(() => {
return delay(1).then(() => {
return client.securityEvents();
});
});
})
.then((events) => {
assert.equal(events.length, 2);
assert.equal(events[0].name, 'account.login');
assert.isBelow(events[0].createdAt, new Date().getTime());
assert.equal(events[0].verified, false);
it('returns security events after account reset w/o keys, with sessionToken', () => {
const email = server.uniqueEmail();
const password = 'oldPassword';
const newPassword = 'newPassword';
let client;
assert.equal(events[1].name, 'account.create');
assert.isBelow(events[1].createdAt, new Date().getTime());
assert.equal(events[1].verified, true);
});
});
return Client.createAndVerify(
config.publicUrl,
email,
password,
server.mailbox,
testOptions
)
.then((x) => {
client = x;
})
.then(() => {
return client.forgotPassword();
})
.then(() => {
return server.mailbox.waitForCode(email);
})
.then((code) => {
assert.isRejected(client.resetPassword(newPassword));
return resetPassword(client, code, newPassword);
})
.then((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');
})
.then(() => {
return delay(1).then(() => {
return client.securityEvents();
});
})
.then((events) => {
assert.equal(events.length, 2);
assert.equal(events[0].name, 'account.reset');
assert.isBelow(events[0].createdAt, new Date().getTime());
assert.equal(events[0].verified, true);
it('returns security events after account reset w/o keys, with sessionToken', () => {
const email = server.uniqueEmail();
const password = 'oldPassword';
const newPassword = 'newPassword';
let client;
assert.equal(events[1].name, 'account.create');
assert.isBelow(events[1].createdAt, new Date().getTime());
assert.equal(events[1].verified, true);
});
});
return Client.createAndVerify(
config.publicUrl,
email,
password,
server.mailbox,
testOptions
)
.then((x) => {
client = x;
})
.then(() => {
return client.forgotPassword();
})
.then(() => {
return server.mailbox.waitForCode(email);
})
.then((code) => {
assert.isRejected(client.resetPassword(newPassword));
return resetPassword(client, code, newPassword);
})
.then((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');
})
.then(() => {
return delay(1).then(() => {
return client.securityEvents();
});
})
.then((events) => {
assert.equal(events.length, 2);
assert.equal(events[0].name, 'account.reset');
assert.isBelow(events[0].createdAt, new Date().getTime());
assert.equal(events[0].verified, true);
after(() => {
return TestServer.stop(server);
});
});
});
assert.equal(events[1].name, 'account.create');
assert.isBelow(events[1].createdAt, new Date().getTime());
assert.equal(events[1].verified, true);
});
});
});
});

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

@ -19,13 +19,14 @@ const publicKey = {
[{ version: '' }, { version: 'V2' }].forEach((testOptions) => {
describe(`#integration${testOptions.version} - remote session`, function () {
this.timeout(15000);
this.timeout(60000);
let server;
config.signinConfirmation.skipForNewAccounts.enabled = false;
before(() => {
return TestServer.start(config).then((s) => {
server = s;
});
before(async () => {
server = await TestServer.start(config);
});
after(async () => {
await TestServer.stop(server);
});
describe('destroy', () => {
@ -609,8 +610,6 @@ const publicKey = {
});
});
after(() => {
return TestServer.stop(server);
});
});
});

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

@ -10,17 +10,18 @@ const TestServer = require('../test_server');
const path = require('path');
describe(`#integration - remote sign key`, function () {
this.timeout(15000);
this.timeout(60000);
let server;
before(() => {
before(async () => {
const config = require('../../config').default.getProperties();
config.oldPublicKeyFile = path.resolve(
__dirname,
'../../config/public-key.json'
);
return TestServer.start(config).then((s) => {
server = s;
});
server = await TestServer.start(config);
});
after(async () => {
await TestServer.stop(server);
});
it('.well-known/browserid has keys', () => {
@ -37,7 +38,5 @@ describe(`#integration - remote sign key`, function () {
});
});
after(() => {
return TestServer.stop(server);
});
});

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

@ -37,7 +37,7 @@ const PRODUCT_NAME = 'All Done Pro';
[{ version: '' }, { version: 'V2' }].forEach((testOptions) => {
describe(`#integration${testOptions.version} - remote subscriptions:`, function () {
this.timeout(10000);
this.timeout(60000);
before(async () => {
config.subscriptions.stripeApiKey = null;
@ -48,7 +48,7 @@ const PRODUCT_NAME = 'All Done Pro';
});
describe('config.subscriptions.enabled = true and direct stripe access:', function () {
this.timeout(15000);
this.timeout(60000);
let client, server, tokens;
const mockStripeHelper = {};

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

@ -20,18 +20,19 @@ const {
[{version:""},{version:"V2"}].forEach((testOptions) => {
describe(`#integration${testOptions.version} - remote tokenCodes`, function () {
this.timeout(60000);
let server, client, email, code;
const password = 'pssssst';
this.timeout(10000);
before(() => {
before(async () => {
Container.set(PlaySubscriptions, {});
Container.set(AppStoreSubscriptions, {});
server = await TestServer.start(config);
});
return TestServer.start(config).then((s) => {
server = s;
});
after(async () => {
await TestServer.stop(server);
});
beforeEach(() => {
@ -241,9 +242,7 @@ describe(`#integration${testOptions.version} - remote tokenCodes`, function () {
);
});
after(() => {
return TestServer.stop(server);
});
});
});

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

@ -22,9 +22,10 @@ function fail() {
[{version:""},{version:"V2"}].forEach((testOptions) => {
describe(`#integration${testOptions.version} - remote token expiry`, function () {
this.timeout(15000);
this.timeout(60000);
let server, config;
before(() => {
before(async () => {
config = require('../../config').default.getProperties();
config.tokenLifetimes.passwordChangeToken = 1;
config.tokenLifetimes.sessionTokenWithoutDevice = 1;
@ -32,9 +33,11 @@ describe(`#integration${testOptions.version} - remote token expiry`, function ()
Container.set(PlaySubscriptions, {});
Container.set(AppStoreSubscriptions, {});
return TestServer.start(config).then((s) => {
server = s;
});
server = await TestServer.start(config);
});
after(async () => {
await TestServer.stop(server);
});
it('token expiry', () => {
@ -73,9 +76,7 @@ describe(`#integration${testOptions.version} - remote token expiry`, function ()
);
});
after(() => {
return TestServer.stop(server);
});
});
});

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

@ -21,6 +21,8 @@ const {
[{version:""},{version:"V2"}].forEach((testOptions) => {
describe(`#integration${testOptions.version} - remote totp`, function () {
this.timeout(60000);
let server, client, email, totpToken, authenticator;
const password = 'pssssst';
const metricsContext = {
@ -28,24 +30,24 @@ describe(`#integration${testOptions.version} - remote totp`, function () {
flowId: '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef',
};
this.timeout(10000);
otplib.authenticator.options = {
crypto: crypto,
encoding: 'hex',
window: 10,
};
before(() => {
before(async () => {
config.securityHistory.ipProfiling = {};
config.signinConfirmation.skipForNewAccounts.enabled = false;
Container.set(PlaySubscriptions, {});
Container.set(AppStoreSubscriptions, {});
return TestServer.start(config).then((s) => {
server = s;
});
server = await TestServer.start(config);
});
after(async () => {
await TestServer.stop(server);
});
function verifyTOTP(client) {
@ -78,7 +80,7 @@ describe(`#integration${testOptions.version} - remote totp`, function () {
emailData.headers['x-template-name'],
'postAddTwoStepAuthentication'
);
})
});
}
beforeEach(() => {
@ -192,7 +194,7 @@ describe(`#integration${testOptions.version} - remote totp`, function () {
server.mailbox,
{
...testOptions,
keys: true
keys: true,
}
);
})
@ -257,7 +259,7 @@ describe(`#integration${testOptions.version} - remote totp`, function () {
server.mailbox,
{
...testOptions,
keys: true
keys: true,
}
);
})
@ -271,7 +273,10 @@ describe(`#integration${testOptions.version} - remote totp`, function () {
});
it('should request `totp-2fa` on login if user has verified totp token', () => {
return Client.login(config.publicUrl, email, password, { ...testOptions, keys:true }).then((response) => {
return Client.login(config.publicUrl, email, password, {
...testOptions,
keys: true,
}).then((response) => {
assert.equal(
response.verificationMethod,
'totp-2fa',
@ -289,7 +294,12 @@ describe(`#integration${testOptions.version} - remote totp`, function () {
return client
.deleteTotpToken()
.then(() => client.createTotpToken())
.then(() => Client.login(config.publicUrl, email, password, { ...testOptions, keys:true }))
.then(() =>
Client.login(config.publicUrl, email, password, {
...testOptions,
keys: true,
})
)
.then((response) => {
assert.notEqual(
response.verificationMethod,
@ -305,7 +315,10 @@ describe(`#integration${testOptions.version} - remote totp`, function () {
});
it('should not bypass `totp-2fa` by resending sign-in confirmation code', () => {
return Client.login(config.publicUrl, email, password, {...testOptions, keys:true}).then((response) => {
return Client.login(config.publicUrl, email, password, {
...testOptions,
keys: true,
}).then((response) => {
client = response;
assert.equal(
response.verificationMethod,
@ -364,21 +377,9 @@ describe(`#integration${testOptions.version} - remote totp`, function () {
});
it('should not bypass `totp-2fa` by when using session reauth', () => {
return Client.login(config.publicUrl, email, password, testOptions).then((response) => {
client = response;
assert.equal(
response.verificationMethod,
'totp-2fa',
'verification method set'
);
assert.equal(
response.verificationReason,
'login',
'verification reason set'
);
// Lets attempt to sign-in reusing session reauth
return client.reauth().then((response) => {
return Client.login(config.publicUrl, email, password, testOptions).then(
(response) => {
client = response;
assert.equal(
response.verificationMethod,
'totp-2fa',
@ -389,34 +390,43 @@ describe(`#integration${testOptions.version} - remote totp`, function () {
'login',
'verification reason set'
);
});
});
// Lets attempt to sign-in reusing session reauth
return client.reauth().then((response) => {
assert.equal(
response.verificationMethod,
'totp-2fa',
'verification method set'
);
assert.equal(
response.verificationReason,
'login',
'verification reason set'
);
});
}
);
});
it('should not create verified session after account reset with totp', async () => {
const newPassword = 'anotherPassword';
const client = await Client.login(config.publicUrl, email, password, { ...testOptions, keys:true });
const client = await Client.login(config.publicUrl, email, password, {
...testOptions,
keys: true,
});
assert.equal(
client.verificationMethod,
'totp-2fa',
'verification method set'
);
assert.equal(
client.verificationReason,
'login',
'verification reason set'
);
assert.equal(client.verificationReason, 'login', 'verification reason set');
await client.forgotPassword();
const code = await server.mailbox.waitForCode(email);
await client.verifyPasswordResetCode(code);
const res = await client.resetPassword(newPassword, {}, { keys: true })
const res = await client.resetPassword(newPassword, {}, { keys: true });
assert.equal(
res.verificationMethod,
'totp-2fa',
'verificationMethod set'
);
assert.equal(res.verificationMethod, 'totp-2fa', 'verificationMethod set');
assert.equal(res.verificationReason, 'login', 'verificationMethod set');
assert.equal(res.verified, false);
assert.ok(res.keyFetchToken);
@ -515,9 +525,7 @@ describe(`#integration${testOptions.version} - remote totp`, function () {
});
});
after(() => {
return TestServer.stop(server);
});
});
});

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

@ -31,61 +31,56 @@ const {
AppStoreSubscriptions,
} = require('../../lib/payments/iap/apple-app-store/subscriptions');
let client, db, server;
[{ version: '' }, { version: 'V2' }].forEach((testOptions) => {
describe(`#integration${testOptions.version} - remote verifier upgrade`, function () {
this.timeout(60000);
[{version:""},{version:"V2"}].forEach((testOptions) => {
let client, db, server;
describe(`#integration${testOptions.version} - remote verifier upgrade`, function () {
this.timeout(30000);
before(async () => {
config.verifierVersion = 0;
config.securityHistory.ipProfiling.allowedRecency = 0;
before(async () => {
config.verifierVersion = 0;
config.securityHistory.ipProfiling.allowedRecency = 0;
Container.set(PlaySubscriptions, {});
Container.set(AppStoreSubscriptions, {});
Container.set(PlaySubscriptions, {});
Container.set(AppStoreSubscriptions, {});
server = await TestServer.start(config);
db = await DB.connect(config);
});
it('upgrading verifierVersion upgrades the account on password change', async () => {
const email = `${Math.random()}@example.com`;
const password = 'ok';
client = await Client.create(config.publicUrl, email, password, {
...testOptions,
preVerified: true,
keys: true,
server = await TestServer.start(config);
db = await DB.connect(config);
});
let account = await db.account(client.uid);
assert.equal(account.verifierVersion, 0, 'wrong version');
await TestServer.stop(server);
config.verifierVersion = 1;
server = await TestServer.start(config);
client = await Client.login(
config.publicUrl,
email,
password,
testOptions
);
await client.changePassword(password);
account = await db.account(client.uid);
assert.equal(account.verifierVersion, 1, 'wrong upgrade version');
});
after(async () => {
try {
await db.close();
after(async () => {
await TestServer.stop(server);
} catch (e) {}
await db.close();
});
it('upgrading verifierVersion upgrades the account on password change', async () => {
const email = `${Math.random()}@example.com`;
const password = 'ok';
client = await Client.create(config.publicUrl, email, password, {
...testOptions,
preVerified: true,
keys: true,
});
let account = await db.account(client.uid);
assert.equal(account.verifierVersion, 0, 'wrong version');
await TestServer.stop(server);
config.verifierVersion = 1;
server = await TestServer.start(config);
client = await Client.login(
config.publicUrl,
email,
password,
testOptions
);
await client.changePassword(password);
account = await db.account(client.uid);
assert.equal(account.verifierVersion, 1, 'wrong upgrade version');
});
});
});
});

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

@ -44,9 +44,11 @@ function createRandomEmailAddr(template) {
return `${Math.random() + template}`;
}
describe('#integration - scripts/check-users:', async function () {
this.timeout(30000);
describe('#integration - scripts/check-users:', function () {
this.timeout(60000);
let server, db, validClient, invalidClient, filename;
before(async () => {
server = await TestServer.start(config);
db = await DB.connect(config);

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

@ -15,8 +15,6 @@ const createProfileHelper = require('./profile_helper');
const { CapabilityService } = require('../lib/payments/capability');
const { AppConfig } = require('../lib/types');
let currentServer;
/* eslint-disable no-console */
function TestServer(config, printLogs, options = {}) {
Container.set(AppConfig, config);
@ -57,60 +55,42 @@ function TestServer(config, printLogs, options = {}) {
}
TestServer.start = async function (config, printLogs, options) {
await TestServer.stop();
currentServer = new TestServer(config, printLogs, options);
return currentServer.start().then(() => currentServer);
const server = new TestServer(config, printLogs, options);
await server.start();
return server;
};
TestServer.prototype.start = function () {
TestServer.prototype.start = async function () {
const { authServerMockDependencies = {} } = this.options;
const createAuthServer = proxyquire(
'../bin/key_server',
authServerMockDependencies
);
const promises = [
createAuthServer(this.config),
createMailHelper(this.printLogs),
];
this.server = await createAuthServer(this.config);
this.mail = await createMailHelper(this.printLogs);
if (this.config.profileServer.url && !this.profileServer) {
promises.push(createProfileHelper());
if (this.config.profileServer.url) {
this.profileServer = await createProfileHelper();
}
return Promise.all(promises).then(([auth, mail, profileServer]) => {
this.server = auth;
this.mail = mail;
this.profileServer = profileServer;
});
};
TestServer.stop = async function (maybeServer) {
if (maybeServer) {
await maybeServer.stop();
maybeServer = undefined;
TestServer.stop = async function (server) {
if (!server) {
throw new Error('Server must be provided');
}
if (currentServer) {
await currentServer.stop();
currentServer = undefined;
}
return Promise.resolve();
await server.stop();
};
TestServer.prototype.stop = async function () {
currentServer = undefined;
if (this.server) {
const doomed = [this.server.close(), this.mail.close()];
if (this.profileServer) {
doomed.push(this.profileServer.close());
}
return Promise.all(doomed);
} else {
return Promise.resolve();
await this.server.close();
}
if (this.mail) {
await this.mail.close();
}
if (this.profileServer) {
await this.profileServer.close();
}
};