2012-09-25 00:45:49 +04:00
|
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
|
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
|
|
|
|
"use strict";
|
|
|
|
|
2018-02-23 22:50:01 +03:00
|
|
|
var EXPORTED_SYMBOLS = [
|
2012-09-25 00:45:49 +04:00
|
|
|
"encryptPayload",
|
2013-12-20 08:57:26 +04:00
|
|
|
"makeIdentityConfig",
|
2016-01-21 05:12:22 +03:00
|
|
|
"makeFxAccountsInternalMock",
|
2013-12-20 08:57:26 +04:00
|
|
|
"configureFxAccountIdentity",
|
2013-12-20 08:57:26 +04:00
|
|
|
"configureIdentity",
|
2012-09-25 00:45:49 +04:00
|
|
|
"SyncTestingInfrastructure",
|
|
|
|
"waitForZeroTimer",
|
2016-11-10 06:34:38 +03:00
|
|
|
"promiseZeroTimer",
|
|
|
|
"promiseNamedTimer",
|
2015-07-21 04:23:12 +03:00
|
|
|
"MockFxaStorageManager",
|
|
|
|
"AccountState", // from a module import
|
2015-09-16 04:18:04 +03:00
|
|
|
"sumHistogram",
|
2017-07-27 04:59:24 +03:00
|
|
|
"getLoginTelemetryScalar",
|
2017-12-21 02:29:21 +03:00
|
|
|
"syncTestLogging",
|
2012-09-25 00:45:49 +04:00
|
|
|
];
|
|
|
|
|
2018-01-30 02:20:18 +03:00
|
|
|
ChromeUtils.import("resource://services-sync/status.js");
|
|
|
|
ChromeUtils.import("resource://services-common/utils.js");
|
|
|
|
ChromeUtils.import("resource://services-crypto/utils.js");
|
|
|
|
ChromeUtils.import("resource://services-sync/util.js");
|
|
|
|
ChromeUtils.import("resource://services-sync/browserid_identity.js");
|
2018-02-06 06:05:45 +03:00
|
|
|
ChromeUtils.import("resource://testing-common/Assert.jsm");
|
2018-01-30 02:20:18 +03:00
|
|
|
ChromeUtils.import("resource://testing-common/services/common/logging.js");
|
|
|
|
ChromeUtils.import("resource://testing-common/services/sync/fakeservices.js");
|
|
|
|
ChromeUtils.import("resource://gre/modules/FxAccounts.jsm");
|
|
|
|
ChromeUtils.import("resource://gre/modules/FxAccountsClient.jsm");
|
|
|
|
ChromeUtils.import("resource://gre/modules/FxAccountsCommon.js");
|
|
|
|
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
2012-09-25 00:45:49 +04:00
|
|
|
|
2015-07-21 04:23:12 +03:00
|
|
|
// and grab non-exported stuff via a backstage pass.
|
2018-01-30 02:20:18 +03:00
|
|
|
const {AccountState} = ChromeUtils.import("resource://gre/modules/FxAccounts.jsm", {});
|
2015-07-21 04:23:12 +03:00
|
|
|
|
|
|
|
// A mock "storage manager" for FxAccounts that doesn't actually write anywhere.
|
|
|
|
function MockFxaStorageManager() {
|
|
|
|
}
|
|
|
|
|
|
|
|
MockFxaStorageManager.prototype = {
|
|
|
|
promiseInitialized: Promise.resolve(),
|
|
|
|
|
|
|
|
initialize(accountData) {
|
|
|
|
this.accountData = accountData;
|
|
|
|
},
|
|
|
|
|
|
|
|
finalize() {
|
|
|
|
return Promise.resolve();
|
|
|
|
},
|
|
|
|
|
|
|
|
getAccountData() {
|
|
|
|
return Promise.resolve(this.accountData);
|
|
|
|
},
|
|
|
|
|
|
|
|
updateAccountData(updatedFields) {
|
2016-08-09 05:40:23 +03:00
|
|
|
for (let [name, value] of Object.entries(updatedFields)) {
|
2015-07-21 04:23:12 +03:00
|
|
|
if (value == null) {
|
|
|
|
delete this.accountData[name];
|
|
|
|
} else {
|
|
|
|
this.accountData[name] = value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return Promise.resolve();
|
|
|
|
},
|
|
|
|
|
|
|
|
deleteAccountData() {
|
|
|
|
this.accountData = null;
|
|
|
|
return Promise.resolve();
|
2018-08-31 08:59:17 +03:00
|
|
|
},
|
2017-10-15 21:50:30 +03:00
|
|
|
};
|
2015-07-21 04:23:12 +03:00
|
|
|
|
2012-09-25 00:45:49 +04:00
|
|
|
/**
|
|
|
|
* First wait >100ms (nsITimers can take up to that much time to fire, so
|
|
|
|
* we can account for the timer in delayedAutoconnect) and then two event
|
2017-08-25 08:41:44 +03:00
|
|
|
* loop ticks (to account for the CommonUtils.nextTick() in autoConnect).
|
2012-09-25 00:45:49 +04:00
|
|
|
*/
|
2018-02-23 22:50:01 +03:00
|
|
|
function waitForZeroTimer(callback) {
|
2012-09-25 00:45:49 +04:00
|
|
|
let ticks = 2;
|
|
|
|
function wait() {
|
|
|
|
if (ticks) {
|
|
|
|
ticks -= 1;
|
|
|
|
CommonUtils.nextTick(wait);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
callback();
|
|
|
|
}
|
|
|
|
CommonUtils.namedTimer(wait, 150, {}, "timer");
|
2018-02-23 22:50:01 +03:00
|
|
|
}
|
2012-09-25 00:45:49 +04:00
|
|
|
|
2018-02-23 22:50:01 +03:00
|
|
|
var promiseZeroTimer = function() {
|
2016-11-10 06:34:38 +03:00
|
|
|
return new Promise(resolve => {
|
|
|
|
waitForZeroTimer(resolve);
|
|
|
|
});
|
2017-10-15 21:50:30 +03:00
|
|
|
};
|
2016-11-10 06:34:38 +03:00
|
|
|
|
2018-02-23 22:50:01 +03:00
|
|
|
var promiseNamedTimer = function(wait, thisObj, name) {
|
2016-11-10 06:34:38 +03:00
|
|
|
return new Promise(resolve => {
|
2017-08-25 08:41:44 +03:00
|
|
|
CommonUtils.namedTimer(resolve, wait, thisObj, name);
|
2016-11-10 06:34:38 +03:00
|
|
|
});
|
2017-10-15 21:50:30 +03:00
|
|
|
};
|
2016-11-10 06:34:38 +03:00
|
|
|
|
2013-12-20 08:57:26 +04:00
|
|
|
// Return an identity configuration suitable for testing with our identity
|
|
|
|
// providers. |overrides| can specify overrides for any default values.
|
2016-11-21 04:22:24 +03:00
|
|
|
// |server| is optional, but if specified, will be used to form the cluster
|
|
|
|
// URL for the FxA identity.
|
2018-02-23 22:50:01 +03:00
|
|
|
var makeIdentityConfig = function(overrides) {
|
2013-12-20 08:57:26 +04:00
|
|
|
// first setup the defaults.
|
|
|
|
let result = {
|
|
|
|
// Username used in both fxaccount and sync identity configs.
|
|
|
|
username: "foo",
|
|
|
|
// fxaccount specific credentials.
|
|
|
|
fxaccount: {
|
|
|
|
user: {
|
2017-01-17 18:48:17 +03:00
|
|
|
assertion: "assertion",
|
2018-01-05 23:56:00 +03:00
|
|
|
email: "foo",
|
|
|
|
kSync: "a".repeat(128),
|
|
|
|
kXCS: "a".repeat(32),
|
|
|
|
kExtSync: "a".repeat(128),
|
|
|
|
kExtKbHash: "a".repeat(32),
|
2017-01-17 18:48:17 +03:00
|
|
|
sessionToken: "sessionToken",
|
2016-07-11 20:37:23 +03:00
|
|
|
uid: "a".repeat(32),
|
2014-01-14 20:00:36 +04:00
|
|
|
verified: true,
|
2013-12-20 08:57:26 +04:00
|
|
|
},
|
|
|
|
token: {
|
2015-07-16 06:19:10 +03:00
|
|
|
endpoint: null,
|
2013-12-20 08:57:26 +04:00
|
|
|
duration: 300,
|
|
|
|
id: "id",
|
|
|
|
key: "key",
|
2016-08-09 19:28:35 +03:00
|
|
|
hashed_fxa_uid: "f".repeat(32), // used during telemetry validation
|
2013-12-20 08:57:26 +04:00
|
|
|
// uid will be set to the username.
|
2018-08-31 08:59:17 +03:00
|
|
|
},
|
|
|
|
},
|
2013-12-20 08:57:26 +04:00
|
|
|
};
|
2013-12-20 08:57:26 +04:00
|
|
|
|
2013-12-20 08:57:26 +04:00
|
|
|
// Now handle any specified overrides.
|
|
|
|
if (overrides) {
|
|
|
|
if (overrides.username) {
|
|
|
|
result.username = overrides.username;
|
|
|
|
}
|
|
|
|
if (overrides.fxaccount) {
|
|
|
|
// TODO: allow just some attributes to be specified
|
|
|
|
result.fxaccount = overrides.fxaccount;
|
|
|
|
}
|
2013-12-20 08:57:26 +04:00
|
|
|
}
|
|
|
|
return result;
|
2017-10-15 21:50:30 +03:00
|
|
|
};
|
2013-12-20 08:57:26 +04:00
|
|
|
|
2018-02-23 22:50:01 +03:00
|
|
|
var makeFxAccountsInternalMock = function(config) {
|
2016-01-21 05:12:22 +03:00
|
|
|
return {
|
2015-07-21 04:23:12 +03:00
|
|
|
newAccountState(credentials) {
|
|
|
|
// We only expect this to be called with null indicating the (mock)
|
|
|
|
// storage should be read.
|
|
|
|
if (credentials) {
|
|
|
|
throw new Error("Not expecting to have credentials passed");
|
|
|
|
}
|
|
|
|
let storageManager = new MockFxaStorageManager();
|
|
|
|
storageManager.initialize(config.fxaccount.user);
|
2015-07-27 01:58:53 +03:00
|
|
|
let accountState = new AccountState(storageManager);
|
2015-07-21 04:23:12 +03:00
|
|
|
return accountState;
|
2015-07-27 01:58:53 +03:00
|
|
|
},
|
2015-07-29 09:06:29 +03:00
|
|
|
_getAssertion(audience) {
|
2018-02-06 06:05:45 +03:00
|
|
|
return Promise.resolve(config.fxaccount.user.assertion);
|
2015-07-27 01:58:53 +03:00
|
|
|
},
|
2014-03-03 03:20:56 +04:00
|
|
|
};
|
2016-01-21 05:12:22 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
// Configure an instance of an FxAccount identity provider with the specified
|
|
|
|
// config (or the default config if not specified).
|
2018-02-23 22:50:01 +03:00
|
|
|
var configureFxAccountIdentity = function(authService,
|
|
|
|
config = makeIdentityConfig(),
|
|
|
|
fxaInternal = makeFxAccountsInternalMock(config)) {
|
2016-01-21 05:12:22 +03:00
|
|
|
// until we get better test infrastructure for bid_identity, we set the
|
|
|
|
// signedin user's "email" to the username, simply as many tests rely on this.
|
|
|
|
config.fxaccount.user.email = config.username;
|
|
|
|
|
|
|
|
let fxa = new FxAccounts(fxaInternal);
|
2014-03-03 03:20:56 +04:00
|
|
|
|
2015-09-02 17:44:00 +03:00
|
|
|
let MockFxAccountsClient = function() {
|
|
|
|
FxAccountsClient.apply(this);
|
|
|
|
};
|
|
|
|
MockFxAccountsClient.prototype = {
|
|
|
|
__proto__: FxAccountsClient.prototype,
|
|
|
|
accountStatus() {
|
|
|
|
return Promise.resolve(true);
|
2018-08-31 08:59:17 +03:00
|
|
|
},
|
2015-09-02 17:44:00 +03:00
|
|
|
};
|
|
|
|
let mockFxAClient = new MockFxAccountsClient();
|
|
|
|
fxa.internal._fxAccountsClient = mockFxAClient;
|
|
|
|
|
2013-12-20 08:57:26 +04:00
|
|
|
let mockTSC = { // TokenServerClient
|
2017-12-07 22:45:15 +03:00
|
|
|
async getTokenFromBrowserIDAssertion(uri, assertion) {
|
2018-02-06 06:05:45 +03:00
|
|
|
Assert.equal(uri, Services.prefs.getStringPref("identity.sync.tokenserver.uri"));
|
|
|
|
Assert.equal(assertion, config.fxaccount.user.assertion);
|
2013-12-20 08:57:26 +04:00
|
|
|
config.fxaccount.token.uid = config.username;
|
2017-12-07 22:45:15 +03:00
|
|
|
return config.fxaccount.token;
|
2013-12-20 08:57:26 +04:00
|
|
|
},
|
|
|
|
};
|
|
|
|
authService._fxaService = fxa;
|
|
|
|
authService._tokenServerClient = mockTSC;
|
|
|
|
// Set the "account" of the browserId manager to be the "email" of the
|
|
|
|
// logged in user of the mockFXA service.
|
2015-07-21 04:23:12 +03:00
|
|
|
authService._signedInUser = config.fxaccount.user;
|
2013-12-20 08:57:26 +04:00
|
|
|
authService._account = config.fxaccount.user.email;
|
2017-10-15 21:50:30 +03:00
|
|
|
};
|
2013-12-20 08:57:26 +04:00
|
|
|
|
2018-02-23 22:50:01 +03:00
|
|
|
var configureIdentity = async function(identityOverrides, server) {
|
2016-11-21 04:22:24 +03:00
|
|
|
let config = makeIdentityConfig(identityOverrides, server);
|
2013-12-20 08:57:26 +04:00
|
|
|
let ns = {};
|
2018-01-30 02:20:18 +03:00
|
|
|
ChromeUtils.import("resource://services-sync/service.js", ns);
|
2013-12-20 08:57:26 +04:00
|
|
|
|
2017-01-13 23:02:21 +03:00
|
|
|
// If a server was specified, ensure FxA has a correct cluster URL available.
|
|
|
|
if (server && !config.fxaccount.token.endpoint) {
|
|
|
|
let ep = server.baseURI;
|
|
|
|
if (!ep.endsWith("/")) {
|
|
|
|
ep += "/";
|
2017-01-26 22:16:12 +03:00
|
|
|
}
|
2017-01-13 23:02:21 +03:00
|
|
|
ep += "1.1/" + config.username + "/";
|
|
|
|
config.fxaccount.token.endpoint = ep;
|
2017-01-26 22:16:12 +03:00
|
|
|
}
|
2017-01-13 23:02:21 +03:00
|
|
|
|
|
|
|
configureFxAccountIdentity(ns.Service.identity, config);
|
2018-02-06 06:05:45 +03:00
|
|
|
// because we didn't send any FxA LOGIN notifications we must set the username.
|
|
|
|
ns.Service.identity.username = config.username;
|
|
|
|
// many of these tests assume all the auth stuff is setup and don't hit
|
|
|
|
// a path which causes that auth to magically happen - so do it now.
|
|
|
|
await ns.Service.identity._ensureValidToken();
|
|
|
|
|
2017-01-13 23:02:21 +03:00
|
|
|
// and cheat to avoid requiring each test do an explicit login - give it
|
|
|
|
// a cluster URL.
|
|
|
|
if (config.fxaccount.token.endpoint) {
|
|
|
|
ns.Service.clusterURL = config.fxaccount.token.endpoint;
|
2016-11-22 07:03:47 +03:00
|
|
|
}
|
2017-10-15 21:50:30 +03:00
|
|
|
};
|
2013-12-20 08:57:26 +04:00
|
|
|
|
2017-12-21 02:29:21 +03:00
|
|
|
function syncTestLogging(level = "Trace") {
|
|
|
|
let logStats = initTestLogging(level);
|
|
|
|
Services.prefs.setStringPref("services.sync.log.logger", level);
|
|
|
|
Services.prefs.setStringPref("services.sync.log.logger.engine", "");
|
|
|
|
return logStats;
|
|
|
|
}
|
|
|
|
|
2018-02-23 22:50:01 +03:00
|
|
|
var SyncTestingInfrastructure = async function(server, username) {
|
2012-09-25 00:45:49 +04:00
|
|
|
let ns = {};
|
2018-01-30 02:20:18 +03:00
|
|
|
ChromeUtils.import("resource://services-sync/service.js", ns);
|
2012-09-25 00:45:49 +04:00
|
|
|
|
2017-01-13 23:02:21 +03:00
|
|
|
let config = makeIdentityConfig({ username });
|
2016-11-22 07:03:47 +03:00
|
|
|
await configureIdentity(config, server);
|
|
|
|
return {
|
2017-12-21 02:29:21 +03:00
|
|
|
logStats: syncTestLogging(),
|
2016-11-22 07:03:47 +03:00
|
|
|
fakeFilesystem: new FakeFilesystemService({}),
|
|
|
|
fakeGUIDService: new FakeGUIDService(),
|
|
|
|
fakeCryptoService: new FakeCryptoService(),
|
2017-10-15 21:50:30 +03:00
|
|
|
};
|
|
|
|
};
|
2012-09-25 00:45:49 +04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Turn WBO cleartext into fake "encrypted" payload as it goes over the wire.
|
|
|
|
*/
|
2018-02-23 22:50:01 +03:00
|
|
|
function encryptPayload(cleartext) {
|
2012-09-25 00:45:49 +04:00
|
|
|
if (typeof cleartext == "object") {
|
|
|
|
cleartext = JSON.stringify(cleartext);
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
ciphertext: cleartext, // ciphertext == cleartext with fake crypto
|
|
|
|
IV: "irrelevant",
|
|
|
|
hmac: fakeSHA256HMAC(cleartext, CryptoUtils.makeHMACKey("")),
|
|
|
|
};
|
2018-02-23 22:50:01 +03:00
|
|
|
}
|
2012-09-25 00:45:49 +04:00
|
|
|
|
2018-02-23 22:50:01 +03:00
|
|
|
var sumHistogram = function(name, options = {}) {
|
2015-09-16 04:18:04 +03:00
|
|
|
let histogram = options.key ? Services.telemetry.getKeyedHistogramById(name) :
|
|
|
|
Services.telemetry.getHistogramById(name);
|
|
|
|
let snapshot = histogram.snapshot(options.key);
|
|
|
|
let sum = -Infinity;
|
|
|
|
if (snapshot) {
|
|
|
|
sum = snapshot.sum;
|
|
|
|
}
|
|
|
|
histogram.clear();
|
|
|
|
return sum;
|
2017-10-15 21:50:30 +03:00
|
|
|
};
|
2017-07-27 04:59:24 +03:00
|
|
|
|
2018-02-23 22:50:01 +03:00
|
|
|
var getLoginTelemetryScalar = function() {
|
2017-07-27 04:59:24 +03:00
|
|
|
let dataset = Services.telemetry.DATASET_RELEASE_CHANNEL_OPTOUT;
|
|
|
|
let snapshot = Services.telemetry.snapshotKeyedScalars(dataset, true);
|
|
|
|
return snapshot.parent ? snapshot.parent["services.sync.sync_login_state_transitions"] : {};
|
2017-10-15 21:50:30 +03:00
|
|
|
};
|