1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
|
/* 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.setCharPref("identity.fxaccounts.loglevel", "Trace");
const { FxAccounts } = ChromeUtils.import(
"resource://gre/modules/FxAccounts.jsm"
);
const { FXA_PWDMGR_HOST, FXA_PWDMGR_REALM } = ChromeUtils.import(
"resource://gre/modules/FxAccountsCommon.js"
);
// Use a backstage pass to get at our LoginManagerStorage object, so we can
// mock the prototype.
var { LoginManagerStorage } = ChromeUtils.import(
"resource://gre/modules/FxAccountsStorage.jsm",
null
);
var isLoggedIn = true;
LoginManagerStorage.prototype.__defineGetter__("_isLoggedIn", () => isLoggedIn);
function setLoginMgrLoggedInState(loggedIn) {
isLoggedIn = loggedIn;
}
initTestLogging("Trace");
function getLoginMgrData() {
let logins = Services.logins.findLogins(
FXA_PWDMGR_HOST,
null,
FXA_PWDMGR_REALM
);
if (logins.length == 0) {
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",
kSync: "the kSync value",
kXCS: "the kXCS value",
kExtSync: "the kExtSync value",
kExtKbHash: "the kExtKbHash value",
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 profileDir = await PathUtils.getProfileDir();
let path = PathUtils.join(profileDir, "signedInUser.json");
let data = await CommonUtils.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(!("kSync" in data.accountData), "kSync not stored in clear text");
Assert.ok(!("kXCS" in data.accountData), "kXCS not stored in clear text");
Assert.ok(
!("kExtSync" in data.accountData),
"kExtSync not stored in clear text"
);
Assert.ok(
!("kExtKbHash" in data.accountData),
"kExtKbHash not stored in clear text"
);
let login = 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.strictEqual(
loginData.accountData.kSync,
creds.kSync,
"correct kSync in the login mgr"
);
Assert.strictEqual(
loginData.accountData.kXCS,
creds.kXCS,
"correct kXCS in the login mgr"
);
Assert.strictEqual(
loginData.accountData.kExtSync,
creds.kExtSync,
"correct kExtSync in the login mgr"
);
Assert.strictEqual(
loginData.accountData.kExtKbHash,
creds.kExtKbHash,
"correct kExtKbHash 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(
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",
kSync: "the kSync value",
kXCS: "the kXCS value",
kExtSync: "the kExtSync value",
kExtKbHash: "the kExtKbHash value",
verified: true,
};
Assert.strictEqual(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 profileDir = await PathUtils.getProfileDir();
let path = PathUtils.join(profileDir, "signedInUser.json");
let data = await CommonUtils.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(!("kSync" in data.accountData), "kSync not stored in clear text");
Assert.ok(!("kXCS" in data.accountData), "kXCS not stored in clear text");
Assert.ok(
!("kExtSync" in data.accountData),
"kExtSync not stored in clear text"
);
Assert.ok(
!("kExtKbHash" in data.accountData),
"kExtKbHash not stored in clear text"
);
Assert.strictEqual(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",
kSync: "the kSync value",
kXCS: "the kXCS value",
kExtSync: "the kExtSync value",
kExtKbHash: "the kExtKbHash value",
verified: true,
};
let creds2 = {
uid: "uid2",
email: "test2@example.com",
sessionToken: "sessionToken2",
kSync: "the kSync value2",
kXCS: "the kXCS value2",
kExtSync: "the kExtSync value2",
kExtKbHash: "the kExtKbHash value2",
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 = getLoginMgrData();
Assert.strictEqual(login.username, creds1.uid);
// and that we do have the first kSync in the login manager.
Assert.strictEqual(
JSON.parse(login.password).accountData.kSync,
creds1.kSync,
"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 kSync at all.
Assert.strictEqual(accountData.kSync, undefined, "stale kSync 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(getLoginMgrData(), null, "expect no logins at the start");
// create the login entry using email as a key.
let contents = { kSync: "kSync" };
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
Services.logins.addLogin(login);
// ensure we read it.
let storage = new LoginManagerStorage();
let got = await storage.get("uid", "foo@bar.com");
Assert.deepEqual(got, contents);
});
|