tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

test_loginmgr_storage.js (7809B)


      1 /* Any copyright is dedicated to the Public Domain.
      2 * http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 "use strict";
      5 
      6 // Tests for FxAccounts, storage and the master password.
      7 
      8 // See verbose logging from FxAccounts.sys.mjs
      9 Services.prefs.setStringPref("identity.fxaccounts.loglevel", "Trace");
     10 
     11 const { FxAccounts } = ChromeUtils.importESModule(
     12  "resource://gre/modules/FxAccounts.sys.mjs"
     13 );
     14 const { FXA_PWDMGR_HOST, FXA_PWDMGR_REALM } = ChromeUtils.importESModule(
     15  "resource://gre/modules/FxAccountsCommon.sys.mjs"
     16 );
     17 
     18 // Use the system global to get at our LoginManagerStorage object, so we can
     19 // mock the prototype.
     20 var { LoginManagerStorage } = ChromeUtils.importESModule(
     21  "resource://gre/modules/FxAccountsStorage.sys.mjs"
     22 );
     23 var isLoggedIn = true;
     24 LoginManagerStorage.prototype.__defineGetter__("_isLoggedIn", () => isLoggedIn);
     25 
     26 function setLoginMgrLoggedInState(loggedIn) {
     27  isLoggedIn = loggedIn;
     28 }
     29 
     30 initTestLogging("Trace");
     31 
     32 async function getLoginMgrData() {
     33  let logins = await Services.logins.searchLoginsAsync({
     34    origin: FXA_PWDMGR_HOST,
     35    httpRealm: FXA_PWDMGR_REALM,
     36  });
     37  if (!logins.length) {
     38    return null;
     39  }
     40  Assert.equal(logins.length, 1, "only 1 login available");
     41  return logins[0];
     42 }
     43 
     44 function createFxAccounts() {
     45  return new FxAccounts({
     46    _fxAccountsClient: {
     47      async registerDevice() {
     48        return { id: "deviceAAAAAA" };
     49      },
     50      async recoveryEmailStatus() {
     51        return { verified: true };
     52      },
     53      async signOut() {},
     54    },
     55    updateDeviceRegistration() {},
     56    _getDeviceName() {
     57      return "mock device name";
     58    },
     59    observerPreloads: [],
     60    fxaPushService: {
     61      async registerPushEndpoint() {
     62        return {
     63          endpoint: "http://mochi.test:8888",
     64          getKey() {
     65            return null;
     66          },
     67        };
     68      },
     69      async unsubscribe() {
     70        return true;
     71      },
     72    },
     73  });
     74 }
     75 
     76 add_task(async function test_simple() {
     77  let fxa = createFxAccounts();
     78 
     79  let creds = {
     80    uid: "abcd",
     81    email: "test@example.com",
     82    sessionToken: "sessionToken",
     83    scopedKeys: {
     84      ...MOCK_ACCOUNT_KEYS.scopedKeys,
     85    },
     86    verified: true,
     87  };
     88  await fxa._internal.setSignedInUser(creds);
     89 
     90  // This should have stored stuff in both the .json file in the profile
     91  // dir, and the login dir.
     92  let path = PathUtils.join(PathUtils.profileDir, "signedInUser.json");
     93  let data = await IOUtils.readJSON(path);
     94 
     95  Assert.strictEqual(
     96    data.accountData.email,
     97    creds.email,
     98    "correct email in the clear text"
     99  );
    100  Assert.strictEqual(
    101    data.accountData.sessionToken,
    102    creds.sessionToken,
    103    "correct sessionToken in the clear text"
    104  );
    105  Assert.strictEqual(
    106    data.accountData.verified,
    107    creds.verified,
    108    "correct verified flag"
    109  );
    110 
    111  Assert.ok(
    112    !("scopedKeys" in data.accountData),
    113    "scopedKeys not stored in clear text"
    114  );
    115 
    116  let login = await getLoginMgrData();
    117  Assert.strictEqual(login.username, creds.uid, "uid used for username");
    118  let loginData = JSON.parse(login.password);
    119  Assert.strictEqual(
    120    loginData.version,
    121    data.version,
    122    "same version flag in both places"
    123  );
    124  Assert.deepEqual(
    125    loginData.accountData.scopedKeys,
    126    creds.scopedKeys,
    127    "correct scoped keys in the login mgr"
    128  );
    129  Assert.ok(!("email" in loginData), "email not stored in the login mgr json");
    130  Assert.ok(
    131    !("sessionToken" in loginData),
    132    "sessionToken not stored in the login mgr json"
    133  );
    134  Assert.ok(
    135    !("verified" in loginData),
    136    "verified not stored in the login mgr json"
    137  );
    138 
    139  await fxa.signOut(/* localOnly = */ true);
    140  Assert.strictEqual(
    141    await getLoginMgrData(),
    142    null,
    143    "login mgr data deleted on logout"
    144  );
    145 });
    146 
    147 add_task(async function test_MPLocked() {
    148  let fxa = createFxAccounts();
    149 
    150  let creds = {
    151    uid: "abcd",
    152    email: "test@example.com",
    153    sessionToken: "sessionToken",
    154    scopedKeys: {
    155      ...MOCK_ACCOUNT_KEYS.scopedKeys,
    156    },
    157    verified: true,
    158  };
    159 
    160  Assert.strictEqual(
    161    await getLoginMgrData(),
    162    null,
    163    "no login mgr at the start"
    164  );
    165  // tell the storage that the MP is locked.
    166  setLoginMgrLoggedInState(false);
    167  await fxa._internal.setSignedInUser(creds);
    168 
    169  // This should have stored stuff in the .json, and the login manager stuff
    170  // will not exist.
    171  let path = PathUtils.join(PathUtils.profileDir, "signedInUser.json");
    172  let data = await IOUtils.readJSON(path);
    173 
    174  Assert.strictEqual(
    175    data.accountData.email,
    176    creds.email,
    177    "correct email in the clear text"
    178  );
    179  Assert.strictEqual(
    180    data.accountData.sessionToken,
    181    creds.sessionToken,
    182    "correct sessionToken in the clear text"
    183  );
    184  Assert.strictEqual(
    185    data.accountData.verified,
    186    creds.verified,
    187    "correct verified flag"
    188  );
    189 
    190  Assert.ok(
    191    !("scopedKeys" in data.accountData),
    192    "scopedKeys not stored in clear text"
    193  );
    194 
    195  Assert.strictEqual(
    196    await getLoginMgrData(),
    197    null,
    198    "login mgr data doesn't exist"
    199  );
    200  await fxa.signOut(/* localOnly = */ true);
    201 });
    202 
    203 add_task(async function test_consistentWithMPEdgeCases() {
    204  setLoginMgrLoggedInState(true);
    205 
    206  let fxa = createFxAccounts();
    207 
    208  let creds1 = {
    209    uid: "uid1",
    210    email: "test@example.com",
    211    sessionToken: "sessionToken",
    212    scopedKeys: {
    213      [SCOPE_OLD_SYNC]: {
    214        kid: "key id 1",
    215        k: "key material 1",
    216        kty: "oct",
    217      },
    218    },
    219    verified: true,
    220  };
    221 
    222  let creds2 = {
    223    uid: "uid2",
    224    email: "test2@example.com",
    225    sessionToken: "sessionToken2",
    226    [SCOPE_OLD_SYNC]: {
    227      kid: "key id 2",
    228      k: "key material 2",
    229      kty: "oct",
    230    },
    231    verified: false,
    232  };
    233 
    234  // Log a user in while MP is unlocked.
    235  await fxa._internal.setSignedInUser(creds1);
    236 
    237  // tell the storage that the MP is locked - this will prevent logout from
    238  // being able to clear the data.
    239  setLoginMgrLoggedInState(false);
    240 
    241  // now set the second credentials.
    242  await fxa._internal.setSignedInUser(creds2);
    243 
    244  // We should still have creds1 data in the login manager.
    245  let login = await getLoginMgrData();
    246  Assert.strictEqual(login.username, creds1.uid);
    247  // and that we do have the first scopedKeys in the login manager.
    248  Assert.deepEqual(
    249    JSON.parse(login.password).accountData.scopedKeys,
    250    creds1.scopedKeys,
    251    "stale data still in login mgr"
    252  );
    253 
    254  // Make a new FxA instance (otherwise the values in memory will be used)
    255  // and we want the login manager to be unlocked.
    256  setLoginMgrLoggedInState(true);
    257  fxa = createFxAccounts();
    258 
    259  let accountData = await fxa.getSignedInUser();
    260  Assert.strictEqual(accountData.email, creds2.email);
    261  // we should have no scopedKeys at all.
    262  Assert.strictEqual(
    263    accountData.scopedKeys,
    264    undefined,
    265    "stale scopedKey wasn't used"
    266  );
    267  await fxa.signOut(/* localOnly = */ true);
    268 });
    269 
    270 // A test for the fact we will accept either a UID or email when looking in
    271 // the login manager.
    272 add_task(async function test_uidMigration() {
    273  setLoginMgrLoggedInState(true);
    274  Assert.strictEqual(
    275    await getLoginMgrData(),
    276    null,
    277    "expect no logins at the start"
    278  );
    279 
    280  // create the login entry using email as a key.
    281  let contents = {
    282    scopedKeys: {
    283      ...MOCK_ACCOUNT_KEYS.scopedKeys,
    284    },
    285  };
    286 
    287  let loginInfo = new Components.Constructor(
    288    "@mozilla.org/login-manager/loginInfo;1",
    289    Ci.nsILoginInfo,
    290    "init"
    291  );
    292  let login = new loginInfo(
    293    FXA_PWDMGR_HOST,
    294    null, // aFormActionOrigin,
    295    FXA_PWDMGR_REALM, // aHttpRealm,
    296    "foo@bar.com", // aUsername
    297    JSON.stringify(contents), // aPassword
    298    "", // aUsernameField
    299    ""
    300  ); // aPasswordField
    301  await Services.logins.addLoginAsync(login);
    302 
    303  // ensure we read it.
    304  let storage = new LoginManagerStorage();
    305  let got = await storage.get("uid", "foo@bar.com");
    306  Assert.deepEqual(got, contents);
    307 });