зеркало из https://github.com/mozilla/gecko-dev.git
Backed out changeset d94ef5bca70e (bug 1152761) for xpcshell failures.
This commit is contained in:
Родитель
2cb72a414d
Коммит
330f835697
|
@ -7,13 +7,11 @@ const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||||
|
|
||||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||||
Cu.import("resource://gre/modules/Services.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", {});
|
const {MozLoopService, LOOP_SESSION_TYPE} = Cu.import("resource:///modules/loop/MozLoopService.jsm", {});
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
|
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
|
||||||
"resource://gre/modules/Promise.jsm");
|
"resource://gre/modules/Promise.jsm");
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "CommonUtils",
|
XPCOMUtils.defineLazyModuleGetter(this, "Task",
|
||||||
"resource://services-common/utils.js");
|
"resource://gre/modules/Task.jsm");
|
||||||
XPCOMUtils.defineLazyGetter(this, "eventEmitter", function() {
|
XPCOMUtils.defineLazyGetter(this, "eventEmitter", function() {
|
||||||
const {EventEmitter} = Cu.import("resource://gre/modules/devtools/event-emitter.js", {});
|
const {EventEmitter} = Cu.import("resource://gre/modules/devtools/event-emitter.js", {});
|
||||||
return new EventEmitter();
|
return new EventEmitter();
|
||||||
|
@ -21,11 +19,8 @@ XPCOMUtils.defineLazyGetter(this, "eventEmitter", function() {
|
||||||
XPCOMUtils.defineLazyGetter(this, "gLoopBundle", function() {
|
XPCOMUtils.defineLazyGetter(this, "gLoopBundle", function() {
|
||||||
return Services.strings.createBundle('chrome://browser/locale/loop/loop.properties');
|
return Services.strings.createBundle('chrome://browser/locale/loop/loop.properties');
|
||||||
});
|
});
|
||||||
|
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "LoopRoomsCache",
|
|
||||||
"resource:///modules/loop/LoopRoomsCache.jsm");
|
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "loopUtils",
|
XPCOMUtils.defineLazyModuleGetter(this, "loopUtils",
|
||||||
"resource:///modules/loop/utils.js", "utils");
|
"resource:///modules/loop/utils.js", "utils")
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "loopCrypto",
|
XPCOMUtils.defineLazyModuleGetter(this, "loopCrypto",
|
||||||
"resource:///modules/loop/crypto.js", "LoopCrypto");
|
"resource:///modules/loop/crypto.js", "LoopCrypto");
|
||||||
|
|
||||||
|
@ -46,8 +41,6 @@ const roomsPushNotification = function(version, channelID) {
|
||||||
let gDirty = true;
|
let gDirty = true;
|
||||||
// Global variable that keeps track of the currently used account.
|
// Global variable that keeps track of the currently used account.
|
||||||
let gCurrentUser = null;
|
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`.
|
* Extend a `target` object with the properties defined in `source`.
|
||||||
|
@ -130,13 +123,6 @@ let LoopRoomsInternal = {
|
||||||
*/
|
*/
|
||||||
rooms: new Map(),
|
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'.
|
* @var {String} sessionType The type of user session. May be 'FXA' or 'GUEST'.
|
||||||
*/
|
*/
|
||||||
|
@ -294,40 +280,12 @@ let LoopRoomsInternal = {
|
||||||
throw new Error("Missing wrappedKey");
|
throw new Error("Missing wrappedKey");
|
||||||
}
|
}
|
||||||
|
|
||||||
let savedRoomKey = yield this.roomsCache.getKey(this.sessionType, roomData.roomToken);
|
// Bug 1152761 will cause us to additionally store keys locally. We'll
|
||||||
let fallback = false;
|
// need to add some code for recovery in case decryption fails.
|
||||||
let key;
|
let key = yield this.promiseDecryptRoomKey(roomData.context.wrappedKey);
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
let decryptedData = yield loopCrypto.decryptBytes(key, roomData.context.value);
|
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.roomKey = key;
|
||||||
roomData.decryptedContext = JSON.parse(decryptedData);
|
roomData.decryptedContext = JSON.parse(decryptedData);
|
||||||
|
|
||||||
|
@ -384,7 +342,7 @@ let LoopRoomsInternal = {
|
||||||
|
|
||||||
this.saveAndNotifyUpdate(roomData, isUpdate);
|
this.saveAndNotifyUpdate(roomData, isUpdate);
|
||||||
} catch (error) {
|
} 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.
|
// Do what we can to save the room data.
|
||||||
room.decryptedContext = {};
|
room.decryptedContext = {};
|
||||||
this.saveAndNotifyUpdate(room, isUpdate);
|
this.saveAndNotifyUpdate(room, isUpdate);
|
||||||
|
@ -537,9 +495,6 @@ let LoopRoomsInternal = {
|
||||||
this.setGuestCreatedRoom(true);
|
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);
|
eventEmitter.emit("add", room);
|
||||||
callback(null, room);
|
callback(null, room);
|
||||||
}.bind(this)).catch(callback);
|
}.bind(this)).catch(callback);
|
||||||
|
@ -749,10 +704,6 @@ let LoopRoomsInternal = {
|
||||||
sendData = {
|
sendData = {
|
||||||
roomName: newRoomName
|
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,
|
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/LoopCalls.jsm',
|
||||||
'modules/LoopContacts.jsm',
|
'modules/LoopContacts.jsm',
|
||||||
'modules/LoopRooms.jsm',
|
'modules/LoopRooms.jsm',
|
||||||
'modules/LoopRoomsCache.jsm',
|
|
||||||
'modules/LoopStorage.jsm',
|
'modules/LoopStorage.jsm',
|
||||||
'modules/MozLoopAPI.jsm',
|
'modules/MozLoopAPI.jsm',
|
||||||
'modules/MozLoopPushHandler.jsm',
|
'modules/MozLoopPushHandler.jsm',
|
||||||
|
|
|
@ -3,9 +3,6 @@
|
||||||
|
|
||||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
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/XPCOMUtils.jsm");
|
||||||
Cu.import("resource://gre/modules/Services.jsm");
|
Cu.import("resource://gre/modules/Services.jsm");
|
||||||
Cu.import("resource://gre/modules/Http.jsm");
|
Cu.import("resource://gre/modules/Http.jsm");
|
||||||
|
@ -14,9 +11,7 @@ Cu.import("resource:///modules/loop/MozLoopService.jsm");
|
||||||
Cu.import("resource://gre/modules/Promise.jsm");
|
Cu.import("resource://gre/modules/Promise.jsm");
|
||||||
Cu.import("resource:///modules/loop/LoopCalls.jsm");
|
Cu.import("resource:///modules/loop/LoopCalls.jsm");
|
||||||
Cu.import("resource:///modules/loop/LoopRooms.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 { MozLoopServiceInternal } = Cu.import("resource:///modules/loop/MozLoopService.jsm", {});
|
||||||
const { LoopRoomsInternal } = Cu.import("resource:///modules/loop/LoopRooms.jsm", {});
|
|
||||||
|
|
||||||
XPCOMUtils.defineLazyModuleGetter(this, "MozLoopPushHandler",
|
XPCOMUtils.defineLazyModuleGetter(this, "MozLoopPushHandler",
|
||||||
"resource:///modules/loop/MozLoopPushHandler.jsm");
|
"resource:///modules/loop/MozLoopPushHandler.jsm");
|
||||||
|
@ -214,10 +209,3 @@ MockWebSocketChannel.prototype = {
|
||||||
this.listener.onServerClose(this.context, err || -1);
|
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;
|
|
||||||
};
|
|
||||||
|
|
|
@ -182,6 +182,13 @@ const kCreateRoomData = {
|
||||||
const kChannelGuest = MozLoopService.channelIDs.roomsGuest;
|
const kChannelGuest = MozLoopService.channelIDs.roomsGuest;
|
||||||
const kChannelFxA = MozLoopService.channelIDs.roomsFxA;
|
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) {
|
const normalizeRoom = function(room) {
|
||||||
let newRoom = extend({}, room);
|
let newRoom = extend({}, room);
|
||||||
let name = newRoom.decryptedContext.roomName;
|
let name = newRoom.decryptedContext.roomName;
|
||||||
|
|
|
@ -1,267 +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/. */
|
|
||||||
|
|
||||||
Cu.import("resource://services-common/utils.js");
|
|
||||||
const { LOOP_ROOMS_CACHE_FILENAME } = Cu.import("resource:///modules/loop/LoopRoomsCache.jsm", {});
|
|
||||||
|
|
||||||
const kContextEnabledPref = "loop.contextInConverations.enabled";
|
|
||||||
|
|
||||||
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);
|
|
||||||
Services.prefs.setBoolPref(kContextEnabledPref, true);
|
|
||||||
|
|
||||||
// Pretend we're signed into FxA.
|
|
||||||
MozLoopServiceInternal.fxAOAuthTokenData = { token_type: "bearer" };
|
|
||||||
MozLoopServiceInternal.fxAOAuthProfile = { email: "fake@invalid.com" };
|
|
||||||
|
|
||||||
do_register_cleanup(function () {
|
|
||||||
Services.prefs.clearUserPref(kContextEnabledPref);
|
|
||||||
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_loopapi_hawk_request.js]
|
||||||
[test_looppush_initialize.js]
|
[test_looppush_initialize.js]
|
||||||
[test_looprooms.js]
|
[test_looprooms.js]
|
||||||
[test_looprooms_encryption_in_fxa.js]
|
|
||||||
[test_loopservice_directcall.js]
|
[test_loopservice_directcall.js]
|
||||||
[test_loopservice_dnd.js]
|
[test_loopservice_dnd.js]
|
||||||
[test_loopservice_encryptionkey.js]
|
[test_loopservice_encryptionkey.js]
|
||||||
|
|
Загрузка…
Ссылка в новой задаче