зеркало из https://github.com/mozilla/gecko-dev.git
Backed out 2 changesets (bug 1152761, bug 1153806) for xpcshell failures.
Backed out changeset cf10c6adab48 (bug 1153806) Backed out changeset 89a69f417877 (bug 1152761) CLOSED TREE
This commit is contained in:
Родитель
887d95a4a7
Коммит
26e41292ad
|
@ -7,13 +7,11 @@ const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
|||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
|
||||
const {MozLoopService, LOOP_SESSION_TYPE} = Cu.import("resource:///modules/loop/MozLoopService.jsm", {});
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
|
||||
"resource://gre/modules/Promise.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "CommonUtils",
|
||||
"resource://services-common/utils.js");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Task",
|
||||
"resource://gre/modules/Task.jsm");
|
||||
XPCOMUtils.defineLazyGetter(this, "eventEmitter", function() {
|
||||
const {EventEmitter} = Cu.import("resource://gre/modules/devtools/event-emitter.js", {});
|
||||
return new EventEmitter();
|
||||
|
@ -21,11 +19,8 @@ XPCOMUtils.defineLazyGetter(this, "eventEmitter", function() {
|
|||
XPCOMUtils.defineLazyGetter(this, "gLoopBundle", function() {
|
||||
return Services.strings.createBundle('chrome://browser/locale/loop/loop.properties');
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "LoopRoomsCache",
|
||||
"resource:///modules/loop/LoopRoomsCache.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "loopUtils",
|
||||
"resource:///modules/loop/utils.js", "utils");
|
||||
"resource:///modules/loop/utils.js", "utils")
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "loopCrypto",
|
||||
"resource:///modules/loop/crypto.js", "LoopCrypto");
|
||||
|
||||
|
@ -46,8 +41,6 @@ const roomsPushNotification = function(version, channelID) {
|
|||
let gDirty = true;
|
||||
// Global variable that keeps track of the currently used account.
|
||||
let gCurrentUser = null;
|
||||
// Global variable that keeps track of the room cache.
|
||||
let gRoomsCache = null;
|
||||
|
||||
/**
|
||||
* Extend a `target` object with the properties defined in `source`.
|
||||
|
@ -130,13 +123,6 @@ let LoopRoomsInternal = {
|
|||
*/
|
||||
rooms: new Map(),
|
||||
|
||||
get roomsCache() {
|
||||
if (!gRoomsCache) {
|
||||
gRoomsCache = new LoopRoomsCache();
|
||||
}
|
||||
return gRoomsCache;
|
||||
},
|
||||
|
||||
/**
|
||||
* @var {String} sessionType The type of user session. May be 'FXA' or 'GUEST'.
|
||||
*/
|
||||
|
@ -232,6 +218,11 @@ let LoopRoomsInternal = {
|
|||
};
|
||||
}
|
||||
|
||||
// For now, disable encryption/context if context is disabled
|
||||
if (!MozLoopService.getLoopPref("contextInConverations.enabled")) {
|
||||
return getUnencryptedData();
|
||||
}
|
||||
|
||||
var newRoomData = extend({}, roomData);
|
||||
|
||||
if (!newRoomData.context) {
|
||||
|
@ -289,40 +280,12 @@ let LoopRoomsInternal = {
|
|||
throw new Error("Missing wrappedKey");
|
||||
}
|
||||
|
||||
let savedRoomKey = yield this.roomsCache.getKey(this.sessionType, roomData.roomToken);
|
||||
let fallback = false;
|
||||
let key;
|
||||
|
||||
try {
|
||||
key = yield this.promiseDecryptRoomKey(roomData.context.wrappedKey);
|
||||
} catch (error) {
|
||||
// If we don't have a key saved, then we can't do anything.
|
||||
if (!savedRoomKey) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
// We failed to decrypt the room key, so has our FxA key changed?
|
||||
// If so, we fall-back to the saved room key.
|
||||
key = savedRoomKey;
|
||||
fallback = true;
|
||||
}
|
||||
// Bug 1152761 will cause us to additionally store keys locally. We'll
|
||||
// need to add some code for recovery in case decryption fails.
|
||||
let key = yield this.promiseDecryptRoomKey(roomData.context.wrappedKey);
|
||||
|
||||
let decryptedData = yield loopCrypto.decryptBytes(key, roomData.context.value);
|
||||
|
||||
if (fallback) {
|
||||
// Fallback decryption succeeded, so we need to re-encrypt the room key and
|
||||
// save the data back again.
|
||||
// XXX Bug 1152764 will implement this or make it a separate bug.
|
||||
} else if (!savedRoomKey || key != savedRoomKey) {
|
||||
// Decryption succeeded, but we don't have the right key saved.
|
||||
try {
|
||||
yield this.roomsCache.setKey(this.sessionType, roomData.roomToken, key);
|
||||
}
|
||||
catch (error) {
|
||||
MozLoopService.log.error("Failed to save room key:", error);
|
||||
}
|
||||
}
|
||||
|
||||
roomData.roomKey = key;
|
||||
roomData.decryptedContext = JSON.parse(decryptedData);
|
||||
|
||||
|
@ -379,7 +342,7 @@ let LoopRoomsInternal = {
|
|||
|
||||
this.saveAndNotifyUpdate(roomData, isUpdate);
|
||||
} catch (error) {
|
||||
MozLoopService.log.error("Failed to decrypt room data: ", error);
|
||||
MozLoopService.log.error("Failed to decrypt room data: " + error);
|
||||
// Do what we can to save the room data.
|
||||
room.decryptedContext = {};
|
||||
this.saveAndNotifyUpdate(room, isUpdate);
|
||||
|
@ -532,9 +495,6 @@ let LoopRoomsInternal = {
|
|||
this.setGuestCreatedRoom(true);
|
||||
}
|
||||
|
||||
// Now we've got the room token, we can save the key to disk.
|
||||
yield this.roomsCache.setKey(this.sessionType, room.roomToken, room.roomKey);
|
||||
|
||||
eventEmitter.emit("add", room);
|
||||
callback(null, room);
|
||||
}.bind(this)).catch(callback);
|
||||
|
@ -740,15 +700,10 @@ let LoopRoomsInternal = {
|
|||
};
|
||||
|
||||
// If we're not encrypting currently, then only send the roomName.
|
||||
// XXX This should go away once bug 1153788 is fixed.
|
||||
if (!sendData.context) {
|
||||
if (!Services.prefs.getBoolPref("loop.contextInConverations.enabled")) {
|
||||
sendData = {
|
||||
roomName: newRoomName
|
||||
};
|
||||
} else {
|
||||
// This might be an upgrade to encrypted rename, so store the key
|
||||
// just in case.
|
||||
yield this.roomsCache.setKey(this.sessionType, all.roomToken, all.roomKey);
|
||||
}
|
||||
|
||||
let response = yield MozLoopService.hawkRequest(this.sessionType,
|
||||
|
|
|
@ -1,159 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
const {MozLoopService, LOOP_SESSION_TYPE} =
|
||||
Cu.import("resource:///modules/loop/MozLoopService.jsm", {});
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "CommonUtils",
|
||||
"resource://services-common/utils.js");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["LoopRoomsCache"];
|
||||
|
||||
const LOOP_ROOMS_CACHE_FILENAME = "loopRoomsCache.json";
|
||||
|
||||
/**
|
||||
* RoomsCache is a cache for saving simple rooms data to the disk in case we
|
||||
* need it for back-up purposes, e.g. recording room keys for FxA if the user
|
||||
* changes their password.
|
||||
*
|
||||
* The format of the data is:
|
||||
*
|
||||
* {
|
||||
* <sessionType>: {
|
||||
* <roomToken>: {
|
||||
* "key": <roomKey>
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* It is intended to try and keep the data forward and backwards compatible in
|
||||
* a reasonable manner, hence why the structure is more complex than it needs
|
||||
* to be to store tokens and keys.
|
||||
*
|
||||
* @param {Object} options The options for the RoomsCache, containing:
|
||||
* - {String} baseDir The base directory in which to save the file.
|
||||
* - {String} filename The filename for the cache file.
|
||||
*/
|
||||
function LoopRoomsCache(options) {
|
||||
options = options || {};
|
||||
|
||||
this.baseDir = options.baseDir || OS.Constants.Path.profileDir;
|
||||
this.path = OS.Path.join(
|
||||
this.baseDir,
|
||||
options.filename || LOOP_ROOMS_CACHE_FILENAME
|
||||
);
|
||||
this._cache = null;
|
||||
}
|
||||
|
||||
LoopRoomsCache.prototype = {
|
||||
/**
|
||||
* Updates the local copy of the cache and saves it to disk.
|
||||
*
|
||||
* @param {Object} contents An object to be saved in json format.
|
||||
* @return {Promise} A promise that is resolved once the save is complete.
|
||||
*/
|
||||
_setCache: function(contents) {
|
||||
this._cache = contents;
|
||||
|
||||
return OS.File.makeDir(this.baseDir, {ignoreExisting: true}).then(() => {
|
||||
return CommonUtils.writeJSON(contents, this.path);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the local copy of the cache if there is one, otherwise it reads
|
||||
* it from the disk.
|
||||
*
|
||||
* @return {Promise} A promise that is resolved once the read is complete.
|
||||
*/
|
||||
_getCache: Task.async(function* () {
|
||||
if (this._cache) {
|
||||
return this._cache;
|
||||
}
|
||||
|
||||
try {
|
||||
return (this._cache = yield CommonUtils.readJSON(this.path));
|
||||
} catch(error) {
|
||||
// This is really complex due to OSFile's error handling, see bug 1160109.
|
||||
if ((OS.Constants.libc && error.unixErrno != OS.Constants.libc.ENOENT) ||
|
||||
(OS.Constants.Win && error.winLastError != OS.Constants.Win.ERROR_FILE_NOT_FOUND)) {
|
||||
MozLoopService.log.debug("Error reading the cache:", error);
|
||||
}
|
||||
return (this._cache = {});
|
||||
}
|
||||
}),
|
||||
|
||||
/**
|
||||
* Function for testability purposes. Clears the cache.
|
||||
*
|
||||
* @return {Promise} A promise that is resolved once the clear is complete.
|
||||
*/
|
||||
clear: function() {
|
||||
this._cache = null;
|
||||
return OS.File.remove(this.path)
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets a room key from the cache.
|
||||
*
|
||||
* @param {LOOP_SESSION_TYPE} sessionType The session type for the room.
|
||||
* @param {String} roomToken The token for the room.
|
||||
* @return {Promise} A promise that is resolved when the data has been read
|
||||
* with the value of the key, or null if it isn't present.
|
||||
*/
|
||||
getKey: Task.async(function* (sessionType, roomToken) {
|
||||
if (sessionType != LOOP_SESSION_TYPE.FXA) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let sessionData = (yield this._getCache())[sessionType];
|
||||
|
||||
if (!sessionData || !sessionData[roomToken]) {
|
||||
return null;
|
||||
}
|
||||
return sessionData[roomToken].key;
|
||||
}),
|
||||
|
||||
/**
|
||||
* Stores a room key into the cache. Note, if the key has not changed,
|
||||
* the store will not be re-written.
|
||||
*
|
||||
* @param {LOOP_SESSION_TYPE} sessionType The session type for the room.
|
||||
* @param {String} roomToken The token for the room.
|
||||
* @param {String} roomKey The encryption key for the room.
|
||||
* @return {Promise} A promise that is resolved when the data has been stored.
|
||||
*/
|
||||
setKey: Task.async(function* (sessionType, roomToken, roomKey) {
|
||||
if (sessionType != LOOP_SESSION_TYPE.FXA) {
|
||||
return;
|
||||
}
|
||||
|
||||
let cache = yield this._getCache();
|
||||
|
||||
// Create these objects if they don't exist.
|
||||
// We aim to do this creation and setting of the room key in a
|
||||
// forwards-compatible way so that if new fields are added to rooms later
|
||||
// then we don't mess them up (if there's no keys).
|
||||
if (!cache[sessionType]) {
|
||||
cache[sessionType] = {};
|
||||
}
|
||||
|
||||
if (!cache[sessionType][roomToken]) {
|
||||
cache[sessionType][roomToken] = {};
|
||||
}
|
||||
|
||||
// Only save it if there's no key, or it is different.
|
||||
if (!cache[sessionType][roomToken].key ||
|
||||
cache[sessionType][roomToken].key != roomKey) {
|
||||
cache[sessionType][roomToken].key = roomKey;
|
||||
return yield this._setCache(cache);
|
||||
}
|
||||
})
|
||||
};
|
|
@ -20,7 +20,6 @@ EXTRA_JS_MODULES.loop += [
|
|||
'modules/LoopCalls.jsm',
|
||||
'modules/LoopContacts.jsm',
|
||||
'modules/LoopRooms.jsm',
|
||||
'modules/LoopRoomsCache.jsm',
|
||||
'modules/LoopStorage.jsm',
|
||||
'modules/MozLoopAPI.jsm',
|
||||
'modules/MozLoopPushHandler.jsm',
|
||||
|
|
|
@ -5,9 +5,6 @@
|
|||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
// Initialize this before the imports, as some of them need it.
|
||||
do_get_profile();
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Http.jsm");
|
||||
|
@ -16,9 +13,7 @@ Cu.import("resource:///modules/loop/MozLoopService.jsm");
|
|||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
Cu.import("resource:///modules/loop/LoopCalls.jsm");
|
||||
Cu.import("resource:///modules/loop/LoopRooms.jsm");
|
||||
Cu.import("resource://gre/modules/osfile.jsm");
|
||||
const { MozLoopServiceInternal } = Cu.import("resource:///modules/loop/MozLoopService.jsm", {});
|
||||
const { LoopRoomsInternal } = Cu.import("resource:///modules/loop/LoopRooms.jsm", {});
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "MozLoopPushHandler",
|
||||
"resource:///modules/loop/MozLoopPushHandler.jsm");
|
||||
|
@ -216,10 +211,3 @@ MockWebSocketChannel.prototype = {
|
|||
this.listener.onServerClose(this.context, err || -1);
|
||||
},
|
||||
};
|
||||
|
||||
const extend = function(target, source) {
|
||||
for (let key of Object.getOwnPropertyNames(source)) {
|
||||
target[key] = source[key];
|
||||
}
|
||||
return target;
|
||||
};
|
||||
|
|
|
@ -11,6 +11,8 @@ Cu.import("resource://gre/modules/Promise.jsm");
|
|||
|
||||
let openChatOrig = Chat.open;
|
||||
|
||||
const kContextEnabledPref = "loop.contextInConverations.enabled";
|
||||
|
||||
const kGuestKey = "uGIs-kGbYt1hBBwjyW7MLQ";
|
||||
|
||||
// Rooms details as responded by the server.
|
||||
|
@ -182,6 +184,13 @@ const kCreateRoomData = {
|
|||
const kChannelGuest = MozLoopService.channelIDs.roomsGuest;
|
||||
const kChannelFxA = MozLoopService.channelIDs.roomsFxA;
|
||||
|
||||
const extend = function(target, source) {
|
||||
for (let key of Object.getOwnPropertyNames(source)) {
|
||||
target[key] = source[key];
|
||||
}
|
||||
return target;
|
||||
};
|
||||
|
||||
const normalizeRoom = function(room) {
|
||||
let newRoom = extend({}, room);
|
||||
let name = newRoom.decryptedContext.roomName;
|
||||
|
@ -290,10 +299,14 @@ add_task(function* setup_server() {
|
|||
let body = CommonUtils.readBytesFromInputStream(req.bodyInputStream);
|
||||
let data = JSON.parse(body);
|
||||
|
||||
Assert.equal(data.roomOwner, kCreateRoomProps.roomOwner);
|
||||
Assert.equal(data.maxSize, kCreateRoomProps.maxSize);
|
||||
Assert.ok(!("decryptedContext" in data), "should not have any decrypted data");
|
||||
Assert.ok("context" in data, "should have context");
|
||||
if (Services.prefs.getBoolPref(kContextEnabledPref)) {
|
||||
Assert.equal(data.roomOwner, kCreateRoomProps.roomOwner);
|
||||
Assert.equal(data.maxSize, kCreateRoomProps.maxSize);
|
||||
Assert.ok(!("decryptedContext" in data), "should not have any decrypted data");
|
||||
Assert.ok("context" in data, "should have context");
|
||||
} else {
|
||||
Assert.deepEqual(data, kCreateRoomUnencryptedProps);
|
||||
}
|
||||
|
||||
res.write(JSON.stringify(kCreateRoomData));
|
||||
} else {
|
||||
|
@ -340,11 +353,15 @@ add_task(function* setup_server() {
|
|||
res.finish();
|
||||
} else if (req.method == "PATCH") {
|
||||
let data = getJSONData(req.bodyInputStream);
|
||||
|
||||
Assert.ok("context" in data, "should have encrypted context");
|
||||
// We return a fake encrypted name here as the context is
|
||||
// encrypted.
|
||||
returnRoomDetails(res, "fakeEncrypted");
|
||||
if (Services.prefs.getBoolPref(kContextEnabledPref)) {
|
||||
Assert.ok("context" in data, "should have encrypted context");
|
||||
// We return a fake encrypted name here as the context is
|
||||
// encrypted.
|
||||
returnRoomDetails(res, "fakeEncrypted");
|
||||
} else {
|
||||
Assert.ok(!("context" in data), "should not have encrypted context");
|
||||
returnRoomDetails(res, data.roomName);
|
||||
}
|
||||
} else {
|
||||
roomDetail.context = room.context;
|
||||
res.setStatusLine(null, 200, "OK");
|
||||
|
@ -398,6 +415,20 @@ add_task(function* test_errorStates() {
|
|||
|
||||
// Test if creating a new room works as expected.
|
||||
add_task(function* test_createRoom() {
|
||||
Services.prefs.setBoolPref(kContextEnabledPref, true);
|
||||
|
||||
var expectedRoom = extend({}, kCreateRoomProps);
|
||||
expectedRoom.roomToken = kCreateRoomData.roomToken;
|
||||
|
||||
gExpectedAdds.push(expectedRoom);
|
||||
let room = yield LoopRooms.promise("create", kCreateRoomProps);
|
||||
compareRooms(room, expectedRoom);
|
||||
});
|
||||
|
||||
// XXX Test unencrypted rooms. This will go away once we switch encryption on.
|
||||
add_task(function* test_createRoom_unencrypted() {
|
||||
Services.prefs.setBoolPref(kContextEnabledPref, false);
|
||||
|
||||
var expectedRoom = extend({}, kCreateRoomProps);
|
||||
expectedRoom.roomToken = kCreateRoomData.roomToken;
|
||||
|
||||
|
@ -570,11 +601,19 @@ add_task(function* test_sendConnectionStatus() {
|
|||
|
||||
// Test if renaming a room works as expected.
|
||||
add_task(function* test_renameRoom() {
|
||||
Services.prefs.setBoolPref(kContextEnabledPref, true);
|
||||
let roomToken = "_nxD4V4FflQ";
|
||||
let renameData = yield LoopRooms.promise("rename", roomToken, "fakeName");
|
||||
Assert.equal(renameData.roomName, "fakeEncrypted", "should have set the new name");
|
||||
});
|
||||
|
||||
add_task(function* test_renameRoom_unencrpyted() {
|
||||
Services.prefs.setBoolPref(kContextEnabledPref, false);
|
||||
let roomToken = "_nxD4V4FflQ";
|
||||
let renameData = yield LoopRooms.promise("rename", roomToken, "fakeName");
|
||||
Assert.equal(renameData.roomName, "fakeName", "should have set the new name");
|
||||
});
|
||||
|
||||
add_task(function* test_roomDeleteNotifications() {
|
||||
gExpectedDeletes.push("_nxD4V4FflQ");
|
||||
roomsPushNotification("5", kChannelGuest);
|
||||
|
@ -618,6 +657,7 @@ function run_test() {
|
|||
do_register_cleanup(function () {
|
||||
// Revert original Chat.open implementation
|
||||
Chat.open = openChatOrig;
|
||||
Services.prefs.clearUserPref(kContextEnabledPref);
|
||||
Services.prefs.clearUserPref("loop.key");
|
||||
|
||||
MozLoopServiceInternal.fxAOAuthTokenData = null;
|
||||
|
|
|
@ -1,265 +0,0 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
Cu.import("resource://services-common/utils.js");
|
||||
const { LOOP_ROOMS_CACHE_FILENAME } = Cu.import("resource:///modules/loop/LoopRoomsCache.jsm", {});
|
||||
|
||||
const kFxAKey = "uGIs-kGbYt1hBBwjyW7MLQ";
|
||||
|
||||
// Rooms details as responded by the server.
|
||||
const kRoomsResponses = new Map([
|
||||
["_nxD4V4FflQ", {
|
||||
// Encrypted with roomKey "FliIGLUolW-xkKZVWstqKw".
|
||||
// roomKey is wrapped with kFxAKey.
|
||||
context: {
|
||||
wrappedKey: "F3V27oPB+FgjFbVPML2PupONYqoIZ53XRU4BqG46Lr3eyIGumgCEqgjSe/MXAXiQ//8=",
|
||||
value: "df7B4SNxhOI44eJjQavCevADyCCxz6/DEZbkOkRUMVUxzS42FbzN6C2PqmCKDYUGyCJTwJ0jln8TLw==",
|
||||
alg: "AES-GCM"
|
||||
},
|
||||
roomToken: "_nxD4V4FflQ",
|
||||
roomUrl: "http://localhost:3000/rooms/_nxD4V4FflQ"
|
||||
}],
|
||||
["QzBbvGmIZWU", {
|
||||
context: {
|
||||
wrappedKey: "AFu7WwFNjhWR5J6L8ks7S6H/1ktYVEw3yt1eIIWVaMabZaB3vh5612/FNzua4oS2oCM=",
|
||||
value: "sqj+xRNEty8K3Q1gSMd5bIUYKu34JfiO2+LIMlJrOetFIbJdBoQ+U8JZNaTFl6Qp3RULZ41x0zeSBSk=",
|
||||
alg: "AES-GCM"
|
||||
},
|
||||
roomToken: "QzBbvGmIZWU",
|
||||
roomUrl: "http://localhost:3000/rooms/QzBbvGmIZWU"
|
||||
}]
|
||||
]);
|
||||
|
||||
const kExpectedRooms = new Map([
|
||||
["_nxD4V4FflQ", {
|
||||
context: {
|
||||
wrappedKey: "F3V27oPB+FgjFbVPML2PupONYqoIZ53XRU4BqG46Lr3eyIGumgCEqgjSe/MXAXiQ//8=",
|
||||
value: "df7B4SNxhOI44eJjQavCevADyCCxz6/DEZbkOkRUMVUxzS42FbzN6C2PqmCKDYUGyCJTwJ0jln8TLw==",
|
||||
alg: "AES-GCM"
|
||||
},
|
||||
decryptedContext: {
|
||||
roomName: "First Room Name"
|
||||
},
|
||||
roomKey: "FliIGLUolW-xkKZVWstqKw",
|
||||
roomToken: "_nxD4V4FflQ",
|
||||
roomUrl: "http://localhost:3000/rooms/_nxD4V4FflQ#FliIGLUolW-xkKZVWstqKw"
|
||||
}],
|
||||
["QzBbvGmIZWU", {
|
||||
context: {
|
||||
wrappedKey: "AFu7WwFNjhWR5J6L8ks7S6H/1ktYVEw3yt1eIIWVaMabZaB3vh5612/FNzua4oS2oCM=",
|
||||
value: "sqj+xRNEty8K3Q1gSMd5bIUYKu34JfiO2+LIMlJrOetFIbJdBoQ+U8JZNaTFl6Qp3RULZ41x0zeSBSk=",
|
||||
alg: "AES-GCM"
|
||||
},
|
||||
decryptedContext: {
|
||||
roomName: "Loopy Discussion",
|
||||
},
|
||||
roomKey: "h2H8Sa9QxLCTTiXNmJVtRA",
|
||||
roomToken: "QzBbvGmIZWU",
|
||||
roomUrl: "http://localhost:3000/rooms/QzBbvGmIZWU"
|
||||
}]
|
||||
]);
|
||||
|
||||
const kCreateRoomProps = {
|
||||
decryptedContext: {
|
||||
roomName: "Say Hello",
|
||||
},
|
||||
roomOwner: "Gavin",
|
||||
maxSize: 2
|
||||
};
|
||||
|
||||
const kCreateRoomData = {
|
||||
roomToken: "Vo2BFQqIaAM",
|
||||
roomUrl: "http://localhost:3000/rooms/_nxD4V4FflQ",
|
||||
expiresAt: 1405534180
|
||||
};
|
||||
|
||||
function getCachePath() {
|
||||
return OS.Path.join(OS.Constants.Path.profileDir, LOOP_ROOMS_CACHE_FILENAME);
|
||||
}
|
||||
|
||||
function readRoomsCache() {
|
||||
return CommonUtils.readJSON(getCachePath());
|
||||
}
|
||||
|
||||
function saveRoomsCache(contents) {
|
||||
delete LoopRoomsInternal.roomsCache._cache;
|
||||
return CommonUtils.writeJSON(contents, getCachePath());
|
||||
}
|
||||
|
||||
function clearRoomsCache() {
|
||||
return LoopRoomsInternal.roomsCache.clear();
|
||||
}
|
||||
|
||||
// This is a cut-down version of the one in test_looprooms.js.
|
||||
add_task(function* setup_server() {
|
||||
loopServer.registerPathHandler("/registration", (req, res) => {
|
||||
res.setStatusLine(null, 200, "OK");
|
||||
res.processAsync();
|
||||
res.finish();
|
||||
});
|
||||
|
||||
loopServer.registerPathHandler("/rooms", (req, res) => {
|
||||
res.setStatusLine(null, 200, "OK");
|
||||
|
||||
if (req.method == "POST") {
|
||||
Assert.ok(req.bodyInputStream, "POST request should have a payload");
|
||||
let body = CommonUtils.readBytesFromInputStream(req.bodyInputStream);
|
||||
let data = JSON.parse(body);
|
||||
|
||||
Assert.ok(!("decryptedContext" in data), "should not have any decrypted data");
|
||||
Assert.ok("context" in data, "should have context");
|
||||
|
||||
res.write(JSON.stringify(kCreateRoomData));
|
||||
} else {
|
||||
res.write(JSON.stringify([...kRoomsResponses.values()]));
|
||||
}
|
||||
|
||||
res.processAsync();
|
||||
res.finish();
|
||||
});
|
||||
|
||||
function returnRoomDetails(res, roomName) {
|
||||
roomDetail.roomName = roomName;
|
||||
res.setStatusLine(null, 200, "OK");
|
||||
res.write(JSON.stringify(roomDetail));
|
||||
res.processAsync();
|
||||
res.finish();
|
||||
}
|
||||
|
||||
function getJSONData(body) {
|
||||
return JSON.parse(CommonUtils.readBytesFromInputStream(body));
|
||||
}
|
||||
|
||||
// Add a request handler for each room in the list.
|
||||
[...kRoomsResponses.values()].forEach(function(room) {
|
||||
loopServer.registerPathHandler("/rooms/" + encodeURIComponent(room.roomToken), (req, res) => {
|
||||
if (req.method == "POST") {
|
||||
let data = getJSONData(req.bodyInputStream);
|
||||
res.setStatusLine(null, 200, "OK");
|
||||
res.write(JSON.stringify(data));
|
||||
res.processAsync();
|
||||
res.finish();
|
||||
} else if (req.method == "PATCH") {
|
||||
let data = getJSONData(req.bodyInputStream);
|
||||
Assert.ok("context" in data, "should have encrypted context");
|
||||
// We return a fake encrypted name here as the context is
|
||||
// encrypted.
|
||||
returnRoomDetails(res, "fakeEncrypted");
|
||||
} else {
|
||||
res.setStatusLine(null, 200, "OK");
|
||||
res.write(JSON.stringify(room));
|
||||
res.processAsync();
|
||||
res.finish();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
loopServer.registerPathHandler("/rooms/error401", (req, res) => {
|
||||
res.setStatusLine(null, 401, "Not Found");
|
||||
res.processAsync();
|
||||
res.finish();
|
||||
});
|
||||
|
||||
loopServer.registerPathHandler("/rooms/errorMalformed", (req, res) => {
|
||||
res.setStatusLine(null, 200, "OK");
|
||||
res.write("{\"some\": \"Syntax Error!\"}}}}}}");
|
||||
res.processAsync();
|
||||
res.finish();
|
||||
});
|
||||
|
||||
mockPushHandler.registrationPushURL = kEndPointUrl;
|
||||
|
||||
yield MozLoopService.promiseRegisteredWithServers();
|
||||
});
|
||||
|
||||
|
||||
// Test if getting rooms saves unknown keys correctly.
|
||||
add_task(function* test_get_rooms_saves_unknown_keys() {
|
||||
let rooms = yield LoopRooms.promise("getAll");
|
||||
|
||||
// Check that we've saved the encryption keys correctly.
|
||||
let roomsCache = yield readRoomsCache();
|
||||
for (let room of [...kExpectedRooms.values()]) {
|
||||
if (room.context.wrappedKey) {
|
||||
Assert.equal(roomsCache[LOOP_SESSION_TYPE.FXA][room.roomToken].key, room.roomKey);
|
||||
}
|
||||
}
|
||||
|
||||
yield clearRoomsCache();
|
||||
});
|
||||
|
||||
// Test that when we get a room it updates the saved key if it is different.
|
||||
add_task(function* test_get_rooms_saves_different_keys() {
|
||||
let roomsCache = {};
|
||||
roomsCache[LOOP_SESSION_TYPE.FXA] = {
|
||||
QzBbvGmIZWU: {key: "fakeKey"}
|
||||
};
|
||||
yield saveRoomsCache(roomsCache);
|
||||
|
||||
const kRoomToken = "QzBbvGmIZWU";
|
||||
|
||||
let room = yield LoopRooms.promise("get", kRoomToken);
|
||||
|
||||
// Check that we've saved the encryption keys correctly.
|
||||
roomsCache = yield readRoomsCache();
|
||||
|
||||
Assert.notEqual(roomsCache[LOOP_SESSION_TYPE.FXA][kRoomToken].key, "fakeKey");
|
||||
Assert.equal(roomsCache[LOOP_SESSION_TYPE.FXA][kRoomToken].key, room.roomKey);
|
||||
|
||||
yield clearRoomsCache();
|
||||
});
|
||||
|
||||
// Test that if roomKey decryption fails, the saved key is used for decryption.
|
||||
add_task(function* test_get_rooms_uses_saved_key() {
|
||||
const kRoomToken = "_nxD4V4FflQ";
|
||||
const kExpected = kExpectedRooms.get(kRoomToken)
|
||||
|
||||
let roomsCache = {};
|
||||
roomsCache[LOOP_SESSION_TYPE.FXA] = {
|
||||
"_nxD4V4FflQ": {key: kExpected.roomKey}
|
||||
};
|
||||
yield saveRoomsCache(roomsCache);
|
||||
|
||||
// Change the encryption key for FxA, so that decoding the room key will break.
|
||||
Services.prefs.setCharPref("loop.key.fxa", "invalidKey");
|
||||
|
||||
let room = yield LoopRooms.promise("get", kRoomToken);
|
||||
|
||||
Assert.deepEqual(room, kExpected);
|
||||
|
||||
Services.prefs.setCharPref("loop.key.fxa", kFxAKey);
|
||||
yield clearRoomsCache();
|
||||
});
|
||||
|
||||
// Test that when a room is created the new key is saved.
|
||||
add_task(function* test_create_room_saves_key() {
|
||||
let room = yield LoopRooms.promise("create", kCreateRoomProps);
|
||||
|
||||
let roomsCache = yield readRoomsCache();
|
||||
|
||||
Assert.equal(roomsCache[LOOP_SESSION_TYPE.FXA][room.roomToken].key, room.roomKey);
|
||||
|
||||
yield clearRoomsCache();
|
||||
});
|
||||
|
||||
function run_test() {
|
||||
setupFakeLoopServer();
|
||||
|
||||
Services.prefs.setCharPref("loop.key.fxa", kFxAKey);
|
||||
|
||||
// Pretend we're signed into FxA.
|
||||
MozLoopServiceInternal.fxAOAuthTokenData = { token_type: "bearer" };
|
||||
MozLoopServiceInternal.fxAOAuthProfile = { email: "fake@invalid.com" };
|
||||
|
||||
do_register_cleanup(function () {
|
||||
Services.prefs.clearUserPref("loop.key.fxa");
|
||||
|
||||
MozLoopServiceInternal.fxAOAuthTokenData = null;
|
||||
MozLoopServiceInternal.fxAOAuthProfile = null;
|
||||
});
|
||||
|
||||
run_next_test();
|
||||
}
|
|
@ -7,7 +7,6 @@ skip-if = toolkit == 'gonk'
|
|||
[test_loopapi_hawk_request.js]
|
||||
[test_looppush_initialize.js]
|
||||
[test_looprooms.js]
|
||||
[test_looprooms_encryption_in_fxa.js]
|
||||
[test_loopservice_directcall.js]
|
||||
[test_loopservice_dnd.js]
|
||||
[test_loopservice_encryptionkey.js]
|
||||
|
|
Загрузка…
Ссылка в новой задаче