summaryrefslogtreecommitdiffstats
path: root/services/fxaccounts/tests/xpcshell/test_loginmgr_storage.js
diff options
context:
space:
mode:
Diffstat (limited to 'services/fxaccounts/tests/xpcshell/test_loginmgr_storage.js')
-rw-r--r--services/fxaccounts/tests/xpcshell/test_loginmgr_storage.js307
1 files changed, 307 insertions, 0 deletions
diff --git a/services/fxaccounts/tests/xpcshell/test_loginmgr_storage.js b/services/fxaccounts/tests/xpcshell/test_loginmgr_storage.js
new file mode 100644
index 0000000000..5b80035418
--- /dev/null
+++ b/services/fxaccounts/tests/xpcshell/test_loginmgr_storage.js
@@ -0,0 +1,307 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Tests for FxAccounts, storage and the master password.
+
+// See verbose logging from FxAccounts.jsm
+Services.prefs.setStringPref("identity.fxaccounts.loglevel", "Trace");
+
+const { FxAccounts } = ChromeUtils.importESModule(
+ "resource://gre/modules/FxAccounts.sys.mjs"
+);
+const { FXA_PWDMGR_HOST, FXA_PWDMGR_REALM } = ChromeUtils.importESModule(
+ "resource://gre/modules/FxAccountsCommon.sys.mjs"
+);
+
+// Use a backstage pass to get at our LoginManagerStorage object, so we can
+// mock the prototype.
+var { LoginManagerStorage } = ChromeUtils.importESModule(
+ "resource://gre/modules/FxAccountsStorage.sys.mjs"
+);
+var isLoggedIn = true;
+LoginManagerStorage.prototype.__defineGetter__("_isLoggedIn", () => isLoggedIn);
+
+function setLoginMgrLoggedInState(loggedIn) {
+ isLoggedIn = loggedIn;
+}
+
+initTestLogging("Trace");
+
+async function getLoginMgrData() {
+ let logins = await Services.logins.searchLoginsAsync({
+ origin: FXA_PWDMGR_HOST,
+ httpRealm: FXA_PWDMGR_REALM,
+ });
+ if (!logins.length) {
+ return null;
+ }
+ Assert.equal(logins.length, 1, "only 1 login available");
+ return logins[0];
+}
+
+function createFxAccounts() {
+ return new FxAccounts({
+ _fxAccountsClient: {
+ async registerDevice() {
+ return { id: "deviceAAAAAA" };
+ },
+ async recoveryEmailStatus() {
+ return { verified: true };
+ },
+ async signOut() {},
+ },
+ updateDeviceRegistration() {},
+ _getDeviceName() {
+ return "mock device name";
+ },
+ observerPreloads: [],
+ fxaPushService: {
+ async registerPushEndpoint() {
+ return {
+ endpoint: "http://mochi.test:8888",
+ getKey() {
+ return null;
+ },
+ };
+ },
+ async unsubscribe() {
+ return true;
+ },
+ },
+ });
+}
+
+add_task(async function test_simple() {
+ let fxa = createFxAccounts();
+
+ let creds = {
+ uid: "abcd",
+ email: "test@example.com",
+ sessionToken: "sessionToken",
+ scopedKeys: {
+ ...MOCK_ACCOUNT_KEYS.scopedKeys,
+ },
+ verified: true,
+ };
+ await fxa._internal.setSignedInUser(creds);
+
+ // This should have stored stuff in both the .json file in the profile
+ // dir, and the login dir.
+ let path = PathUtils.join(PathUtils.profileDir, "signedInUser.json");
+ let data = await IOUtils.readJSON(path);
+
+ Assert.strictEqual(
+ data.accountData.email,
+ creds.email,
+ "correct email in the clear text"
+ );
+ Assert.strictEqual(
+ data.accountData.sessionToken,
+ creds.sessionToken,
+ "correct sessionToken in the clear text"
+ );
+ Assert.strictEqual(
+ data.accountData.verified,
+ creds.verified,
+ "correct verified flag"
+ );
+
+ Assert.ok(
+ !("scopedKeys" in data.accountData),
+ "scopedKeys not stored in clear text"
+ );
+
+ let login = await getLoginMgrData();
+ Assert.strictEqual(login.username, creds.uid, "uid used for username");
+ let loginData = JSON.parse(login.password);
+ Assert.strictEqual(
+ loginData.version,
+ data.version,
+ "same version flag in both places"
+ );
+ Assert.deepEqual(
+ loginData.accountData.scopedKeys,
+ creds.scopedKeys,
+ "correct scoped keys in the login mgr"
+ );
+ Assert.ok(!("email" in loginData), "email not stored in the login mgr json");
+ Assert.ok(
+ !("sessionToken" in loginData),
+ "sessionToken not stored in the login mgr json"
+ );
+ Assert.ok(
+ !("verified" in loginData),
+ "verified not stored in the login mgr json"
+ );
+
+ await fxa.signOut(/* localOnly = */ true);
+ Assert.strictEqual(
+ await getLoginMgrData(),
+ null,
+ "login mgr data deleted on logout"
+ );
+});
+
+add_task(async function test_MPLocked() {
+ let fxa = createFxAccounts();
+
+ let creds = {
+ uid: "abcd",
+ email: "test@example.com",
+ sessionToken: "sessionToken",
+ scopedKeys: {
+ ...MOCK_ACCOUNT_KEYS.scopedKeys,
+ },
+ verified: true,
+ };
+
+ Assert.strictEqual(
+ await getLoginMgrData(),
+ null,
+ "no login mgr at the start"
+ );
+ // tell the storage that the MP is locked.
+ setLoginMgrLoggedInState(false);
+ await fxa._internal.setSignedInUser(creds);
+
+ // This should have stored stuff in the .json, and the login manager stuff
+ // will not exist.
+ let path = PathUtils.join(PathUtils.profileDir, "signedInUser.json");
+ let data = await IOUtils.readJSON(path);
+
+ Assert.strictEqual(
+ data.accountData.email,
+ creds.email,
+ "correct email in the clear text"
+ );
+ Assert.strictEqual(
+ data.accountData.sessionToken,
+ creds.sessionToken,
+ "correct sessionToken in the clear text"
+ );
+ Assert.strictEqual(
+ data.accountData.verified,
+ creds.verified,
+ "correct verified flag"
+ );
+
+ Assert.ok(
+ !("scopedKeys" in data.accountData),
+ "scopedKeys not stored in clear text"
+ );
+
+ Assert.strictEqual(
+ await getLoginMgrData(),
+ null,
+ "login mgr data doesn't exist"
+ );
+ await fxa.signOut(/* localOnly = */ true);
+});
+
+add_task(async function test_consistentWithMPEdgeCases() {
+ setLoginMgrLoggedInState(true);
+
+ let fxa = createFxAccounts();
+
+ let creds1 = {
+ uid: "uid1",
+ email: "test@example.com",
+ sessionToken: "sessionToken",
+ scopedKeys: {
+ [SCOPE_OLD_SYNC]: {
+ kid: "key id 1",
+ k: "key material 1",
+ kty: "oct",
+ },
+ },
+ verified: true,
+ };
+
+ let creds2 = {
+ uid: "uid2",
+ email: "test2@example.com",
+ sessionToken: "sessionToken2",
+ [SCOPE_OLD_SYNC]: {
+ kid: "key id 2",
+ k: "key material 2",
+ kty: "oct",
+ },
+ verified: false,
+ };
+
+ // Log a user in while MP is unlocked.
+ await fxa._internal.setSignedInUser(creds1);
+
+ // tell the storage that the MP is locked - this will prevent logout from
+ // being able to clear the data.
+ setLoginMgrLoggedInState(false);
+
+ // now set the second credentials.
+ await fxa._internal.setSignedInUser(creds2);
+
+ // We should still have creds1 data in the login manager.
+ let login = await getLoginMgrData();
+ Assert.strictEqual(login.username, creds1.uid);
+ // and that we do have the first scopedKeys in the login manager.
+ Assert.deepEqual(
+ JSON.parse(login.password).accountData.scopedKeys,
+ creds1.scopedKeys,
+ "stale data still in login mgr"
+ );
+
+ // Make a new FxA instance (otherwise the values in memory will be used)
+ // and we want the login manager to be unlocked.
+ setLoginMgrLoggedInState(true);
+ fxa = createFxAccounts();
+
+ let accountData = await fxa.getSignedInUser();
+ Assert.strictEqual(accountData.email, creds2.email);
+ // we should have no scopedKeys at all.
+ Assert.strictEqual(
+ accountData.scopedKeys,
+ undefined,
+ "stale scopedKey wasn't used"
+ );
+ await fxa.signOut(/* localOnly = */ true);
+});
+
+// A test for the fact we will accept either a UID or email when looking in
+// the login manager.
+add_task(async function test_uidMigration() {
+ setLoginMgrLoggedInState(true);
+ Assert.strictEqual(
+ await getLoginMgrData(),
+ null,
+ "expect no logins at the start"
+ );
+
+ // create the login entry using email as a key.
+ let contents = {
+ scopedKeys: {
+ ...MOCK_ACCOUNT_KEYS.scopedKeys,
+ },
+ };
+
+ let loginInfo = new Components.Constructor(
+ "@mozilla.org/login-manager/loginInfo;1",
+ Ci.nsILoginInfo,
+ "init"
+ );
+ let login = new loginInfo(
+ FXA_PWDMGR_HOST,
+ null, // aFormActionOrigin,
+ FXA_PWDMGR_REALM, // aHttpRealm,
+ "foo@bar.com", // aUsername
+ JSON.stringify(contents), // aPassword
+ "", // aUsernameField
+ ""
+ ); // aPasswordField
+ await Services.logins.addLoginAsync(login);
+
+ // ensure we read it.
+ let storage = new LoginManagerStorage();
+ let got = await storage.get("uid", "foo@bar.com");
+ Assert.deepEqual(got, contents);
+});