зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1548404 - Update UITour to reflect the decoupling of FxA and Sync. r=MattN,andreio,rfkelly
Differential Revision: https://phabricator.services.mozilla.com/D51976 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
f9f5d6809a
Коммит
ab41353fa5
|
@ -387,6 +387,8 @@ if (typeof Mozilla == "undefined") {
|
||||||
* <li>{@link Mozilla.UITour.Configuration.Search|selectedSearchEngine}
|
* <li>{@link Mozilla.UITour.Configuration.Search|selectedSearchEngine}
|
||||||
* - DEPRECATED, use 'search'</li>
|
* - DEPRECATED, use 'search'</li>
|
||||||
* <li>{@link Mozilla.UITour.Configuration.Sync|sync}</li>
|
* <li>{@link Mozilla.UITour.Configuration.Sync|sync}</li>
|
||||||
|
* - DEPRECATED, use 'fxa'</li>
|
||||||
|
* <li>{@link Mozilla.UITour.Configuration.FxA|fxa}</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -448,6 +450,65 @@ if (typeof Mozilla == "undefined") {
|
||||||
* @since 50
|
* @since 50
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FxA status, including whether FxA is connected, device counts, services
|
||||||
|
* connected to this browser and services externally connected to the account.
|
||||||
|
* @typedef {Object} Mozilla.UITour.Configuration.FxA
|
||||||
|
* @property {Boolean} setup - Whether FxA is setup on this device. If false,
|
||||||
|
* no other properties will exist.
|
||||||
|
* @property {Number} [numOtherDevices] - Number of devices connected to this
|
||||||
|
* account, not counting this device.
|
||||||
|
* @property {Object.<String, Number>} [numDevicesByType] - A count of devices
|
||||||
|
* connected to the account by device 'type'. Valid values for type are
|
||||||
|
* defined by the FxA server but roughly correspond to form-factor with
|
||||||
|
* values like 'desktop', 'mobile', 'vr', etc.
|
||||||
|
* @property {Mozilla.UITour.Configuration.AccountServices} [accountServices] -
|
||||||
|
* Information about services attached to this account. These services
|
||||||
|
* may be enabled on devices or applications external to this
|
||||||
|
* browser and should not be confused with devices. For example, if the user
|
||||||
|
* has enabled Monitor or Lockwise on one or more devices - including on
|
||||||
|
* this device - that service will have a single entry here.
|
||||||
|
* @property {Mozilla.UITour.Configuration.BrowserServices} [browserServices] -
|
||||||
|
* Information about account services attached to this browser, and with
|
||||||
|
* special support implemented by this browser. You should not expect
|
||||||
|
* every accountService connected in this browser to get a special entry
|
||||||
|
* here. Indeed, what services, and in what circumstances they may appear
|
||||||
|
* here in the future is largely TBD.
|
||||||
|
* @since 71
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Information about clients attached to the account.
|
||||||
|
* An object. The key is a string ID of the attached service. A list of attached
|
||||||
|
* service IDs can be found at
|
||||||
|
* {@link https://docs.telemetry.mozilla.org/datasets/fxa_metrics/attribution.html#service-attribution|
|
||||||
|
* on our telemetry documentation site}
|
||||||
|
* The value is a {@link Mozilla.UITour.Configuration.AccountService}
|
||||||
|
* @typedef {Object.<string, Mozilla.UITour.Configuration.AccountService>} Mozilla.UITour.Configuration.AccountService
|
||||||
|
* @since 71
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Information about an account service
|
||||||
|
* @typedef {Object} Mozilla.UITour.Configuration.AccountService
|
||||||
|
* @property {String} id - The service ID. A list of attached
|
||||||
|
* service IDs can be found at
|
||||||
|
* {@link https://docs.telemetry.mozilla.org/datasets/fxa_metrics/attribution.html#service-attribution|
|
||||||
|
* on our telemetry documentation site}
|
||||||
|
* @property {Number} lastAccessedWeeksAgo - How many weeks ago the service
|
||||||
|
* was accessed by this account.
|
||||||
|
* @since 71
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Information about a services attached to the browser. All properties are
|
||||||
|
* optional and only exist if the service is enabled.
|
||||||
|
*
|
||||||
|
* @typedef {Object} Mozilla.UITour.Configuration.BrowserServices
|
||||||
|
* @property {Mozilla.UITour.Configuration.Sync} sync - If sync is configured
|
||||||
|
* @since 71
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Array of UI {@link Mozilla.UITour.Target|Targets} currently available to be annotated.
|
* Array of UI {@link Mozilla.UITour.Target|Targets} currently available to be annotated.
|
||||||
* @typedef {Mozilla.UITour.Target[]} Mozilla.UITour.Configuration.AvailableTargets
|
* @typedef {Mozilla.UITour.Target[]} Mozilla.UITour.Configuration.AvailableTargets
|
||||||
|
|
|
@ -24,6 +24,11 @@ ChromeUtils.defineModuleGetter(
|
||||||
"CustomizableUI",
|
"CustomizableUI",
|
||||||
"resource:///modules/CustomizableUI.jsm"
|
"resource:///modules/CustomizableUI.jsm"
|
||||||
);
|
);
|
||||||
|
ChromeUtils.defineModuleGetter(
|
||||||
|
this,
|
||||||
|
"fxAccounts",
|
||||||
|
"resource://gre/modules/FxAccounts.jsm"
|
||||||
|
);
|
||||||
ChromeUtils.defineModuleGetter(
|
ChromeUtils.defineModuleGetter(
|
||||||
this,
|
this,
|
||||||
"FxAccounts",
|
"FxAccounts",
|
||||||
|
@ -1669,6 +1674,13 @@ var UITour = {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
|
case "fxa":
|
||||||
|
this.getFxA(aMessageManager, aCallbackID);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// NOTE: 'sync' is deprecated and should be removed in Firefox 73 (because
|
||||||
|
// by then, all consumers will have upgraded to use 'fxa' in that version
|
||||||
|
// and later.)
|
||||||
case "sync":
|
case "sync":
|
||||||
this.sendPageCallback(aMessageManager, aCallbackID, {
|
this.sendPageCallback(aMessageManager, aCallbackID, {
|
||||||
setup: Services.prefs.prefHasUserValue("services.sync.username"),
|
setup: Services.prefs.prefHasUserValue("services.sync.username"),
|
||||||
|
@ -1721,6 +1733,77 @@ var UITour = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getFxA(aMessageManager, aCallbackID) {
|
||||||
|
(async () => {
|
||||||
|
let setup = !!(await fxAccounts.getSignedInUser());
|
||||||
|
let result = { setup };
|
||||||
|
if (!setup) {
|
||||||
|
this.sendPageCallback(aMessageManager, aCallbackID, result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// We are signed in so need to build a richer result.
|
||||||
|
let devices = fxAccounts.device.recentDeviceList;
|
||||||
|
// A recent device list is fine, but if we don't even have that we should
|
||||||
|
// wait for it to be fetched.
|
||||||
|
if (!devices) {
|
||||||
|
await fxAccounts.device.refreshDeviceList();
|
||||||
|
devices = fxAccounts.device.recentDeviceList;
|
||||||
|
}
|
||||||
|
if (devices) {
|
||||||
|
// A falsey `devices` should be impossible, so we omit `devices` from
|
||||||
|
// the result object so the consuming page can try to differentiate
|
||||||
|
// between "no additional devices" and "something's wrong"
|
||||||
|
result.numOtherDevices = Math.max(0, devices.length - 1);
|
||||||
|
result.numDevicesByType = devices
|
||||||
|
.filter(d => !d.isCurrentDevice)
|
||||||
|
.reduce((accum, d) => {
|
||||||
|
let type = d.type || "unknown";
|
||||||
|
accum[type] = (accum[type] || 0) + 1;
|
||||||
|
return accum;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Each of the "browser services" - currently only "sync" is supported.
|
||||||
|
result.browserServices = {};
|
||||||
|
let hasSync = Services.prefs.prefHasUserValue("services.sync.username");
|
||||||
|
if (hasSync) {
|
||||||
|
result.browserServices.sync = {
|
||||||
|
// We always include 'setup' for b/w compatibility.
|
||||||
|
setup: true,
|
||||||
|
desktopDevices: Services.prefs.getIntPref(
|
||||||
|
"services.sync.clients.devices.desktop",
|
||||||
|
0
|
||||||
|
),
|
||||||
|
mobileDevices: Services.prefs.getIntPref(
|
||||||
|
"services.sync.clients.devices.mobile",
|
||||||
|
0
|
||||||
|
),
|
||||||
|
totalDevices: Services.prefs.getIntPref(
|
||||||
|
"services.sync.numClients",
|
||||||
|
0
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
// Each of the "account services", which we turn into a map keyed by ID.
|
||||||
|
let attachedClients = await fxAccounts.listAttachedOAuthClients();
|
||||||
|
result.accountServices = attachedClients
|
||||||
|
.filter(c => !!c.id)
|
||||||
|
.reduce((accum, c) => {
|
||||||
|
accum[c.id] = {
|
||||||
|
id: c.id,
|
||||||
|
lastAccessedWeeksAgo: c.lastAccessedDaysAgo
|
||||||
|
? Math.floor(c.lastAccessedDaysAgo / 7)
|
||||||
|
: null,
|
||||||
|
};
|
||||||
|
return accum;
|
||||||
|
}, {});
|
||||||
|
this.sendPageCallback(aMessageManager, aCallbackID, result);
|
||||||
|
})().catch(err => {
|
||||||
|
log.error(err);
|
||||||
|
this.sendPageCallback(aMessageManager, aCallbackID, {});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
getAppInfo(aMessageManager, aWindow, aCallbackID) {
|
getAppInfo(aMessageManager, aWindow, aCallbackID) {
|
||||||
(async () => {
|
(async () => {
|
||||||
let appinfo = { version: Services.appinfo.version };
|
let appinfo = { version: Services.appinfo.version };
|
||||||
|
|
|
@ -15,6 +15,7 @@ support-files =
|
||||||
skip-if = (verify && !debug && (os == 'linux'))
|
skip-if = (verify && !debug && (os == 'linux'))
|
||||||
[browser_fxa.js]
|
[browser_fxa.js]
|
||||||
skip-if = debug || asan # updateUI leaks
|
skip-if = debug || asan # updateUI leaks
|
||||||
|
[browser_fxa_config.js]
|
||||||
[browser_no_tabs.js]
|
[browser_no_tabs.js]
|
||||||
[browser_openPreferences.js]
|
[browser_openPreferences.js]
|
||||||
[browser_openSearchPanel.js]
|
[browser_openSearchPanel.js]
|
||||||
|
|
|
@ -0,0 +1,239 @@
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const { sinon } = ChromeUtils.import("resource://testing-common/Sinon.jsm");
|
||||||
|
|
||||||
|
var gTestTab;
|
||||||
|
var gContentAPI;
|
||||||
|
var gContentWindow;
|
||||||
|
|
||||||
|
add_task(setup_UITourTest);
|
||||||
|
|
||||||
|
add_UITour_task(async function test_no_user() {
|
||||||
|
const sandbox = sinon.createSandbox();
|
||||||
|
sandbox.stub(fxAccounts, "getSignedInUser").returns(null);
|
||||||
|
let result = await getConfigurationPromise("fxa");
|
||||||
|
Assert.deepEqual(result, { setup: false });
|
||||||
|
sandbox.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
add_UITour_task(async function test_no_sync_no_devices() {
|
||||||
|
const sandbox = sinon.createSandbox();
|
||||||
|
sandbox
|
||||||
|
.stub(fxAccounts, "getSignedInUser")
|
||||||
|
.returns({ email: "foo@example.com" });
|
||||||
|
sandbox.stub(fxAccounts.device, "recentDeviceList").get(() => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: "This Device",
|
||||||
|
isCurrentDevice: true,
|
||||||
|
type: "desktop",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
});
|
||||||
|
sandbox.stub(fxAccounts, "listAttachedOAuthClients").resolves([]);
|
||||||
|
|
||||||
|
let result = await getConfigurationPromise("fxa");
|
||||||
|
Assert.deepEqual(result, {
|
||||||
|
setup: true,
|
||||||
|
numOtherDevices: 0,
|
||||||
|
numDevicesByType: {},
|
||||||
|
accountServices: {},
|
||||||
|
browserServices: {},
|
||||||
|
});
|
||||||
|
sandbox.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
add_UITour_task(async function test_no_sync_many_devices() {
|
||||||
|
const sandbox = sinon.createSandbox();
|
||||||
|
sandbox
|
||||||
|
.stub(fxAccounts, "getSignedInUser")
|
||||||
|
.returns({ email: "foo@example.com" });
|
||||||
|
sandbox.stub(fxAccounts.device, "recentDeviceList").get(() => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: "This Device",
|
||||||
|
isCurrentDevice: true,
|
||||||
|
type: "desktop",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: "Other Device",
|
||||||
|
type: "mobile",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
name: "My phone",
|
||||||
|
type: "phone",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
name: "Who knows?",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
name: "Another desktop",
|
||||||
|
type: "desktop",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 6,
|
||||||
|
name: "Yet Another desktop",
|
||||||
|
type: "desktop",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
});
|
||||||
|
sandbox.stub(fxAccounts, "listAttachedOAuthClients").resolves([]);
|
||||||
|
|
||||||
|
let result = await getConfigurationPromise("fxa");
|
||||||
|
Assert.deepEqual(result, {
|
||||||
|
setup: true,
|
||||||
|
accountServices: {},
|
||||||
|
browserServices: {},
|
||||||
|
numOtherDevices: 5,
|
||||||
|
numDevicesByType: {
|
||||||
|
desktop: 2,
|
||||||
|
mobile: 1,
|
||||||
|
phone: 1,
|
||||||
|
unknown: 1,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
sandbox.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
add_UITour_task(async function test_no_sync_no_cached_devices() {
|
||||||
|
const sandbox = sinon.createSandbox();
|
||||||
|
sandbox
|
||||||
|
.stub(fxAccounts, "getSignedInUser")
|
||||||
|
.returns({ email: "foo@example.com" });
|
||||||
|
let devicesStub = sandbox.stub(fxAccounts.device, "recentDeviceList");
|
||||||
|
devicesStub.get(() => {
|
||||||
|
// Sinon doesn't seem to support second `getters` returning a different
|
||||||
|
// value, so replace the getter here.
|
||||||
|
devicesStub.get(() => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: "This Device",
|
||||||
|
isCurrentDevice: true,
|
||||||
|
type: "desktop",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: "Other Device",
|
||||||
|
type: "mobile",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
});
|
||||||
|
// and here we want to say "nothing is yet cached"
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
sandbox.stub(fxAccounts, "listAttachedOAuthClients").resolves([]);
|
||||||
|
let rdlStub = sandbox.stub(fxAccounts.device, "refreshDeviceList").resolves();
|
||||||
|
|
||||||
|
let result = await getConfigurationPromise("fxa");
|
||||||
|
Assert.deepEqual(result, {
|
||||||
|
setup: true,
|
||||||
|
accountServices: {},
|
||||||
|
browserServices: {},
|
||||||
|
numOtherDevices: 1,
|
||||||
|
numDevicesByType: {
|
||||||
|
mobile: 1,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
Assert.ok(rdlStub.called);
|
||||||
|
sandbox.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
add_UITour_task(async function test_account_clients() {
|
||||||
|
const sandbox = sinon.createSandbox();
|
||||||
|
sandbox
|
||||||
|
.stub(fxAccounts, "getSignedInUser")
|
||||||
|
.returns({ email: "foo@example.com" });
|
||||||
|
sandbox.stub(fxAccounts.device, "recentDeviceList").get(() => []);
|
||||||
|
sandbox.stub(fxAccounts, "listAttachedOAuthClients").resolves([
|
||||||
|
{
|
||||||
|
id: "802d56ef2a9af9fa",
|
||||||
|
lastAccessedDaysAgo: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "1f30e32975ae5112",
|
||||||
|
lastAccessedDaysAgo: 10,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: null,
|
||||||
|
name: "Some browser",
|
||||||
|
lastAccessedDaysAgo: 10,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "null-last-accessed",
|
||||||
|
lastAccessedDaysAgo: null,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
Assert.deepEqual(await getConfigurationPromise("fxa"), {
|
||||||
|
setup: true,
|
||||||
|
numOtherDevices: 0,
|
||||||
|
numDevicesByType: {},
|
||||||
|
accountServices: {
|
||||||
|
"802d56ef2a9af9fa": {
|
||||||
|
id: "802d56ef2a9af9fa",
|
||||||
|
lastAccessedWeeksAgo: 0,
|
||||||
|
},
|
||||||
|
"1f30e32975ae5112": {
|
||||||
|
id: "1f30e32975ae5112",
|
||||||
|
lastAccessedWeeksAgo: 1,
|
||||||
|
},
|
||||||
|
"null-last-accessed": {
|
||||||
|
id: "null-last-accessed",
|
||||||
|
lastAccessedWeeksAgo: null,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
browserServices: {},
|
||||||
|
});
|
||||||
|
sandbox.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
add_UITour_task(async function test_sync() {
|
||||||
|
const sandbox = sinon.createSandbox();
|
||||||
|
sandbox
|
||||||
|
.stub(fxAccounts, "getSignedInUser")
|
||||||
|
.returns({ email: "foo@example.com" });
|
||||||
|
sandbox.stub(fxAccounts.device, "recentDeviceList").get(() => []);
|
||||||
|
sandbox.stub(fxAccounts, "listAttachedOAuthClients").resolves([]);
|
||||||
|
Services.prefs.setCharPref("services.sync.username", "tests@mozilla.org");
|
||||||
|
Services.prefs.setIntPref("services.sync.clients.devices.desktop", 4);
|
||||||
|
Services.prefs.setIntPref("services.sync.clients.devices.mobile", 5);
|
||||||
|
Services.prefs.setIntPref("services.sync.numClients", 9);
|
||||||
|
|
||||||
|
Assert.deepEqual(await getConfigurationPromise("fxa"), {
|
||||||
|
setup: true,
|
||||||
|
numOtherDevices: 0,
|
||||||
|
numDevicesByType: {},
|
||||||
|
accountServices: {},
|
||||||
|
browserServices: {
|
||||||
|
sync: {
|
||||||
|
setup: true,
|
||||||
|
mobileDevices: 5,
|
||||||
|
desktopDevices: 4,
|
||||||
|
totalDevices: 9,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
Services.prefs.clearUserPref("services.sync.username");
|
||||||
|
Services.prefs.clearUserPref("services.sync.clients.devices.desktop");
|
||||||
|
Services.prefs.clearUserPref("services.sync.clients.devices.mobile");
|
||||||
|
Services.prefs.clearUserPref("services.sync.numClients");
|
||||||
|
sandbox.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
add_UITour_task(async function test_fxa_fails() {
|
||||||
|
const sandbox = sinon.createSandbox();
|
||||||
|
sandbox.stub(fxAccounts, "getSignedInUser").throws();
|
||||||
|
let result = await getConfigurationPromise("fxa");
|
||||||
|
Assert.deepEqual(result, {});
|
||||||
|
sandbox.restore();
|
||||||
|
});
|
|
@ -435,40 +435,37 @@ class FxAccounts {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an array listing all the OAuth clients
|
* Returns an array listing all the OAuth clients connected to the
|
||||||
* connected to the authenticated user's account.
|
* authenticated user's account. This includes browsers and web sessions - no
|
||||||
* Devices and web sessions are not included.
|
* filtering is done of the set returned by the FxA server.
|
||||||
*
|
*
|
||||||
* @typedef {Object} AttachedClient
|
* @typedef {Object} AttachedClient
|
||||||
* @property {String} id - OAuth `client_id` of the client.
|
* @property {String} id - OAuth `client_id` of the client.
|
||||||
* @property {String} name - Client name. e.g. Firefox Monitor.
|
* @property {Number} lastAccessedDaysAgo - How many days ago the client last
|
||||||
* @property {Number} lastAccessTime - Last access time in milliseconds.
|
* accessed the FxA server APIs.
|
||||||
*
|
*
|
||||||
* @returns {Array.<AttachedClient>} A list of attached clients.
|
* @returns {Array.<AttachedClient>} A list of attached clients.
|
||||||
*/
|
*/
|
||||||
async listAttachedOAuthClients() {
|
async listAttachedOAuthClients() {
|
||||||
|
// We expose last accessed times in 'days ago'
|
||||||
|
const ONE_DAY = 24 * 60 * 60 * 1000;
|
||||||
|
|
||||||
return this._withVerifiedAccountState(async state => {
|
return this._withVerifiedAccountState(async state => {
|
||||||
const { sessionToken } = await state.getUserAccountData(["sessionToken"]);
|
const { sessionToken } = await state.getUserAccountData(["sessionToken"]);
|
||||||
const attachedClients = await this._internal.fxAccountsClient.attachedClients(
|
const attachedClients = await this._internal.fxAccountsClient.attachedClients(
|
||||||
sessionToken
|
sessionToken
|
||||||
);
|
);
|
||||||
return attachedClients.reduce((oauthClients, client) => {
|
// We should use the server timestamp here - bug 1595635
|
||||||
// This heuristic aims to keep tokens for "associated services"
|
let now = Date.now();
|
||||||
// while throwing away the "browser" ones.
|
return attachedClients.map(client => {
|
||||||
if (
|
const daysAgo = client.lastAccessTime
|
||||||
client.clientId &&
|
? Math.max(Math.floor((now - client.lastAccessTime) / ONE_DAY), 0)
|
||||||
!client.deviceId &&
|
: null;
|
||||||
!client.sessionTokenId &&
|
return {
|
||||||
client.scope
|
|
||||||
) {
|
|
||||||
oauthClients.push({
|
|
||||||
id: client.clientId,
|
id: client.clientId,
|
||||||
name: client.name,
|
lastAccessedDaysAgo: daysAgo,
|
||||||
lastAccessTime: client.lastAccessTime,
|
};
|
||||||
});
|
});
|
||||||
}
|
|
||||||
return oauthClients;
|
|
||||||
}, []);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1600,6 +1600,9 @@ add_task(async function test_getOAuthToken_authErrorRefreshesCertificate() {
|
||||||
});
|
});
|
||||||
|
|
||||||
add_task(async function test_listAttachedOAuthClients() {
|
add_task(async function test_listAttachedOAuthClients() {
|
||||||
|
const ONE_HOUR = 60 * 60 * 1000;
|
||||||
|
const ONE_DAY = 24 * ONE_HOUR;
|
||||||
|
|
||||||
let fxa = new MockFxAccounts();
|
let fxa = new MockFxAccounts();
|
||||||
let alice = getTestUser("alice");
|
let alice = getTestUser("alice");
|
||||||
alice.verified = true;
|
alice.verified = true;
|
||||||
|
@ -1607,29 +1610,14 @@ add_task(async function test_listAttachedOAuthClients() {
|
||||||
let client = fxa._internal.fxAccountsClient;
|
let client = fxa._internal.fxAccountsClient;
|
||||||
client.attachedClients = async () => {
|
client.attachedClients = async () => {
|
||||||
return [
|
return [
|
||||||
{
|
// This entry was previously filtered but no longer is!
|
||||||
clientId: null,
|
|
||||||
deviceId: "deadbeef",
|
|
||||||
sessionTokenId: "deadbeef",
|
|
||||||
name: "Good ol' desktop device",
|
|
||||||
scope: null,
|
|
||||||
lastAccessTime: 1569263031001,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
clientId: null,
|
|
||||||
deviceId: null,
|
|
||||||
sessionTokenId: "deadbeef",
|
|
||||||
name: "Mobile device w/ no device record",
|
|
||||||
scope: null,
|
|
||||||
lastAccessTime: 1569263031001,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
clientId: "a2270f727f45f648",
|
clientId: "a2270f727f45f648",
|
||||||
deviceId: "deadbeef",
|
deviceId: "deadbeef",
|
||||||
sessionTokenId: null,
|
sessionTokenId: null,
|
||||||
name: "Firefox Preview (no session token)",
|
name: "Firefox Preview (no session token)",
|
||||||
scope: ["profile", "https://identity.mozilla.com/apps/oldsync"],
|
scope: ["profile", "https://identity.mozilla.com/apps/oldsync"],
|
||||||
lastAccessTime: 1569263031001,
|
lastAccessTime: Date.now(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
clientId: "802d56ef2a9af9fa",
|
clientId: "802d56ef2a9af9fa",
|
||||||
|
@ -1637,7 +1625,7 @@ add_task(async function test_listAttachedOAuthClients() {
|
||||||
sessionTokenId: null,
|
sessionTokenId: null,
|
||||||
name: "Firefox Monitor",
|
name: "Firefox Monitor",
|
||||||
scope: ["profile"],
|
scope: ["profile"],
|
||||||
lastAccessTime: 1569263031000,
|
lastAccessTime: Date.now() - ONE_DAY - ONE_HOUR,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
clientId: "1f30e32975ae5112",
|
clientId: "1f30e32975ae5112",
|
||||||
|
@ -1645,7 +1633,23 @@ add_task(async function test_listAttachedOAuthClients() {
|
||||||
sessionTokenId: null,
|
sessionTokenId: null,
|
||||||
name: "Firefox Send",
|
name: "Firefox Send",
|
||||||
scope: ["profile", "https://identity.mozilla.com/apps/send"],
|
scope: ["profile", "https://identity.mozilla.com/apps/send"],
|
||||||
lastAccessTime: 1569263013000,
|
lastAccessTime: Date.now() - ONE_DAY * 2 - ONE_HOUR,
|
||||||
|
},
|
||||||
|
// One with a future date should be impossible, but having a negative
|
||||||
|
// result here would almost certainly confuse something!
|
||||||
|
{
|
||||||
|
clientId: "future-date",
|
||||||
|
deviceId: null,
|
||||||
|
sessionTokenId: null,
|
||||||
|
name: "Whatever",
|
||||||
|
lastAccessTime: Date.now() + ONE_DAY,
|
||||||
|
},
|
||||||
|
// A missing/null lastAccessTime should end up with a missing lastAccessedDaysAgo
|
||||||
|
{
|
||||||
|
clientId: "missing-date",
|
||||||
|
deviceId: null,
|
||||||
|
sessionTokenId: null,
|
||||||
|
name: "Whatever",
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
@ -1653,15 +1657,25 @@ add_task(async function test_listAttachedOAuthClients() {
|
||||||
await fxa.setSignedInUser(alice);
|
await fxa.setSignedInUser(alice);
|
||||||
const clients = await fxa.listAttachedOAuthClients();
|
const clients = await fxa.listAttachedOAuthClients();
|
||||||
Assert.deepEqual(clients, [
|
Assert.deepEqual(clients, [
|
||||||
|
{
|
||||||
|
id: "a2270f727f45f648",
|
||||||
|
lastAccessedDaysAgo: 0,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: "802d56ef2a9af9fa",
|
id: "802d56ef2a9af9fa",
|
||||||
name: "Firefox Monitor",
|
lastAccessedDaysAgo: 1,
|
||||||
lastAccessTime: 1569263031000,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "1f30e32975ae5112",
|
id: "1f30e32975ae5112",
|
||||||
name: "Firefox Send",
|
lastAccessedDaysAgo: 2,
|
||||||
lastAccessTime: 1569263013000,
|
},
|
||||||
|
{
|
||||||
|
id: "future-date",
|
||||||
|
lastAccessedDaysAgo: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "missing-date",
|
||||||
|
lastAccessedDaysAgo: null,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
Загрузка…
Ссылка в новой задаче