Bug 1339163 - Make TPS tests attempt to automatically verify fxa emails when using a restmail account r?markh
MozReview-Commit-ID: LwBrVSXFqyc
--- a/services/sync/tps/extensions/tps/resource/auth/fxaccounts.jsm
+++ b/services/sync/tps/extensions/tps/resource/auth/fxaccounts.jsm
@@ -5,36 +5,142 @@
"use strict";
this.EXPORTED_SYMBOLS = [
"Authentication",
];
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
+Cu.import("resource://gre/modules/Log.jsm");
+Cu.import("resource://gre/modules/Timer.jsm");
Cu.import("resource://gre/modules/FxAccounts.jsm");
Cu.import("resource://gre/modules/FxAccountsClient.jsm");
Cu.import("resource://gre/modules/FxAccountsConfig.jsm");
Cu.import("resource://services-common/async.js");
Cu.import("resource://services-sync/main.js");
Cu.import("resource://tps/logger.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Services", "resource://gre/modules/Services.jsm");
+Cu.importGlobalProperties(["fetch"]);
/**
* Helper object for Firefox Accounts authentication
*/
var Authentication = {
/**
* Check if an user has been logged in
*/
get isLoggedIn() {
return !!this.getSignedInUser();
},
+ _getRestmailUsername(user) {
+ const restmailSuffix = "@restmail.net";
+ if (user.toLowerCase().endsWith(restmailSuffix)) {
+ return user.slice(0, -restmailSuffix.length);
+ }
+ return null;
+ },
+
+ async shortWaitForVerification(ms) {
+ let userData = this.getSignedInUser();
+ await Promise.race([
+ fxAccounts.whenVerified(userData),
+ new Promise(resolve => {
+ setTimeout(() => {
+ Logger.logInfo(`Warning: no verification after ${ms}ms.`);
+ resolve();
+ }, ms);
+ })
+ ]);
+ userData = this.getSignedInUser();
+ return userData && userData.verified;
+ },
+
+ async _openVerificationPage(uri) {
+ let mainWindow = Services.wm.getMostRecentWindow("navigator:browser");
+ let newtab = mainWindow.getBrowser().addTab(uri);
+ let win = mainWindow.getBrowser().getBrowserForTab(newtab);
+ await new Promise(resolve => {
+ win.addEventListener("loadend", resolve, { once: true });
+ });
+ let didVerify = await this.shortWaitForVerification(10000);
+ mainWindow.getBrowser().removeTab(newtab);
+ return didVerify;
+ },
+
+ async _completeVerification(user) {
+ let username = this._getRestmailUsername(user);
+ if (!username) {
+ Logger.logInfo(`Username "${user}" isn't a restmail username so can't complete verification`);
+ return false;
+ }
+ Logger.logInfo("Fetching mail (from restmail) for user " + username);
+ let restmailURI = `https://www.restmail.net/mail/${encodeURIComponent(username)}`;
+ let triedAlready = new Set();
+ const tries = 10;
+ const normalWait = 2000;
+ for (let i = 0; i < tries; ++i) {
+ if (await this.shortWaitForVerification(normalWait)) {
+ return true;
+ }
+ let resp = await fetch(restmailURI);
+ let messages = await resp.json();
+ // Sort so that the most recent emails are first.
+ messages.sort((a, b) => new Date(b.receivedAt) - new Date(a.receivedAt));
+ for (let m of messages) {
+ // We look for a link that has a x-link that we haven't yet tried.
+ if (!m.headers["x-link"] || triedAlready.has(m.headers["x-link"])) {
+ continue;
+ }
+ let confirmLink = m.headers["x-link"];
+ triedAlready.add(confirmLink);
+ Logger.logInfo("Trying confirmation link " + confirmLink);
+ try {
+ if (await this._openVerificationPage(confirmLink)) {
+ return true;
+ }
+ } catch (e) {
+ Logger.logInfo("Warning: Failed to follow confirmation link: " + Log.exceptionStr(e));
+ }
+ }
+ if (i === 0) {
+ // first time through after failing we'll do this.
+ await fxAccounts.resendVerificationEmail();
+ }
+ }
+ // One last try.
+ return this.shortWaitForVerification(normalWait);
+ },
+
+ async deleteEmail(user) {
+ let username = this._getRestmailUsername(user);
+ if (!username) {
+ Logger.logInfo("Not a restmail username, can't delete");
+ return false;
+ }
+ Logger.logInfo("Deleting mail (from restmail) for user " + username);
+ let restmailURI = `https://www.restmail.net/mail/${encodeURIComponent(username)}`;
+ try {
+ // Clean up after ourselves.
+ let deleteResult = await fetch(restmailURI, { method: "DELETE" });
+ if (!deleteResult.ok) {
+ Logger.logInfo(`Warning: Got non-success status ${deleteResult.status} when deleting emails`);
+ return false;
+ }
+ } catch (e) {
+ Logger.logInfo("Warning: Failed to delete old emails: " + Log.exceptionStr(e));
+ return false;
+ }
+ return true;
+ },
+
/**
* Wrapper to retrieve the currently signed in user
*
* @returns Information about the currently signed in user
*/
getSignedInUser: function getSignedInUser() {
let cb = Async.makeSpinningCallback();
@@ -72,16 +178,18 @@ var Authentication = {
// Required here since we don't go through the real login page
Async.promiseSpinningly(FxAccountsConfig.ensureConfigured());
let client = new FxAccountsClient();
client.signIn(account["username"], account["password"], true).then(credentials => {
return fxAccounts.setSignedInUser(credentials);
}).then(() => {
+ return this._completeVerification(account["username"])
+ }).then(() => {
cb(null, true);
}, error => {
cb(error, false);
});
try {
cb.wait();
@@ -107,15 +215,16 @@ var Authentication = {
if (!user) {
throw new Error("Failed to get signed in user!");
}
let fxc = new FxAccountsClient();
let { sessionToken, deviceId } = user;
if (deviceId) {
Logger.logInfo("Destroying device " + deviceId);
Async.promiseSpinningly(fxAccounts.deleteDeviceRegistration(sessionToken, deviceId));
+ Async.promiseSpinningly(fxAccounts.signOut(true));
} else {
Logger.logError("No device found.");
Async.promiseSpinningly(fxc.signOut(sessionToken, { service: "sync" }));
}
}
}
};
--- a/services/sync/tps/extensions/tps/resource/tps.jsm
+++ b/services/sync/tps/extensions/tps/resource/tps.jsm
@@ -582,16 +582,17 @@ var TPS = {
if (Authentication.isLoggedIn) {
// signout and wait for Sync to completely reset itself.
Logger.logInfo("signing out");
let waiter = this.createEventWaiter("weave:service:start-over:finish");
Authentication.signOut();
waiter();
Logger.logInfo("signout complete");
}
+ Authentication.deleteEmail(this.config.fx_account.username);
} catch (e) {
Logger.logError("Failed to sign out: " + Log.exceptionStr(e));
}
},
/**
* Use Sync's bookmark validation code to see if we've corrupted the tree.
*/