Merge services-central into mozilla-central

This commit is contained in:
Gregory Szorc 2012-03-30 11:51:02 -07:00
commit 7bf25cd84a
81 changed files with 1985 additions and 1617 deletions

View File

@ -83,7 +83,7 @@ let gSyncAddDevice = {
this.wizard.getButton("back").hidden = false;
this.wizard.getButton("next").hidden = true;
document.getElementById("weavePassphrase").value =
Weave.Utils.hyphenatePassphrase(Weave.Service.passphrase);
Weave.Utils.hyphenatePassphrase(Weave.Identity.syncKey);
break;
case DEVICE_CONNECTED_PAGE:
this.wizard.canAdvance = true;
@ -113,9 +113,9 @@ let gSyncAddDevice = {
let self = this;
let jpakeclient = this._jpakeclient = new Weave.JPAKEClient({
onPaired: function onPaired() {
let credentials = {account: Weave.Service.account,
password: Weave.Service.password,
synckey: Weave.Service.passphrase,
let credentials = {account: Weave.Identity.account,
password: Weave.Identity.basicPassword,
synckey: Weave.Identity.syncKey,
serverURL: Weave.Service.serverURL};
jpakeclient.sendAndComplete(credentials);
},

View File

@ -106,7 +106,7 @@ let Change = {
document.getElementById("generatePassphraseButton").hidden = false;
document.getElementById("passphraseBackupButtons").hidden = false;
this._passphraseBox.setAttribute("readonly", "true");
let pp = Weave.Service.passphrase;
let pp = Weave.Identity.syncKey;
if (Weave.Utils.isPassphrase(pp))
pp = Weave.Utils.hyphenatePassphrase(pp);
this._passphraseBox.value = pp;
@ -193,7 +193,7 @@ let Change = {
doChangePassphrase: function Change_doChangePassphrase() {
let pp = Weave.Utils.normalizePassphrase(this._passphraseBox.value);
if (this._updatingPassphrase) {
Weave.Service.passphrase = pp;
Weave.Identity.syncKey = pp;
if (Weave.Service.login()) {
this._updateStatus("change.recoverykey.success", "success");
Weave.Service.persistLogin();
@ -217,7 +217,7 @@ let Change = {
doChangePassword: function Change_doChangePassword() {
if (this._currentPasswordInvalid) {
Weave.Service.password = this._firstBox.value;
Weave.Identity.basicPassword = this._firstBox.value;
if (Weave.Service.login()) {
this._updateStatus("change.password.status.success", "success");
Weave.Service.persistLogin();

View File

@ -179,13 +179,13 @@ var gSyncSetup = {
resetPassphrase: function resetPassphrase() {
// Apply the existing form fields so that
// Weave.Service.changePassphrase() has the necessary credentials.
Weave.Service.account = document.getElementById("existingAccountName").value;
Weave.Service.password = document.getElementById("existingPassword").value;
Weave.Identity.account = document.getElementById("existingAccountName").value;
Weave.Identity.basicPassword = document.getElementById("existingPassword").value;
// Generate a new passphrase so that Weave.Service.login() will
// actually do something.
let passphrase = Weave.Utils.generatePassphrase();
Weave.Service.passphrase = passphrase;
Weave.Identity.syncKey = passphrase;
// Only open the dialog if username + password are actually correct.
Weave.Service.login();
@ -209,8 +209,8 @@ var gSyncSetup = {
},
onResetPassphrase: function () {
document.getElementById("existingPassphrase").value =
Weave.Utils.hyphenatePassphrase(Weave.Service.passphrase);
document.getElementById("existingPassphrase").value =
Weave.Utils.hyphenatePassphrase(Weave.Identity.syncKey);
this.checkFields();
this.wizard.advance();
},
@ -227,9 +227,9 @@ var gSyncSetup = {
let send = function() {
Services.obs.removeObserver("weave:service:sync:finish", send);
Services.obs.removeObserver("weave:service:sync:error", send);
let credentials = {account: Weave.Service.account,
password: Weave.Service.password,
synckey: Weave.Service.passphrase,
let credentials = {account: Weave.Identity.account,
password: Weave.Identity.basicPassword,
synckey: Weave.Identity.syncKey,
serverURL: Weave.Service.serverURL};
this._jpakeclient.sendAndComplete(credentials);
}.bind(this);
@ -368,7 +368,7 @@ var gSyncSetup = {
this._setFeedbackMessage(feedback, valid, str);
this.status.email = valid;
if (valid)
Weave.Service.account = value;
Weave.Identity.account = value;
this.checkFields();
},
@ -504,9 +504,9 @@ var gSyncSetup = {
challenge, response);
if (error == null) {
Weave.Service.account = email;
Weave.Service.password = password;
Weave.Service.passphrase = Weave.Utils.generatePassphrase();
Weave.Identity.account = email;
Weave.Identity.basicPassword = password;
Weave.Identity.syncKey = Weave.Utils.generatePassphrase();
this._handleNoScript(false);
Weave.Svc.Prefs.set("firstSync", "newAccount");
this.wizardFinish();
@ -517,11 +517,12 @@ var gSyncSetup = {
label.value = Weave.Utils.getErrorString(error);
return false;
case EXISTING_ACCOUNT_LOGIN_PAGE:
Weave.Service.account = Weave.Utils.normalizeAccount(
Weave.Identity.account = Weave.Utils.normalizeAccount(
document.getElementById("existingAccountName").value);
Weave.Service.password = document.getElementById("existingPassword").value;
Weave.Identity.basicPassword =
document.getElementById("existingPassword").value;
let pp = document.getElementById("existingPassphrase").value;
Weave.Service.passphrase = Weave.Utils.normalizePassphrase(pp);
Weave.Identity.syncKey = Weave.Utils.normalizePassphrase(pp);
if (Weave.Service.login()) {
this.wizardFinish();
}
@ -700,9 +701,9 @@ var gSyncSetup = {
onPairingStart: function onPairingStart() {},
onComplete: function onComplete(credentials) {
Weave.Service.account = credentials.account;
Weave.Service.password = credentials.password;
Weave.Service.passphrase = credentials.synckey;
Weave.Identity.account = credentials.account;
Weave.Identity.basicPassword = credentials.password;
Weave.Identity.syncKey = credentials.synckey;
Weave.Service.serverURL = credentials.serverURL;
gSyncSetup.wizardFinish();
},

View File

@ -227,13 +227,13 @@ let gSyncUtils = {
if (!el2)
valid = val1.length >= Weave.MIN_PASS_LENGTH;
else if (val1 && val1 == Weave.Service.username)
else if (val1 && val1 == Weave.Identity.username)
error = "change.password.pwSameAsUsername";
else if (val1 && val1 == Weave.Service.account)
else if (val1 && val1 == Weave.Identity.account)
error = "change.password.pwSameAsEmail";
else if (val1 && val1 == Weave.Service.password)
else if (val1 && val1 == Weave.Identity.basicPassword)
error = "change.password.pwSameAsPassword";
else if (val1 && val1 == Weave.Service.passphrase)
else if (val1 && val1 == Weave.Identity.syncKey)
error = "change.password.pwSameAsRecoveryKey";
else if (val1 && val2) {
if (val1 == val2 && val1.length >= Weave.MIN_PASS_LENGTH)

View File

@ -101,7 +101,7 @@ let gSyncPane = {
this.needsUpdate();
} else {
this.page = PAGE_HAS_ACCOUNT;
document.getElementById("accountName").value = Weave.Service.account;
document.getElementById("accountName").value = Weave.Identity.account;
document.getElementById("syncComputerName").value = Weave.Clients.localName;
document.getElementById("tosPP").hidden = this._usingCustomServer;
}

View File

@ -335,7 +335,7 @@ let WeaveGlue = {
// Now try to re-connect. If successful, this will reset the UI into the
// correct state automatically.
Weave.Service.login(Weave.Service.username, this.setupData.password, this.setupData.synckey);
Weave.Service.login(Weave.Identity.username, this.setupData.password, this.setupData.synckey);
},
connect: function connect(aSetupData) {
@ -344,7 +344,7 @@ let WeaveGlue = {
this.setupData = aSetupData;
// Cause the Sync system to reset internals if we seem to be switching accounts
if (this.setupData.account != Weave.Service.account)
if (this.setupData.account != Weave.Identity.account)
Weave.Service.startOver();
// Remove any leftover connection error string
@ -355,9 +355,9 @@ let WeaveGlue = {
Weave.Service.serverURL = this.setupData.serverURL;
// Sync will use the account value and munge it into a username, as needed
Weave.Service.account = this.setupData.account;
Weave.Service.password = this.setupData.password;
Weave.Service.passphrase = this.setupData.synckey;
Weave.Identity.account = this.setupData.account;
Weave.Identity.basicPassword = this.setupData.password;
Weave.Identity.syncKey = this.setupData.synckey;
Weave.Service.persistLogin();
Weave.Svc.Obs.notify("weave:service:setup-complete");
setTimeout(function () { Weave.Service.sync(); }, 0);
@ -505,7 +505,7 @@ let WeaveGlue = {
}, 0, this);
// Dynamically generate some strings
let accountStr = this._bundle.formatStringFromName("account.label", [Weave.Service.account], 1);
let accountStr = this._bundle.formatStringFromName("account.label", [Weave.Identity.account], 1);
disconnect.setAttribute("title", accountStr);
// Show the day-of-week and time (HH:MM) of last sync
@ -589,9 +589,9 @@ let WeaveGlue = {
loadSetupData: function _loadSetupData() {
this.setupData = {};
this.setupData.account = Weave.Service.account || "";
this.setupData.password = Weave.Service.password || "";
this.setupData.synckey = Weave.Service.passphrase || "";
this.setupData.account = Weave.Identity.account || "";
this.setupData.password = Weave.Identity.basicPassword || "";
this.setupData.synckey = Weave.Identity.syncKey || "";
let serverURL = Weave.Service.serverURL;
let defaultPrefs = Services.prefs.getDefaultBranch(null);
@ -652,9 +652,9 @@ let SyncPairDevice = {
let self = this;
let jpake = this.jpake = new Weave.JPAKEClient({
onPaired: function onPaired() {
let credentials = {account: Weave.Service.account,
password: Weave.Service.password,
synckey: Weave.Service.passphrase,
let credentials = {account: Weave.Identity.account,
password: Weave.Identity.basicPassword,
synckey: Weave.Identity.syncKey,
serverURL: Weave.Service.serverURL};
jpake.sendAndComplete(credentials);
},

View File

@ -573,7 +573,7 @@ SyncEngine.prototype = {
applyIncomingBatchSize: DEFAULT_STORE_BATCH_SIZE,
get storageURL() Svc.Prefs.get("clusterURL") + SYNC_API_VERSION +
"/" + ID.get("WeaveID").username + "/storage/",
"/" + Identity.username + "/storage/",
get engineURL() this.storageURL + this.name,

View File

@ -808,6 +808,11 @@ BookmarksStore.prototype = {
this.removeById(itemId, record.id);
},
_taggableTypes: ["bookmark", "microsummary", "query"],
isTaggable: function isTaggable(recordType) {
return this._taggableTypes.indexOf(recordType) != -1;
},
update: function BStore_update(record) {
let itemId = this.idForGUID(record.id);
@ -853,7 +858,11 @@ BookmarksStore.prototype = {
break;
case "tags":
if (Array.isArray(val)) {
this._tagURI(PlacesUtils.bookmarks.getBookmarkURI(itemId), val);
if (this.isTaggable(remoteRecordType)) {
this._tagID(itemId, val);
} else {
this._log.debug("Remote record type is invalid for tags: " + remoteRecordType);
}
}
break;
case "keyword":
@ -1217,15 +1226,46 @@ BookmarksStore.prototype = {
return items;
},
_tagURI: function BStore_tagURI(bmkURI, tags) {
/**
* Associates the URI of the item with the provided ID with the
* provided array of tags.
* If the provided ID does not identify an item with a URI,
* returns immediately.
*/
_tagID: function _tagID(itemID, tags) {
if (!itemID || !tags) {
return;
}
try {
let u = PlacesUtils.bookmarks.getBookmarkURI(itemID);
this._tagURI(u, tags);
} catch (e) {
this._log.warn("Got exception fetching URI for " + itemID + ": not tagging. " +
Utils.exceptionStr(e));
// I guess it doesn't have a URI. Don't try to tag it.
return;
}
},
/**
* Associate the provided URI with the provided array of tags.
* If the provided URI is falsy, returns immediately.
*/
_tagURI: function _tagURI(bookmarkURI, tags) {
if (!bookmarkURI || !tags) {
return;
}
// Filter out any null/undefined/empty tags.
tags = tags.filter(function(t) t);
// Temporarily tag a dummy URI to preserve tag ids when untagging.
let dummyURI = Utils.makeURI("about:weave#BStore_tagURI");
PlacesUtils.tagging.tagURI(dummyURI, tags);
PlacesUtils.tagging.untagURI(bmkURI, null);
PlacesUtils.tagging.tagURI(bmkURI, tags);
PlacesUtils.tagging.untagURI(bookmarkURI, null);
PlacesUtils.tagging.tagURI(bookmarkURI, tags);
PlacesUtils.tagging.untagURI(dummyURI, null);
},

View File

@ -200,7 +200,7 @@ ClientEngine.prototype = {
wipeAll: { args: 0, desc: "Delete all client data for all engines" },
wipeEngine: { args: 1, desc: "Delete all client data for engine" },
logout: { args: 0, desc: "Log out client" },
displayURI: { args: 2, desc: "Instruct a client to display a URI" }
displayURI: { args: 3, desc: "Instruct a client to display a URI" },
},
/**
@ -286,7 +286,7 @@ ClientEngine.prototype = {
Weave.Service.logout();
return false;
case "displayURI":
this._handleDisplayURI(args[0], args[1]);
this._handleDisplayURI.apply(this, args);
break;
default:
this._log.debug("Received an unknown command: " + command);
@ -350,10 +350,13 @@ ClientEngine.prototype = {
* @param clientId
* ID of client to send the command to. If not defined, will be sent
* to all remote clients.
* @param title
* Title of the page being sent.
*/
sendURIToClientForDisplay: function sendURIToClientForDisplay(uri, clientId) {
this._log.info("Sending URI to client: " + uri + " -> " + clientId);
this.sendCommand("displayURI", [uri, this.syncID], clientId);
sendURIToClientForDisplay: function sendURIToClientForDisplay(uri, clientId, title) {
this._log.info("Sending URI to client: " + uri + " -> " +
clientId + " (" + title + ")");
this.sendCommand("displayURI", [uri, this.localID, title], clientId);
Clients._tracker.score += SCORE_INCREMENT_XLARGE;
},
@ -365,8 +368,9 @@ ClientEngine.prototype = {
* topic. The callback will receive an object as the subject parameter with
* the following keys:
*
* uri URI (string) that is requested for display
* clientId ID of client that sent the command
* uri URI (string) that is requested for display.
* clientId ID of client that sent the command.
* title Title of page that loaded URI (likely) corresponds to.
*
* The 'data' parameter to the callback will not be defined.
*
@ -374,11 +378,15 @@ ClientEngine.prototype = {
* String URI that was received
* @param clientId
* ID of client that sent URI
* @param title
* String title of page that URI corresponds to. Older clients may not
* send this.
*/
_handleDisplayURI: function _handleDisplayURI(uri, clientId) {
this._log.info("Received a URI for display: " + uri + " from " + clientId);
_handleDisplayURI: function _handleDisplayURI(uri, clientId, title) {
this._log.info("Received a URI for display: " + uri + " (" + title +
") from " + clientId);
let subject = { uri: uri, client: clientId };
let subject = {uri: uri, client: clientId, title: title};
Svc.Obs.notify("weave:engine:clients:display-uri", subject);
}
};

View File

@ -139,12 +139,28 @@ TabStore.prototype = {
return id == Clients.localID;
},
/**
* Return the recorded last used time of the provided tab, or
* 0 if none is present.
* The result will always be an integer value.
*/
tabLastUsed: function tabLastUsed(tab) {
// weaveLastUsed will only be set if the tab was ever selected (or
// opened after Sync was running).
let weaveLastUsed = tab.extData && tab.extData.weaveLastUsed;
if (!weaveLastUsed) {
return 0;
}
return parseInt(weaveLastUsed, 10) || 0;
},
getAllTabs: function getAllTabs(filter) {
let filteredUrls = new RegExp(Svc.Prefs.get("engine.tabs.filteredUrls"), "i");
let allTabs = [];
let currentState = JSON.parse(Svc.Session.getBrowserState());
let tabLastUsed = this.tabLastUsed;
currentState.windows.forEach(function(window) {
window.tabs.forEach(function(tab) {
// Make sure there are history entries to look at.
@ -159,15 +175,13 @@ TabStore.prototype = {
if (!entry.url || filter && filteredUrls.test(entry.url))
return;
// weaveLastUsed will only be set if the tab was ever selected (or
// opened after Weave was running). So it might not ever be set.
// I think it's also possible that attributes[.image] might not be set
// so handle that as well.
allTabs.push({
title: entry.title || "",
urlHistory: [entry.url],
icon: tab.attributes && tab.attributes.image || "",
lastUsed: tab.extData && tab.extData.weaveLastUsed || 0
lastUsed: tabLastUsed(tab)
});
});
});

View File

@ -1,143 +1,491 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Bookmarks Sync.
*
* The Initial Developer of the Original Code is Mozilla.
* Portions created by the Initial Developer are Copyright (C) 2007
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Dan Mills <thunder@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/* 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/. */
const EXPORTED_SYMBOLS = ['Identity', 'ID'];
"use strict";
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
const Cu = Components.utils;
const EXPORTED_SYMBOLS = ["Identity", "IdentityManager"];
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://services-sync/constants.js");
Cu.import("resource://services-sync/keys.js");
Cu.import("resource://services-sync/log4moz.js");
Cu.import("resource://services-sync/util.js");
// Avoid circular import.
__defineGetter__("Service", function() {
delete this.Service;
Cu.import("resource://services-sync/service.js", this);
return this.Service;
XPCOMUtils.defineLazyGetter(this, "Identity", function() {
return new IdentityManager();
});
XPCOMUtils.defineLazyGetter(this, "ID", function () {
return new IDManager();
});
// For storing identities we'll use throughout Weave
function IDManager() {
this._ids = {};
}
IDManager.prototype = {
get: function IDMgr_get(name) {
if (name in this._ids)
return this._ids[name];
return null;
},
set: function IDMgr_set(name, id) {
this._ids[name] = id;
return id;
},
del: function IDMgr_del(name) {
delete this._ids[name];
}
};
/*
* Identity
* These objects hold a realm, username, and password
* They can hold a password in memory, but will try to fetch it from
* the password manager if it's not set.
* FIXME: need to rethink this stuff as part of a bigger identity mgmt framework
/**
* Manages identity and authentication for Sync.
*
* The following entities are managed:
*
* account - The main Sync/services account. This is typically an email
* address.
* username - A normalized version of your account. This is what's
* transmitted to the server.
* basic password - UTF-8 password used for authenticating when using HTTP
* basic authentication.
* sync key - The main encryption key used by Sync.
* sync key bundle - A representation of your sync key.
*
* An instance of this type is lazily instantiated under Weave.Identity. It is
* and should be treated as a global variable. The reason is that saved changes
* are stored in preferences and the password manager. So, if you created
* multiple instances, they would just step on each other's state.
*
* When changes are made to entities that are stored in the password manager
* (basic password, sync key), those changes are merely staged. To commit them
* to the password manager, you'll need to call persistCredentials().
*
* This type also manages authenticating Sync's network requests. Sync's
* network code calls into getRESTRequestAuthenticator and
* getResourceAuthenticator (depending on the network layer being used). Each
* returns a function which can be used to add authentication information to an
* outgoing request.
*
* In theory, this type supports arbitrary identity and authentication
* mechanisms. You can add support for them by monkeypatching the global
* instance of this type. Specifically, you'll need to redefine the
* aforementioned network code functions to do whatever your authentication
* mechanism needs them to do. In addition, you may wish to install custom
* functions to support your API. Although, that is certainly not required.
* If you do monkeypatch, please be advised that Sync expects the core
* attributes to have values. You will need to carry at least account and
* username forward. If you do not wish to support one of the built-in
* authentication mechanisms, you'll probably want to redefine currentAuthState
* and any other function that involves the built-in functionality.
*/
function IdentityManager() {
this._log = Log4Moz.repository.getLogger("Sync.Identity");
this._log.Level = Log4Moz.Level[Svc.Prefs.get("log.logger.identity")];
function Identity(realm, username, password) {
this.realm = realm;
this.username = username;
this._password = password;
if (password)
this._passwordUTF8 = Utils.encodeUTF8(password);
this._basicPassword = null;
this._basicPasswordAllowLookup = true;
this._basicPasswordUpdated = false;
this._syncKey = null;
this._syncKeyAllowLookup = true;
this._syncKeySet = false;
this._syncKeyBundle = null;
}
Identity.prototype = {
get password() {
// Look up the password then cache it
if (this._password == null)
for each (let login in this._logins)
if (login.username.toLowerCase() == this.username) {
this._password = login.password;
this._passwordUTF8 = Utils.encodeUTF8(login.password);
}
return this._password;
IdentityManager.prototype = {
_log: null,
_basicPassword: null,
_basicPasswordAllowLookup: true,
_basicPasswordUpdated: false,
_syncKey: null,
_syncKeyAllowLookup: true,
_syncKeySet: false,
_syncKeyBundle: null,
get account() {
return Svc.Prefs.get("account", this.username);
},
set password(value) {
this._password = value;
this._passwordUTF8 = Utils.encodeUTF8(value);
},
get passwordUTF8() {
if (!this._passwordUTF8)
this.password; // invoke password getter
return this._passwordUTF8;
},
persist: function persist() {
// Clean up any existing passwords unless it's what we're persisting
let exists = false;
for each (let login in this._logins) {
if (login.username == this.username && login.password == this._password)
exists = true;
else
Services.logins.removeLogin(login);
/**
* Sets the active account name.
*
* This should almost always be called in favor of setting username, as
* username is derived from account.
*
* Changing the account name has the side-effect of wiping out stored
* credentials. Keep in mind that persistCredentials() will need to be called
* to flush the changes to disk.
*
* Set this value to null to clear out identity information.
*/
set account(value) {
if (value) {
value = value.toLowerCase();
Svc.Prefs.set("account", value);
} else {
Svc.Prefs.reset("account");
}
// No need to create the login after clearing out the other ones
let log = Log4Moz.repository.getLogger("Sync.Identity");
if (exists) {
log.trace("Skipping persist: " + this.realm + " for " + this.username);
this.username = this.usernameFromAccount(value);
},
get username() {
return Svc.Prefs.get("username", null);
},
/**
* Set the username value.
*
* Changing the username has the side-effect of wiping credentials.
*/
set username(value) {
if (value) {
value = value.toLowerCase();
if (value == this.username) {
return;
}
Svc.Prefs.set("username", value);
} else {
Svc.Prefs.reset("username");
}
// If we change the username, we interpret this as a major change event
// and wipe out the credentials.
this._log.info("Username changed. Removing stored credentials.");
this.basicPassword = null;
this.syncKey = null;
// syncKeyBundle cleared as a result of setting syncKey.
},
/**
* Obtains the HTTP Basic auth password.
*
* Returns a string if set or null if it is not set.
*/
get basicPassword() {
if (this._basicPasswordAllowLookup) {
// We need a username to find the credentials.
let username = this.username;
if (!username) {
return null;
}
for each (let login in this._getLogins(PWDMGR_PASSWORD_REALM)) {
if (login.username.toLowerCase() == username) {
// It should already be UTF-8 encoded, but we don't take any chances.
this._basicPassword = Utils.encodeUTF8(login.password);
}
}
this._basicPasswordAllowLookup = false;
}
return this._basicPassword;
},
/**
* Set the HTTP basic password to use.
*
* Changes will not persist unless persistSyncCredentials() is called.
*/
set basicPassword(value) {
// Wiping out value.
if (!value) {
this._log.info("Basic password has no value. Removing.");
this._basicPassword = null;
this._basicPasswordUpdated = true;
this._basicPasswordAllowLookup = false;
return;
}
// Add the new username/password
log.trace("Persisting " + this.realm + " for " + this.username);
let nsLoginInfo = new Components.Constructor(
"@mozilla.org/login-manager/loginInfo;1", Ci.nsILoginInfo, "init");
let newLogin = new nsLoginInfo(PWDMGR_HOST, null, this.realm,
this.username, this.password, "", "");
Services.logins.addLogin(newLogin);
let username = this.username;
if (!username) {
throw new Error("basicPassword cannot be set before username.");
}
this._log.info("Basic password being updated.");
this._basicPassword = Utils.encodeUTF8(value);
this._basicPasswordUpdated = true;
},
get _logins() Services.logins.findLogins({}, PWDMGR_HOST, null, this.realm)
/**
* Obtain the Sync Key.
*
* This returns a 26 character "friendly" Base32 encoded string on success or
* null if no Sync Key could be found.
*
* If the Sync Key hasn't been set in this session, this will look in the
* password manager for the sync key.
*/
get syncKey() {
if (this._syncKeyAllowLookup) {
let username = this.username;
if (!username) {
return null;
}
for each (let login in this._getLogins(PWDMGR_PASSPHRASE_REALM)) {
if (login.username.toLowerCase() == username) {
this._syncKey = login.password;
}
}
this._syncKeyAllowLookup = false;
}
return this._syncKey;
},
/**
* Set the active Sync Key.
*
* If being set to null, the Sync Key and its derived SyncKeyBundle are
* removed. However, the Sync Key won't be deleted from the password manager
* until persistSyncCredentials() is called.
*
* If a value is provided, it should be a 26 or 32 character "friendly"
* Base32 string for which Utils.isPassphrase() returns true.
*
* A side-effect of setting the Sync Key is that a SyncKeyBundle is
* generated. For historical reasons, this will silently error out if the
* value is not a proper Sync Key (!Utils.isPassphrase()). This should be
* fixed in the future (once service.js is more sane) to throw if the passed
* value is not valid.
*/
set syncKey(value) {
if (!value) {
this._log.info("Sync Key has no value. Deleting.");
this._syncKey = null;
this._syncKeyBundle = null;
this._syncKeyUpdated = true;
return;
}
if (!this.username) {
throw new Error("syncKey cannot be set before username.");
}
this._log.info("Sync Key being updated.");
this._syncKey = value;
// Calling the getter has the side-effect of populating the object, which
// we desire.
let bundle = this.syncKeyBundle;
this._syncKeyUpdated = true;
},
/**
* Obtain the active SyncKeyBundle.
*
* This returns a SyncKeyBundle representing a key pair derived from the
* Sync Key on success. If no Sync Key is present or if the Sync Key is not
* valid, this returns null.
*
* The SyncKeyBundle should be treated as immutable.
*/
get syncKeyBundle() {
// We can't obtain a bundle without a username set.
if (!this.username) {
this._log.warn("Attempted to obtain Sync Key Bundle with no username set!");
return null;
}
if (!this.syncKey) {
this._log.warn("Attempted to obtain Sync Key Bundle with no Sync Key " +
"set!");
return null;
}
if (!this._syncKeyBundle) {
try {
this._syncKeyBundle = new SyncKeyBundle(this.username, this.syncKey);
} catch (ex) {
this._log.warn(Utils.exceptionStr(ex));
return null;
}
}
return this._syncKeyBundle;
},
/**
* The current state of the auth credentials.
*
* This essentially validates that enough credentials are available to use
* Sync.
*/
get currentAuthState() {
if (!this.username) {
return LOGIN_FAILED_NO_USERNAME;
}
if (Utils.mpLocked()) {
return STATUS_OK;
}
if (!this.basicPassword) {
return LOGIN_FAILED_NO_PASSWORD;
}
if (!this.syncKey) {
return LOGIN_FAILED_NO_PASSPHRASE;
}
return STATUS_OK;
},
/**
* Persist credentials to password store.
*
* When credentials are updated, they are changed in memory only. This will
* need to be called to save them to the underlying password store.
*
* If the password store is locked (e.g. if the master password hasn't been
* entered), this could throw an exception.
*/
persistCredentials: function persistCredentials() {
if (this._basicPasswordUpdated) {
if (this._basicPassword) {
this._setLogin(PWDMGR_PASSWORD_REALM, this.username,
this._basicPassword);
} else {
for each (let login in this._getLogins(PWDMGR_PASSWORD_REALM)) {
Services.logins.removeLogin(login);
}
}
this._basicPasswordUpdated = false;
}
if (this._syncKeyUpdated) {
if (this._syncKey) {
this._setLogin(PWDMGR_PASSPHRASE_REALM, this.username, this._syncKey);
} else {
for each (let login in this._getLogins(PWDMGR_PASSPHRASE_REALM)) {
Services.logins.removeLogin(login);
}
}
this._syncKeyUpdated = false;
}
},
/**
* Deletes the Sync Key from the system.
*/
deleteSyncKey: function deleteSyncKey() {
this.syncKey = null;
this.persistCredentials();
},
hasBasicCredentials: function hasBasicCredentials() {
// Because JavaScript.
return this.username && this.basicPassword && true;
},
/**
* Obtains the array of basic logins from nsiPasswordManager.
*/
_getLogins: function _getLogins(realm) {
return Services.logins.findLogins({}, PWDMGR_HOST, null, realm);
},
/**
* Set a login in the password manager.
*
* This has the side-effect of deleting any other logins for the specified
* realm.
*/
_setLogin: function _setLogin(realm, username, password) {
let exists = false;
for each (let login in this._getLogins(realm)) {
if (login.username == username && login.password == password) {
exists = true;
} else {
this._log.debug("Pruning old login for " + username + " from " + realm);
Services.logins.removeLogin(login);
}
}
if (exists) {
return;
}
this._log.debug("Updating saved password for " + username + " in " +
realm);
let loginInfo = new Components.Constructor(
"@mozilla.org/login-manager/loginInfo;1", Ci.nsILoginInfo, "init");
let login = new loginInfo(PWDMGR_HOST, null, realm, username,
password, "", "");
Services.logins.addLogin(login);
},
/**
* Deletes Sync credentials from the password manager.
*/
deleteSyncCredentials: function deleteSyncCredentials() {
let logins = Services.logins.findLogins({}, PWDMGR_HOST, "", "");
for each (let login in logins) {
Services.logins.removeLogin(login);
}
// Wait until after store is updated in case it fails.
this._basicPassword = null;
this._basicPasswordAllowLookup = true;
this._basicPasswordUpdated = false;
this._syncKey = null;
// this._syncKeyBundle is nullified as part of _syncKey setter.
this._syncKeyAllowLookup = true;
this._syncKeyUpdated = false;
},
usernameFromAccount: function usernameFromAccount(value) {
// If we encounter characters not allowed by the API (as found for
// instance in an email address), hash the value.
if (value && value.match(/[^A-Z0-9._-]/i)) {
return Utils.sha1Base32(value.toLowerCase()).toLowerCase();
}
return value ? value.toLowerCase() : value;
},
/**
* Obtain a function to be used for adding auth to Resource HTTP requests.
*/
getResourceAuthenticator: function getResourceAuthenticator() {
if (this.hasBasicCredentials()) {
return this._onResourceRequestBasic.bind(this);
}
return null;
},
/**
* Helper method to return an authenticator for basic Resource requests.
*/
getBasicResourceAuthenticator:
function getBasicResourceAuthenticator(username, password) {
return function basicAuthenticator(resource) {
let value = "Basic " + btoa(username + ":" + password);
return {headers: {authorization: value}};
};
},
_onResourceRequestBasic: function _onResourceRequestBasic(resource) {
let value = "Basic " + btoa(this.username + ":" + this.basicPassword);
return {headers: {authorization: value}};
},
_onResourceRequestMAC: function _onResourceRequestMAC(resource, method) {
// TODO Get identifier and key from somewhere.
let identifier;
let key;
let result = Utils.computeHTTPMACSHA1(identifier, key, method, resource.uri);
return {headers: {authorization: result.header}};
},
/**
* Obtain a function to be used for adding auth to RESTRequest instances.
*/
getRESTRequestAuthenticator: function getRESTRequestAuthenticator() {
if (this.hasBasicCredentials()) {
return this.onRESTRequestBasic.bind(this);
}
return null;
},
onRESTRequestBasic: function onRESTRequestBasic(request) {
let up = this.username + ":" + this.basicPassword;
request.setHeader("authorization", "Basic " + btoa(up));
}
};

View File

@ -0,0 +1,214 @@
/* 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 EXPORTED_SYMBOLS = [
"BulkKeyBundle",
"SyncKeyBundle"
];
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://services-sync/constants.js");
Cu.import("resource://services-sync/log4moz.js");
Cu.import("resource://services-sync/util.js");
/**
* Represents a pair of keys.
*
* Each key stored in a key bundle is 256 bits. One key is used for symmetric
* encryption. The other is used for HMAC.
*
* A KeyBundle by itself is just an anonymous pair of keys. Other types
* deriving from this one add semantics, such as associated collections or
* generating a key bundle via HKDF from another key.
*/
function KeyBundle() {
this._encrypt = null;
this._encryptB64 = null;
this._hmac = null;
this._hmacB64 = null;
this._hmacObj = null;
this._sha256HMACHasher = null;
}
KeyBundle.prototype = {
_encrypt: null,
_encryptB64: null,
_hmac: null,
_hmacB64: null,
_hmacObj: null,
_sha256HMACHasher: null,
equals: function equals(bundle) {
return bundle &&
(bundle.hmacKey == this.hmacKey) &&
(bundle.encryptionKey == this.encryptionKey);
},
/*
* Accessors for the two keys.
*/
get encryptionKey() {
return this._encrypt;
},
set encryptionKey(value) {
if (!value || typeof value != "string") {
throw new Error("Encryption key can only be set to string values.");
}
if (value.length < 16) {
throw new Error("Encryption key must be at least 128 bits long.");
}
this._encrypt = value;
this._encryptB64 = btoa(value);
},
get encryptionKeyB64() {
return this._encryptB64;
},
get hmacKey() {
return this._hmac;
},
set hmacKey(value) {
if (!value || typeof value != "string") {
throw new Error("HMAC key can only be set to string values.");
}
if (value.length < 16) {
throw new Error("HMAC key must be at least 128 bits long.");
}
this._hmac = value;
this._hmacB64 = btoa(value);
this._hmacObj = value ? Utils.makeHMACKey(value) : null;
this._sha256HMACHasher = value ? Utils.makeHMACHasher(
Ci.nsICryptoHMAC.SHA256, this._hmacObj) : null;
},
get hmacKeyB64() {
return this._hmacB64;
},
get hmacKeyObject() {
return this._hmacObj;
},
get sha256HMACHasher() {
return this._sha256HMACHasher;
},
/**
* Populate this key pair with 2 new, randomly generated keys.
*/
generateRandom: function generateRandom() {
let generatedHMAC = Svc.Crypto.generateRandomKey();
let generatedEncr = Svc.Crypto.generateRandomKey();
this.keyPairB64 = [generatedEncr, generatedHMAC];
},
};
/**
* Represents a KeyBundle associated with a collection.
*
* This is just a KeyBundle with a collection attached.
*/
function BulkKeyBundle(collection) {
let log = Log4Moz.repository.getLogger("Sync.BulkKeyBundle");
log.info("BulkKeyBundle being created for " + collection);
KeyBundle.call(this);
this._collection = collection;
}
BulkKeyBundle.prototype = {
__proto__: KeyBundle.prototype,
get collection() {
return this._collection;
},
/**
* Obtain the key pair in this key bundle.
*
* The returned keys are represented as raw byte strings.
*/
get keyPair() {
return [this.encryptionKey, this.hmacKey];
},
set keyPair(value) {
if (!Array.isArray(value) || value.length != 2) {
throw new Error("BulkKeyBundle.keyPair value must be array of 2 keys.");
}
this.encryptionKey = value[0];
this.hmacKey = value[1];
},
get keyPairB64() {
return [this.encryptionKeyB64, this.hmacKeyB64];
},
set keyPairB64(value) {
if (!Array.isArray(value) || value.length != 2) {
throw new Error("BulkKeyBundle.keyPairB64 value must be an array of 2 " +
"keys.");
}
this.encryptionKey = Utils.safeAtoB(value[0]);
this.hmacKey = Utils.safeAtoB(value[1]);
},
};
/**
* Represents a key pair derived from a Sync Key via HKDF.
*
* Instances of this type should be considered immutable. You create an
* instance by specifying the username and 26 character "friendly" Base32
* encoded Sync Key. The Sync Key is derived at instance creation time.
*
* If the username or Sync Key is invalid, an Error will be thrown.
*/
function SyncKeyBundle(username, syncKey) {
let log = Log4Moz.repository.getLogger("Sync.SyncKeyBundle");
log.info("SyncKeyBundle being created.");
KeyBundle.call(this);
this.generateFromKey(username, syncKey);
}
SyncKeyBundle.prototype = {
__proto__: KeyBundle.prototype,
/*
* If we've got a string, hash it into keys and store them.
*/
generateFromKey: function generateFromKey(username, syncKey) {
if (!username || (typeof username != "string")) {
throw new Error("Sync Key cannot be generated from non-string username.");
}
if (!syncKey || (typeof syncKey != "string")) {
throw new Error("Sync Key cannot be generated from non-string key.");
}
if (!Utils.isPassphrase(syncKey)) {
throw new Error("Provided key is not a passphrase, cannot derive Sync " +
"Key Bundle.");
}
// Expand the base32 Sync Key to an AES 256 and 256 bit HMAC key.
let prk = Utils.decodeKeyBase32(syncKey);
let info = HMAC_INPUT + username;
let okm = Utils.hkdfExpand(prk, info, 32 * 2);
this.encryptionKey = okm.slice(0, 32);
this.hmacKey = okm.slice(32, 64);
},
};

View File

@ -40,7 +40,7 @@ const EXPORTED_SYMBOLS = ['Weave'];
let Weave = {};
Components.utils.import("resource://services-sync/constants.js", Weave);
let lazies = {
"record.js": ["CollectionKeys", "BulkKeyBundle", "SyncKeyBundle"],
"record.js": ["CollectionKeys"],
"engines.js": ['Engines', 'Engine', 'SyncEngine', 'Store'],
"engines/addons.js": ["AddonsEngine"],
"engines/bookmarks.js": ['BookmarksEngine', 'BookmarksSharingManager'],
@ -51,13 +51,13 @@ let lazies = {
"engines/passwords.js": ["PasswordEngine"],
"engines/tabs.js": ["TabEngine"],
"engines/apps.js": ["AppsEngine"],
"identity.js": ["Identity", "ID"],
"identity.js": ["Identity"],
"jpakeclient.js": ["JPAKEClient"],
"keys.js": ["BulkKeyBundle", "SyncKeyBundle"],
"notifications.js": ["Notifications", "Notification", "NotificationButton"],
"policies.js": ["SyncScheduler", "ErrorHandler",
"SendCredentialsController"],
"resource.js": ["Resource", "AsyncResource", "Auth",
"BasicAuthenticator", "NoOpAuthenticator"],
"resource.js": ["Resource", "AsyncResource"],
"service.js": ["Service"],
"status.js": ["Status"],
"util.js": ['Utils', 'Svc', 'Str']

View File

@ -420,7 +420,9 @@ let SyncScheduler = {
* non-syncing.
*/
scheduleAtInterval: function scheduleAtInterval(minimumInterval) {
let interval = Utils.calculateBackoff(this._syncErrors, MINIMUM_BACKOFF_INTERVAL);
let interval = Utils.calculateBackoff(this._syncErrors,
MINIMUM_BACKOFF_INTERVAL,
Status.backoffInterval);
if (minimumInterval) {
interval = Math.max(minimumInterval, interval);
}
@ -907,9 +909,9 @@ SendCredentialsController.prototype = {
sendCredentials: function sendCredentials() {
this._log.trace("Sending credentials.");
let credentials = {account: Weave.Service.account,
password: Weave.Service.password,
synckey: Weave.Service.passphrase,
let credentials = {account: Weave.Identity.account,
password: Weave.Identity.basicPassword,
synckey: Weave.Identity.syncKey,
serverURL: Weave.Service.serverURL};
this.jpakeclient.sendAndComplete(credentials);
},

View File

@ -38,8 +38,7 @@
* ***** END LICENSE BLOCK ***** */
const EXPORTED_SYMBOLS = ["WBORecord", "RecordManager", "Records",
"CryptoWrapper", "CollectionKeys", "BulkKeyBundle",
"SyncKeyBundle", "Collection"];
"CryptoWrapper", "CollectionKeys", "Collection"];
const Cc = Components.classes;
const Ci = Components.interfaces;
@ -51,6 +50,7 @@ const KEYS_WBO = "keys";
Cu.import("resource://services-sync/constants.js");
Cu.import("resource://services-sync/identity.js");
Cu.import("resource://services-sync/keys.js");
Cu.import("resource://services-sync/log4moz.js");
Cu.import("resource://services-sync/resource.js");
Cu.import("resource://services-sync/util.js");
@ -88,8 +88,11 @@ WBORecord.prototype = {
// Take a base URI string, with trailing slash, and return the URI of this
// WBO based on collection and ID.
uri: function(base) {
if (this.collection && this.id)
return Utils.makeURL(base + this.collection + "/" + this.id);
if (this.collection && this.id) {
let url = Utils.makeURI(base + this.collection + "/" + this.id);
url.QueryInterface(Ci.nsIURL);
return url;
}
return null;
},
@ -198,8 +201,9 @@ CryptoWrapper.prototype = {
ciphertextHMAC: function ciphertextHMAC(keyBundle) {
let hasher = keyBundle.sha256HMACHasher;
if (!hasher)
if (!hasher) {
throw "Cannot compute HMAC without an HMAC key.";
}
return Utils.bytesAsHex(Utils.digestUTF8(this.ciphertext, hasher));
},
@ -215,12 +219,13 @@ CryptoWrapper.prototype = {
*/
encrypt: function encrypt(keyBundle) {
keyBundle = keyBundle || CollectionKeys.keyForCollection(this.collection);
if (!keyBundle)
if (!keyBundle) {
throw new Error("Key bundle is null for " + this.uri.spec);
}
this.IV = Svc.Crypto.generateRandomIV();
this.ciphertext = Svc.Crypto.encrypt(JSON.stringify(this.cleartext),
keyBundle.encryptionKey, this.IV);
keyBundle.encryptionKeyB64, this.IV);
this.hmac = this.ciphertextHMAC(keyBundle);
this.cleartext = null;
},
@ -232,8 +237,9 @@ CryptoWrapper.prototype = {
}
keyBundle = keyBundle || CollectionKeys.keyForCollection(this.collection);
if (!keyBundle)
if (!keyBundle) {
throw new Error("Key bundle is null for " + this.collection + "/" + this.id);
}
// Authenticate the encrypted blob with the expected HMAC
let computedHMAC = this.ciphertextHMAC(keyBundle);
@ -244,7 +250,7 @@ CryptoWrapper.prototype = {
// Handle invalid data here. Elsewhere we assume that cleartext is an object.
let cleartext = Svc.Crypto.decrypt(this.ciphertext,
keyBundle.encryptionKey, this.IV);
keyBundle.encryptionKeyB64, this.IV);
let json_result = JSON.parse(cleartext);
if (json_result && (json_result instanceof Object)) {
@ -293,8 +299,7 @@ XPCOMUtils.defineLazyGetter(this, "CollectionKeys", function () {
/**
* Keeps track of mappings between collection names ('tabs') and
* keyStrs, which you can feed into KeyBundle to get encryption tokens.
* Keeps track of mappings between collection names ('tabs') and KeyBundles.
*
* You can update this thing simply by giving it /info/collections. It'll
* use the last modified time to bring itself up to date.
@ -365,10 +370,10 @@ CollectionKeyManager.prototype = {
let wbo = new CryptoWrapper(CRYPTO_COLLECTION, KEYS_WBO);
let c = {};
for (let k in collections) {
c[k] = collections[k].keyPair;
c[k] = collections[k].keyPairB64;
}
wbo.cleartext = {
"default": defaultBundle ? defaultBundle.keyPair : null,
"default": defaultBundle ? defaultBundle.keyPairB64 : null,
"collections": c,
"collection": CRYPTO_COLLECTION,
"id": KEYS_WBO
@ -386,13 +391,13 @@ CollectionKeyManager.prototype = {
* Compute a new default key, and new keys for any specified collections.
*/
newKeys: function(collections) {
let newDefaultKey = new BulkKeyBundle(null, DEFAULT_KEYBUNDLE_NAME);
let newDefaultKey = new BulkKeyBundle(DEFAULT_KEYBUNDLE_NAME);
newDefaultKey.generateRandom();
let newColls = {};
if (collections) {
collections.forEach(function (c) {
let b = new BulkKeyBundle(null, c);
let b = new BulkKeyBundle(c);
b.generateRandom();
newColls[c] = b;
});
@ -459,8 +464,8 @@ CollectionKeyManager.prototype = {
}
// Process the incoming default key.
let b = new BulkKeyBundle(null, DEFAULT_KEYBUNDLE_NAME);
b.keyPair = payload.default;
let b = new BulkKeyBundle(DEFAULT_KEYBUNDLE_NAME);
b.keyPairB64 = payload.default;
let newDefault = b;
// Process the incoming collections.
@ -471,8 +476,8 @@ CollectionKeyManager.prototype = {
for (let k in colls) {
let v = colls[k];
if (v) {
let keyObj = new BulkKeyBundle(null, k);
keyObj.keyPair = v;
let keyObj = new BulkKeyBundle(k);
keyObj.keyPairB64 = v;
if (keyObj) {
newCollections[k] = keyObj;
}
@ -528,202 +533,6 @@ CollectionKeyManager.prototype = {
}
}
/**
* Abuse Identity: store the collection name (or default) in the
* username field, and the keyStr in the password field.
*
* We very rarely want to override the realm, so pass null and
* it'll default to PWDMGR_KEYBUNDLE_REALM.
*
* KeyBundle is the base class for two similar classes:
*
* SyncKeyBundle:
*
* A key string is provided, and it must be hashed to derive two different
* keys (one HMAC, one AES).
*
* BulkKeyBundle:
*
* Two independent keys are provided, or randomly generated on request.
*
*/
function KeyBundle(realm, collectionName, keyStr) {
let realm = realm || PWDMGR_KEYBUNDLE_REALM;
if (keyStr && !keyStr.charAt)
// Ensure it's valid.
throw "KeyBundle given non-string key.";
Identity.call(this, realm, collectionName, keyStr);
}
KeyBundle.prototype = {
__proto__: Identity.prototype,
_encrypt: null,
_hmac: null,
_hmacObj: null,
_sha256HMACHasher: null,
equals: function equals(bundle) {
return bundle &&
(bundle.hmacKey == this.hmacKey) &&
(bundle.encryptionKey == this.encryptionKey);
},
/*
* Accessors for the two keys.
*/
get encryptionKey() {
return this._encrypt;
},
set encryptionKey(value) {
this._encrypt = value;
},
get hmacKey() {
return this._hmac;
},
set hmacKey(value) {
this._hmac = value;
this._hmacObj = value ? Utils.makeHMACKey(value) : null;
this._sha256HMACHasher = value ? Utils.makeHMACHasher(
Ci.nsICryptoHMAC.SHA256, this._hmacObj) : null;
},
get hmacKeyObject() {
return this._hmacObj;
},
get sha256HMACHasher() {
return this._sha256HMACHasher;
}
};
function BulkKeyBundle(realm, collectionName) {
let log = Log4Moz.repository.getLogger("Sync.BulkKeyBundle");
log.info("BulkKeyBundle being created for " + collectionName);
KeyBundle.call(this, realm, collectionName);
}
BulkKeyBundle.prototype = {
__proto__: KeyBundle.prototype,
generateRandom: function generateRandom() {
let generatedHMAC = Svc.Crypto.generateRandomKey();
let generatedEncr = Svc.Crypto.generateRandomKey();
this.keyPair = [generatedEncr, generatedHMAC];
},
get keyPair() {
return [this._encrypt, btoa(this._hmac)];
},
/*
* Use keyPair = [enc, hmac], or generateRandom(), when
* you want to manage the two individual keys.
*/
set keyPair(value) {
if (value.length && (value.length == 2)) {
let json = JSON.stringify(value);
let en = value[0];
let hm = value[1];
this.password = json;
this.hmacKey = Utils.safeAtoB(hm);
this._encrypt = en; // Store in base64.
}
else {
throw "Invalid keypair";
}
}
};
function SyncKeyBundle(realm, collectionName, syncKey) {
let log = Log4Moz.repository.getLogger("Sync.SyncKeyBundle");
log.info("SyncKeyBundle being created for " + collectionName);
KeyBundle.call(this, realm, collectionName, syncKey);
if (syncKey)
this.keyStr = syncKey; // Accessor sets up keys.
}
SyncKeyBundle.prototype = {
__proto__: KeyBundle.prototype,
/*
* Use keyStr when you want to work with a key string that's
* hashed into individual keys.
*/
get keyStr() {
return this.password;
},
set keyStr(value) {
this.password = value;
this._hmac = null;
this._hmacObj = null;
this._encrypt = null;
this._sha256HMACHasher = null;
},
/*
* Can't rely on password being set through any of our setters:
* Identity does work under the hood.
*
* Consequently, make sure we derive keys if that work hasn't already been
* done.
*/
get encryptionKey() {
if (!this._encrypt)
this.generateEntry();
return this._encrypt;
},
get hmacKey() {
if (!this._hmac)
this.generateEntry();
return this._hmac;
},
get hmacKeyObject() {
if (!this._hmacObj)
this.generateEntry();
return this._hmacObj;
},
get sha256HMACHasher() {
if (!this._sha256HMACHasher)
this.generateEntry();
return this._sha256HMACHasher;
},
/*
* If we've got a string, hash it into keys and store them.
*/
generateEntry: function generateEntry() {
let syncKey = this.keyStr;
if (!syncKey)
return;
// Expand the base32 Sync Key to an AES 256 and 256 bit HMAC key.
let prk = Utils.decodeKeyBase32(syncKey);
let info = HMAC_INPUT + this.username;
let okm = Utils.hkdfExpand(prk, info, 32 * 2);
let enc = okm.slice(0, 32);
let hmac = okm.slice(32, 64);
// Save them.
this._encrypt = btoa(enc);
// Individual sets: cheaper than calling parent setter.
this._hmac = hmac;
this._hmacObj = Utils.makeHMACKey(hmac);
this._sha256HMACHasher = Utils.makeHMACHasher(
Ci.nsICryptoHMAC.SHA256, this._hmacObj);
}
};
function Collection(uri, recordObj) {
Resource.call(this, uri);
this._recordObj = recordObj;

View File

@ -37,9 +37,10 @@
*
* ***** END LICENSE BLOCK ***** */
const EXPORTED_SYMBOLS = ["Resource", "AsyncResource",
"Auth", "BrokenBasicAuthenticator",
"BasicAuthenticator", "NoOpAuthenticator"];
const EXPORTED_SYMBOLS = [
"AsyncResource",
"Resource"
];
const Cc = Components.classes;
const Ci = Components.interfaces;
@ -50,68 +51,10 @@ Cu.import("resource://services-sync/async.js");
Cu.import("resource://services-sync/constants.js");
Cu.import("resource://services-sync/ext/Observers.js");
Cu.import("resource://services-sync/ext/Preferences.js");
Cu.import("resource://services-sync/identity.js");
Cu.import("resource://services-sync/log4moz.js");
Cu.import("resource://services-sync/util.js");
XPCOMUtils.defineLazyGetter(this, "Auth", function () {
return new AuthMgr();
});
// XXX: the authenticator api will probably need to be changed to support
// other methods (digest, oauth, etc)
function NoOpAuthenticator() {}
NoOpAuthenticator.prototype = {
onRequest: function NoOpAuth_onRequest(headers) {
return headers;
}
};
// Warning: This will drop the high unicode bytes from passwords.
// Use BasicAuthenticator to send non-ASCII passwords UTF8-encoded.
function BrokenBasicAuthenticator(identity) {
this._id = identity;
}
BrokenBasicAuthenticator.prototype = {
onRequest: function BasicAuth_onRequest(headers) {
headers['authorization'] = 'Basic ' +
btoa(this._id.username + ':' + this._id.password);
return headers;
}
};
function BasicAuthenticator(identity) {
this._id = identity;
}
BasicAuthenticator.prototype = {
onRequest: function onRequest(headers) {
headers['authorization'] = 'Basic ' +
btoa(this._id.username + ':' + this._id.passwordUTF8);
return headers;
}
};
function AuthMgr() {
this._authenticators = {};
this.defaultAuthenticator = new NoOpAuthenticator();
}
AuthMgr.prototype = {
defaultAuthenticator: null,
registerAuthenticator: function AuthMgr_register(match, authenticator) {
this._authenticators[match] = authenticator;
},
lookupAuthenticator: function AuthMgr_lookup(uri) {
for (let match in this._authenticators) {
if (uri.match(match))
return this._authenticators[match];
}
return this.defaultAuthenticator;
}
};
/*
* AsyncResource represents a remote network resource, identified by a URI.
* Create an instance like so:
@ -150,6 +93,14 @@ AsyncResource.prototype = {
// Caches the latest server timestamp (X-Weave-Timestamp header).
serverTime: null,
/**
* Callback to be invoked at request time to add authentication details.
*
* By default, a global authenticator is provided. If this is set, it will
* be used instead of the global one.
*/
authenticator: null,
// The string to use as the base User-Agent in Sync requests.
// These strings will look something like
//
@ -167,29 +118,13 @@ AsyncResource.prototype = {
// Wait 5 minutes before killing a request.
ABORT_TIMEOUT: 300000,
// ** {{{ AsyncResource.authenticator }}} **
//
// Getter and setter for the authenticator module
// responsible for this particular resource. The authenticator
// module may modify the headers to perform authentication
// while performing a request for the resource, for example.
get authenticator() {
if (this._authenticator)
return this._authenticator;
else
return Auth.lookupAuthenticator(this.spec);
},
set authenticator(value) {
this._authenticator = value;
},
// ** {{{ AsyncResource.headers }}} **
//
// Headers to be included when making a request for the resource.
// Note: Header names should be all lower case, there's no explicit
// check for duplicates due to case!
get headers() {
return this.authenticator.onRequest(this._headers);
return this._headers;
},
set headers(value) {
this._headers = value;
@ -235,7 +170,7 @@ AsyncResource.prototype = {
// through. It is never called directly, only {{{_doRequest}}} uses it
// to obtain a request channel.
//
_createRequest: function Res__createRequest() {
_createRequest: function Res__createRequest(method) {
let channel = Services.io.newChannel(this.spec, null, null)
.QueryInterface(Ci.nsIRequest)
.QueryInterface(Ci.nsIHttpChannel);
@ -253,9 +188,24 @@ AsyncResource.prototype = {
channel.setRequestHeader("user-agent", ua, false);
}
// Avoid calling the authorizer more than once.
let headers = this.headers;
for (let key in headers) {
let authenticator = this.authenticator;
if (!authenticator) {
authenticator = Identity.getResourceAuthenticator();
}
if (authenticator) {
let result = authenticator(this, method);
if (result && result.headers) {
for (let [k, v] in Iterator(result.headers)) {
headers[k.toLowerCase()] = v;
}
}
} else {
this._log.debug("No authenticator found.");
}
for (let [key, value] in Iterator(headers)) {
if (key == 'authorization')
this._log.trace("HTTP Header " + key + ": ***** (suppressed)");
else
@ -270,7 +220,7 @@ AsyncResource.prototype = {
_doRequest: function _doRequest(action, data, callback) {
this._log.trace("In _doRequest.");
this._callback = callback;
let channel = this._createRequest();
let channel = this._createRequest(action);
if ("undefined" != typeof(data))
this._data = data;

View File

@ -654,13 +654,11 @@ SyncStorageRequest.prototype = {
this.setHeader("user-agent", ua);
}
// Set the BasicAuth header.
let id = ID.get("WeaveID");
if (id) {
let auth_header = "Basic " + btoa(id.username + ':' + id.passwordUTF8);
this.setHeader("authorization", auth_header);
let authenticator = Identity.getRESTRequestAuthenticator();
if (authenticator) {
authenticator(this);
} else {
this._log.debug("Couldn't set Authentication header: WeaveID not found.");
this._log.debug("No authenticator found.");
}
return RESTRequest.prototype.dispatch.apply(this, arguments);

View File

@ -91,53 +91,13 @@ WeaveSvc.prototype = {
_lock: Utils.lock,
_locked: false,
_loggedIn: false,
_identity: Weave.Identity,
get account() Svc.Prefs.get("account", this.username),
set account(value) {
if (value) {
value = value.toLowerCase();
Svc.Prefs.set("account", value);
} else {
Svc.Prefs.reset("account");
}
this.username = this._usernameFromAccount(value);
},
_usernameFromAccount: function _usernameFromAccount(value) {
// If we encounter characters not allowed by the API (as found for
// instance in an email address), hash the value.
if (value && value.match(/[^A-Z0-9._-]/i))
return Utils.sha1Base32(value.toLowerCase()).toLowerCase();
return value;
},
get username() {
return Svc.Prefs.get("username", "").toLowerCase();
},
set username(value) {
if (value) {
// Make sure all uses of this new username is lowercase
value = value.toLowerCase();
Svc.Prefs.set("username", value);
}
else
Svc.Prefs.reset("username");
// fixme - need to loop over all Identity objects - needs some rethinking...
ID.get('WeaveID').username = value;
ID.get('WeaveCryptoID').username = value;
// FIXME: need to also call this whenever the username pref changes
this._updateCachedURLs();
},
get password() ID.get("WeaveID").password,
set password(value) ID.get("WeaveID").password = value,
get passphrase() ID.get("WeaveCryptoID").keyStr,
set passphrase(value) ID.get("WeaveCryptoID").keyStr = value,
get syncKeyBundle() ID.get("WeaveCryptoID"),
userBaseURL: null,
infoURL: null,
storageURL: null,
metaURL: null,
cryptoKeyURL: null,
get serverURL() Svc.Prefs.get("serverURL"),
set serverURL(value) {
@ -218,11 +178,11 @@ WeaveSvc.prototype = {
_updateCachedURLs: function _updateCachedURLs() {
// Nothing to cache yet if we don't have the building blocks
if (this.clusterURL == "" || this.username == "")
if (this.clusterURL == "" || this._identity.username == "")
return;
let storageAPI = this.clusterURL + SYNC_API_VERSION + "/";
this.userBaseURL = storageAPI + this.username + "/";
this.userBaseURL = storageAPI + this._identity.username + "/";
this._log.debug("Caching URLs under storage user base: " + this.userBaseURL);
// Generate and cache various URLs under the storage API for this user
@ -301,7 +261,7 @@ WeaveSvc.prototype = {
return false;
}
let keysChanged = this.handleFetchedKeys(this.syncKeyBundle,
let keysChanged = this.handleFetchedKeys(this._identity.syncKeyBundle,
cryptoKeys, true);
if (keysChanged) {
// Did they change? If so, carry on.
@ -390,24 +350,16 @@ WeaveSvc.prototype = {
SyncScheduler.init();
if (!this.enabled)
this._log.info("Weave Sync disabled");
// Create Weave identities (for logging in, and for encryption)
let id = ID.get("WeaveID");
if (!id)
id = ID.set("WeaveID", new Identity(PWDMGR_PASSWORD_REALM, this.username));
Auth.defaultAuthenticator = new BasicAuthenticator(id);
if (!ID.get("WeaveCryptoID"))
ID.set("WeaveCryptoID",
new SyncKeyBundle(PWDMGR_PASSPHRASE_REALM, this.username));
if (!this.enabled) {
this._log.info("Firefox Sync disabled.");
}
this._updateCachedURLs();
let status = this._checkSetup();
if (status != STATUS_DISABLED && status != CLIENT_NOT_CONFIGURED)
if (status != STATUS_DISABLED && status != CLIENT_NOT_CONFIGURED) {
Svc.Obs.notify("weave:engine:start-tracking");
}
// Send an event now that Weave service is ready. We don't do this
// synchronously so that observers can import this module before
@ -418,9 +370,10 @@ WeaveSvc.prototype = {
});
},
_checkSetup: function WeaveSvc__checkSetup() {
if (!this.enabled)
_checkSetup: function _checkSetup() {
if (!this.enabled) {
return Status.service = STATUS_DISABLED;
}
return Status.checkSetup();
},
@ -507,10 +460,10 @@ WeaveSvc.prototype = {
// gets cluster from central LDAP server and returns it, or null on error
_findCluster: function _findCluster() {
this._log.debug("Finding cluster for user " + this.username);
this._log.debug("Finding cluster for user " + this._identity.username);
let fail;
let res = new Resource(this.userAPI + this.username + "/node/weave");
let res = new Resource(this.userAPI + this._identity.username + "/node/weave");
try {
let node = res.get();
switch (node.status) {
@ -601,8 +554,8 @@ WeaveSvc.prototype = {
// Furthermore, we assume that our sync key is already upgraded,
// and fail if that assumption is invalidated.
let syncKey = this.syncKeyBundle;
if (!syncKey) {
let syncKeyBundle = this._identity.syncKeyBundle;
if (!syncKeyBundle) {
this._log.error("No sync key: cannot fetch symmetric keys.");
Status.login = LOGIN_FAILED_NO_PASSPHRASE;
Status.sync = CREDENTIALS_CHANGED; // For want of a better option.
@ -610,7 +563,7 @@ WeaveSvc.prototype = {
}
// Not sure this validation is necessary now.
if (!Utils.isPassphrase(syncKey.keyStr)) {
if (!Utils.isPassphrase(this._identity.syncKey)) {
this._log.warn("Sync key input is invalid: cannot fetch symmetric keys.");
Status.login = LOGIN_FAILED_INVALID_PASSPHRASE;
Status.sync = CREDENTIALS_CHANGED;
@ -647,7 +600,7 @@ WeaveSvc.prototype = {
let cryptoResp = cryptoKeys.fetch(this.cryptoKeysURL).response;
if (cryptoResp.success) {
let keysChanged = this.handleFetchedKeys(syncKey, cryptoKeys);
let keysChanged = this.handleFetchedKeys(syncKeyBundle, cryptoKeys);
return true;
}
else if (cryptoResp.status == 404) {
@ -715,7 +668,7 @@ WeaveSvc.prototype = {
verifyLogin: function verifyLogin()
this._notify("verify-login", "", function() {
if (!this.username) {
if (!this._identity.username) {
this._log.warn("No username in verifyLogin.");
Status.login = LOGIN_FAILED_NO_USERNAME;
return false;
@ -727,7 +680,7 @@ WeaveSvc.prototype = {
// exceptions!
// Try to fetch the passphrase first, while we still have control.
try {
this.passphrase;
this._identity.syncKey;
} catch (ex) {
this._log.debug("Fetching passphrase threw " + ex +
"; assuming master password locked.");
@ -755,7 +708,7 @@ WeaveSvc.prototype = {
// We have no way of verifying the passphrase right now,
// so wait until remoteSetup to do so.
// Just make the most trivial checks.
if (!this.passphrase) {
if (!this._identity.syncKey) {
this._log.warn("No passphrase in verifyLogin.");
Status.login = LOGIN_FAILED_NO_PASSPHRASE;
return false;
@ -775,26 +728,8 @@ WeaveSvc.prototype = {
case 401:
this._log.warn("401: login failed.");
// Login failed. If the password contains non-ASCII characters,
// perhaps the server password is an old low-byte only one?
let id = ID.get('WeaveID');
if (id.password != id.passwordUTF8) {
let res = new Resource(this.infoURL);
let auth = new BrokenBasicAuthenticator(id);
res.authenticator = auth;
test = res.get();
if (test.status == 200) {
this._log.debug("Non-ASCII password detected. "
+ "Changing to UTF-8 version.");
// Let's change the password on the server to the UTF8 version.
let url = this.userAPI + this.username + "/password";
res = new Resource(url);
res.authenticator = auth;
res.post(id.passwordUTF8);
return this.verifyLogin();
}
}
// Yes, we want to fall through to the 404 case.
// Fall through to the 404 case.
case 404:
// Check that we're verifying with the correct cluster
if (this._setCluster())
@ -825,7 +760,7 @@ WeaveSvc.prototype = {
this._log.info("Generating new keys WBO...");
let wbo = CollectionKeys.generateNewKeysWBO();
this._log.info("Encrypting new key bundle.");
wbo.encrypt(this.syncKeyBundle);
wbo.encrypt(this._identity.syncKeyBundle);
this._log.info("Uploading...");
let uploadRes = wbo.upload(this.cryptoKeysURL);
@ -871,7 +806,7 @@ WeaveSvc.prototype = {
this._log.warn("Failed to download keys.");
throw new Error("Symmetric key download failed.");
}
let keysChanged = this.handleFetchedKeys(this.syncKeyBundle,
let keysChanged = this.handleFetchedKeys(this._identity.syncKeyBundle,
cryptoKeys, true);
if (keysChanged) {
this._log.info("Downloaded keys differed, as expected.");
@ -880,7 +815,7 @@ WeaveSvc.prototype = {
changePassword: function WeaveSvc_changePassword(newpass)
this._notify("changepwd", "", function() {
let url = this.userAPI + this.username + "/password";
let url = this.userAPI + this._identity.username + "/password";
try {
let resp = new Resource(url).post(Utils.encodeUTF8(newpass));
if (resp.status != 200) {
@ -895,7 +830,7 @@ WeaveSvc.prototype = {
}
// Save the new password for requests and login manager.
this.password = newpass;
this._identity.basicPassword = newpass;
this.persistLogin();
return true;
})(),
@ -908,7 +843,7 @@ WeaveSvc.prototype = {
this.logout();
/* Set this so UI is updated on next run. */
this.passphrase = newphrase;
this._identity.syncKey = newphrase;
this.persistLogin();
/* We need to re-encrypt everything, so reset. */
@ -920,7 +855,7 @@ WeaveSvc.prototype = {
return true;
}))(),
startOver: function() {
startOver: function startOver() {
this._log.trace("Invoking Service.startOver.");
Svc.Obs.notify("weave:engine:stop-tracking");
Status.resetSync();
@ -928,7 +863,7 @@ WeaveSvc.prototype = {
// We want let UI consumers of the following notification know as soon as
// possible, so let's fake for the CLIENT_NOT_CONFIGURED status for now
// by emptying the passphrase (we still need the password).
Service.passphrase = "";
this._identity.syncKey = null;
Status.login = LOGIN_FAILED_NO_PASSPHRASE;
this.logout();
Svc.Obs.notify("weave:service:start-over");
@ -959,23 +894,19 @@ WeaveSvc.prototype = {
this._ignorePrefObserver = false;
Svc.Prefs.set("lastversion", WEAVE_VERSION);
// Find weave logins and remove them.
this.password = "";
Services.logins.findLogins({}, PWDMGR_HOST, "", "").map(function(login) {
Services.logins.removeLogin(login);
});
this._identity.deleteSyncCredentials();
},
persistLogin: function persistLogin() {
// Canceled master password prompt can prevent these from succeeding.
try {
ID.get("WeaveID").persist();
ID.get("WeaveCryptoID").persist();
this._identity.persistCredentials();
} catch (ex) {
this._log.info("Unable to persist credentials: " + ex);
}
catch(ex) {}
},
login: function WeaveSvc_login(username, password, passphrase)
login: function login(username, password, passphrase)
this._catch(this._lock("service.js: login",
this._notify("login", "", function() {
this._loggedIn = false;
@ -985,23 +916,29 @@ WeaveSvc.prototype = {
}
let initialStatus = this._checkSetup();
if (username)
this.username = username;
if (password)
this.password = password;
if (passphrase)
this.passphrase = passphrase;
if (username) {
this._identity.username = username;
}
if (password) {
this._identity.basicPassword = password;
}
if (passphrase) {
this._identity.syncKey = passphrase;
}
if (this._checkSetup() == CLIENT_NOT_CONFIGURED)
throw "aborting login, client not configured";
if (this._checkSetup() == CLIENT_NOT_CONFIGURED) {
throw "Aborting login, client not configured.";
}
// Calling login() with parameters when the client was
// previously not configured means setup was completed.
if (initialStatus == CLIENT_NOT_CONFIGURED
&& (username || password || passphrase))
&& (username || password || passphrase)) {
Svc.Obs.notify("weave:service:setup-complete");
}
this._log.info("Logging in user " + this.username);
this._log.info("Logging in user " + this._identity.username);
this._updateCachedURLs();
if (!this.verifyLogin()) {
// verifyLogin sets the failure states here.
@ -1013,7 +950,7 @@ WeaveSvc.prototype = {
return true;
})))(),
logout: function WeaveSvc_logout() {
logout: function logout() {
// No need to do anything if we're already logged out.
if (!this._loggedIn)
return;
@ -1025,10 +962,9 @@ WeaveSvc.prototype = {
},
checkAccount: function checkAccount(account) {
let username = this._usernameFromAccount(account);
let username = this._identity.usernameFromAccount(account);
let url = this.userAPI + username;
let res = new Resource(url);
res.authenticator = new NoOpAuthenticator();
let data = "";
try {
@ -1049,7 +985,7 @@ WeaveSvc.prototype = {
createAccount: function createAccount(email, password,
captchaChallenge, captchaResponse) {
let username = this._usernameFromAccount(email);
let username = this._identity.usernameFromAccount(email);
let payload = JSON.stringify({
"password": Utils.encodeUTF8(password),
"email": email,
@ -1059,7 +995,6 @@ WeaveSvc.prototype = {
let url = this.userAPI + username;
let res = new Resource(url);
res.authenticator = new NoOpAuthenticator();
// Hint to server to allow scripted user creation or otherwise
// ignore captcha.
@ -1106,7 +1041,7 @@ WeaveSvc.prototype = {
Records.del(this.metaURL);
// ... fetch the current record from the server, and COPY THE FLAGS.
let newMeta = Records.get(this.metaURL);
let newMeta = Records.get(this.metaURL);
if (!Records.response.success || !newMeta) {
this._log.debug("No meta/global record on the server. Creating one.");
@ -1508,21 +1443,6 @@ WeaveSvc.prototype = {
return true;
},
/**
* Silently fixes case issues.
*/
syncKeyNeedsUpgrade: function syncKeyNeedsUpgrade() {
let p = this.passphrase;
// Check whether it's already a key that we generated.
if (Utils.isPassphrase(p)) {
this._log.info("Sync key is up-to-date: no need to upgrade.");
return false;
}
return true;
},
/**
* If we have a passphrase, rather than a 25-alphadigit sync key,
* use the provided sync ID to bootstrap it using PBKDF2.
@ -1534,11 +1454,17 @@ WeaveSvc.prototype = {
* we decide to bump the server storage version.
*/
upgradeSyncKey: function upgradeSyncKey(syncID) {
let p = this.passphrase;
let p = this._identity.syncKey;
if (!p) {
return false;
}
// Check whether it's already a key that we generated.
if (!this.syncKeyNeedsUpgrade(p))
if (Utils.isPassphrase(p)) {
this._log.info("Sync key is up-to-date: no need to upgrade.");
return true;
}
// Otherwise, let's upgrade it.
// N.B., we persist the sync key without testing it first...
@ -1552,7 +1478,7 @@ WeaveSvc.prototype = {
}
this._log.info("Upgrading sync key...");
this.passphrase = k;
this._identity.syncKey = k;
this._log.info("Saving upgraded sync key...");
this.persistLogin();
this._log.info("Done saving.");

View File

@ -42,10 +42,12 @@ const Cu = Components.utils;
Cu.import("resource://services-sync/constants.js");
Cu.import("resource://services-sync/log4moz.js");
Cu.import("resource://services-sync/identity.js");
Cu.import("resource://gre/modules/Services.jsm");
let Status = {
_log: Log4Moz.repository.getLogger("Sync.Status"),
_authManager: Identity,
ready: false,
get service() {
@ -65,10 +67,10 @@ let Status = {
this._log.debug("Status.login: " + this._login + " => " + code);
this._login = code;
if (code == LOGIN_FAILED_NO_USERNAME ||
code == LOGIN_FAILED_NO_PASSWORD ||
if (code == LOGIN_FAILED_NO_USERNAME ||
code == LOGIN_FAILED_NO_PASSWORD ||
code == LOGIN_FAILED_NO_PASSPHRASE) {
this.service = CLIENT_NOT_CONFIGURED;
this.service = CLIENT_NOT_CONFIGURED;
} else if (code != LOGIN_SUCCEEDED) {
this.service = LOGIN_FAILED;
} else {
@ -109,46 +111,14 @@ let Status = {
},
checkSetup: function checkSetup() {
// Check whether we have a username without importing The World(tm).
let prefs = Cc["@mozilla.org/preferences-service;1"]
.getService(Ci.nsIPrefService)
.getBranch(PREFS_BRANCH);
let username;
try {
username = prefs.getCharPref("username");
} catch(ex) {}
if (!username) {
Status.login = LOGIN_FAILED_NO_USERNAME;
return Status.service;
let result = this._authManager.currentAuthState;
if (result == STATUS_OK) {
Status.service = result;
return result;
}
Cu.import("resource://services-sync/util.js");
Cu.import("resource://services-sync/identity.js");
Cu.import("resource://services-sync/record.js");
if (!Utils.mpLocked()) {
let id = ID.get("WeaveID");
if (!id) {
id = ID.set("WeaveID", new Identity(PWDMGR_PASSWORD_REALM, username));
}
if (!id.password) {
Status.login = LOGIN_FAILED_NO_PASSWORD;
return Status.service;
}
id = ID.get("WeaveCryptoID");
if (!id) {
id = ID.set("WeaveCryptoID",
new SyncKeyBundle(PWDMGR_PASSPHRASE_REALM, username));
}
if (!id.keyStr) {
Status.login = LOGIN_FAILED_NO_PASSPHRASE;
return Status.service;
}
}
return Status.service = STATUS_OK;
Status.login = result;
return Status.service;
},
resetBackoff: function resetBackoff() {

View File

@ -49,7 +49,6 @@ Cu.import("resource://services-sync/ext/Observers.js");
Cu.import("resource://services-sync/ext/Preferences.js");
Cu.import("resource://services-sync/ext/StringBundle.js");
Cu.import("resource://services-sync/log4moz.js");
Cu.import("resource://services-sync/status.js");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/PlacesUtils.jsm");
@ -207,28 +206,6 @@ let Utils = {
return !!guid && this._base64url_regex.test(guid);
},
ensureOneOpen: let (windows = {}) function ensureOneOpen(window) {
// Close the other window if it exists
let url = window.location.href;
let other = windows[url];
if (other != null)
other.close();
// Save the new window for future closure
windows[url] = window;
// Actively clean up when the window is closed
window.addEventListener("unload", function() windows[url] = null, false);
},
// Returns a nsILocalFile representing a file relative to the current
// user's profile directory. The argument should be a string with
// unix-style slashes for directory names (these slashes are automatically
// converted to platform-specific path separators).
getProfileFile: function getProfileFile(path) {
return FileUtils.getFile("ProfD", path.split("/"), true);
},
/**
* Add a simple getter/setter to an object that defers access of a property
* to an inner property.
@ -292,70 +269,46 @@ let Utils = {
return true;
},
deepCopy: function Weave_deepCopy(thing, noSort) {
if (typeof(thing) != "object" || thing == null)
return thing;
let ret;
if (Array.isArray(thing)) {
ret = [];
for (let i = 0; i < thing.length; i++)
ret.push(Utils.deepCopy(thing[i], noSort));
} else {
ret = {};
let props = [p for (p in thing)];
if (!noSort)
props = props.sort();
props.forEach(function(k) ret[k] = Utils.deepCopy(thing[k], noSort));
}
return ret;
},
// Works on frames or exceptions, munges file:// URIs to shorten the paths
// FIXME: filename munging is sort of hackish, might be confusing if
// there are multiple extensions with similar filenames
formatFrame: function Utils_formatFrame(frame) {
let tmp = "<file:unknown>";
let file = frame.filename || frame.fileName;
if (file)
tmp = file.replace(/^(?:chrome|file):.*?([^\/\.]+\.\w+)$/, "$1");
if (frame.lineNumber)
tmp += ":" + frame.lineNumber;
if (frame.name)
tmp = frame.name + "()@" + tmp;
return tmp;
},
exceptionStr: function Weave_exceptionStr(e) {
let message = e.message ? e.message : e;
return message + " " + Utils.stackTrace(e);
},
stackTraceFromFrame: function Weave_stackTraceFromFrame(frame) {
let output = [];
while (frame) {
let str = Utils.formatFrame(frame);
if (str)
output.push(str);
frame = frame.caller;
}
return output.join(" < ");
},
stackTrace: function Weave_stackTrace(e) {
// Wrapped nsIException
if (e.location)
return "Stack trace: " + Utils.stackTraceFromFrame(e.location);
if (e.location){
let frame = e.location;
let output = [];
while (frame) {
// Works on frames or exceptions, munges file:// URIs to shorten the paths
// FIXME: filename munging is sort of hackish, might be confusing if
// there are multiple extensions with similar filenames
let str = "<file:unknown>";
let file = frame.filename || frame.fileName;
if (file){
str = file.replace(/^(?:chrome|file):.*?([^\/\.]+\.\w+)$/, "$1");
}
if (frame.lineNumber){
str += ":" + frame.lineNumber;
}
if (frame.name){
str = frame.name + "()@" + str;
}
if (str){
output.push(str);
}
frame = frame.caller;
}
return "Stack trace: " + output.join(" < ");
}
// Standard JS exception
if (e.stack)
if (e.stack){
return "JS Stack trace: " + e.stack.trim().replace(/\n/g, " < ").
replace(/@[^@]*?([^\/\.]+\.\w+:)/g, "@$1");
}
return "No traceback available";
},
@ -891,12 +844,6 @@ let Utils = {
}
},
makeURL: function Weave_makeURL(URIString) {
let url = Utils.makeURI(URIString);
url.QueryInterface(Ci.nsIURL);
return url;
},
/**
* Load a json object from disk
*
@ -912,7 +859,7 @@ let Utils = {
if (that._log)
that._log.trace("Loading json from disk: " + filePath);
let file = Utils.getProfileFile(filePath);
let file = FileUtils.getFile("ProfD", filePath.split("/"), true);
if (!file.exists()) {
callback.call(that);
return;
@ -957,7 +904,7 @@ let Utils = {
if (that._log)
that._log.trace("Saving json to disk: " + filePath);
let file = Utils.getProfileFile(filePath);
let file = FileUtils.getFile("ProfD", filePath.split("/"), true);
let json = typeof obj == "function" ? obj.call(that) : obj;
let out = JSON.stringify(json);
@ -1233,12 +1180,14 @@ let Utils = {
* Status.backoffInterval is higher.
*
*/
calculateBackoff: function calculateBackoff(attempts, base_interval) {
calculateBackoff: function calculateBackoff(attempts, baseInterval,
statusInterval) {
let backoffInterval = attempts *
(Math.floor(Math.random() * base_interval) +
base_interval);
return Math.max(Math.min(backoffInterval, MAXIMUM_BACKOFF_INTERVAL), Status.backoffInterval);
}
(Math.floor(Math.random() * baseInterval) +
baseInterval);
return Math.max(Math.min(backoffInterval, MAXIMUM_BACKOFF_INTERVAL),
statusInterval);
},
};
XPCOMUtils.defineLazyGetter(Utils, "_utf8Converter", function() {

View File

@ -2,6 +2,7 @@
http://creativecommons.org/publicdomain/zero/1.0/ */
Cu.import("resource://services-sync/async.js");
Cu.import("resource://services-sync/identity.js");
Cu.import("resource://services-sync/util.js");
Cu.import("resource://services-sync/record.js");
Cu.import("resource://services-sync/engines.js");
@ -260,14 +261,22 @@ FakeCryptoService.prototype = {
}
};
function setBasicCredentials(username, password, syncKey) {
let auth = Identity;
auth.username = username;
auth.basicPassword = password;
auth.syncKey = syncKey;
}
function SyncTestingInfrastructure() {
Cu.import("resource://services-sync/identity.js");
function SyncTestingInfrastructure(username, password, syncKey) {
Cu.import("resource://services-sync/service.js");
ID.set('WeaveID',
new Identity('Mozilla Services Encryption Passphrase', 'foo'));
ID.set('WeaveCryptoID',
new Identity('Mozilla Services Encryption Passphrase', 'foo'));
Identity.account = username || "foo";
Identity.basicPassword = password || "password";
Identity.syncKey = syncKey || "foo";
Service.serverURL = TEST_SERVER_URL;
Service.clusterURL = TEST_CLUSTER_URL;
this.logStats = initTestLogging();
this.fakeFilesystem = new FakeFilesystemService({});
@ -456,3 +465,26 @@ RotaryEngine.prototype = {
}
}
};
deepCopy: function deepCopy(thing, noSort) {
if (typeof(thing) != "object" || thing == null){
return thing;
}
let ret;
if (Array.isArray(thing)) {
ret = [];
for (let i = 0; i < thing.length; i++){
ret.push(deepCopy(thing[i], noSort));
}
} else {
ret = {};
let props = [p for (p in thing)];
if (!noSort){
props = props.sort();
}
props.forEach(function(k) ret[k] = deepCopy(thing[k], noSort));
}
return ret;
};

View File

@ -64,8 +64,12 @@ function basic_auth_header(user, password) {
}
function basic_auth_matches(req, user, password) {
return req.hasHeader("Authorization") &&
(req.getHeader("Authorization") == basic_auth_header(user, password));
if (!req.hasHeader("Authorization")) {
return false;
}
let expected = basic_auth_header(user, Utils.encodeUTF8(password));
return req.getHeader("Authorization") == expected;
}
function httpd_basic_auth_handler(body, metadata, response) {

View File

@ -154,11 +154,7 @@ add_test(function test_disabled_install_semantics() {
const PASSPHRASE = "abcdeabcdeabcdeabcdeabcdea";
const ADDON_ID = "addon1@tests.mozilla.org";
Service.username = USER;
Service.password = PASSWORD;
Service.passphrase = PASSPHRASE;
Service.serverURL = TEST_SERVER_URL;
Service.clusterURL = TEST_CLUSTER_URL;
new SyncTestingInfrastructure(USER, PASSWORD, PASSPHRASE);
generateNewKeys();

View File

@ -1,65 +0,0 @@
Cu.import("resource://services-sync/identity.js");
Cu.import("resource://services-sync/log4moz.js");
Cu.import("resource://services-sync/resource.js");
Cu.import("resource://services-sync/util.js");
let logger;
function server_handler(metadata, response) {
let body, statusCode, status;
let guestHeader = basic_auth_header("guest", "guest");
let johnHeader = basic_auth_header("johndoe", "moneyislike$£¥");
_("Guest header: " + guestHeader);
_("John header: " + johnHeader);
switch (metadata.getHeader("Authorization")) {
case guestHeader:
body = "This path exists and is protected";
statusCode = 200;
status = "OK";
break;
case johnHeader:
body = "This path exists and is protected by a UTF8 password";
statusCode = 200;
status = "OK";
break;
default:
body = "This path exists and is protected - failed";
statusCode = 401;
status = "Unauthorized";
}
response.setStatusLine(metadata.httpVersion, statusCode, status);
response.setHeader("WWW-Authenticate", 'Basic realm="secret"', false);
response.bodyOutputStream.write(body, body.length);
}
function run_test() {
initTestLogging("Trace");
do_test_pending();
let server = new nsHttpServer();
server.registerPathHandler("/foo", server_handler);
server.registerPathHandler("/bar", server_handler);
server.start(8080);
let guestIdentity = new Identity("secret", "guest", "guest");
let johnIdentity = new Identity("secret2", "johndoe", "moneyislike$£¥")
let guestAuth = new BasicAuthenticator(guestIdentity);
let johnAuth = new BasicAuthenticator(johnIdentity);
Auth.defaultAuthenticator = guestAuth;
Auth.registerAuthenticator("bar$", johnAuth);
try {
let content = new Resource("http://localhost:8080/foo").get();
do_check_eq(content, "This path exists and is protected");
do_check_eq(content.status, 200);
content = new Resource("http://localhost:8080/bar").get();
do_check_eq(content, "This path exists and is protected by a UTF8 password");
do_check_eq(content.status, 200);
} finally {
server.stop(do_test_finished);
}
}

View File

@ -92,10 +92,7 @@ function serverForFoo(engine) {
add_test(function test_processIncoming_error_orderChildren() {
_("Ensure that _orderChildren() is called even when _processIncoming() throws an error.");
let syncTesting = new SyncTestingInfrastructure();
Svc.Prefs.set("serverURL", TEST_SERVER_URL);
Svc.Prefs.set("clusterURL", TEST_CLUSTER_URL);
Svc.Prefs.set("username", "foo");
new SyncTestingInfrastructure();
let engine = new BookmarksEngine();
let store = engine._store;
@ -165,10 +162,7 @@ add_test(function test_processIncoming_error_orderChildren() {
add_test(function test_restorePromptsReupload() {
_("Ensure that restoring from a backup will reupload all records.");
let syncTesting = new SyncTestingInfrastructure();
Svc.Prefs.set("username", "foo");
Service.serverURL = TEST_SERVER_URL;
Service.clusterURL = TEST_CLUSTER_URL;
new SyncTestingInfrastructure();
let engine = new BookmarksEngine();
let store = engine._store;
@ -333,10 +327,7 @@ add_test(function test_mismatched_types() {
"parentid": "toolbar"
};
let syncTesting = new SyncTestingInfrastructure();
Svc.Prefs.set("username", "foo");
Service.serverURL = TEST_SERVER_URL;
Service.clusterURL = TEST_CLUSTER_URL;
new SyncTestingInfrastructure();
let engine = new BookmarksEngine();
let store = engine._store;
@ -379,10 +370,8 @@ add_test(function test_mismatched_types() {
add_test(function test_bookmark_guidMap_fail() {
_("Ensure that failures building the GUID map cause early death.");
let syncTesting = new SyncTestingInfrastructure();
Svc.Prefs.set("serverURL", TEST_SERVER_URL);
Svc.Prefs.set("clusterURL", TEST_CLUSTER_URL);
Svc.Prefs.set("username", "foo");
new SyncTestingInfrastructure();
let engine = new BookmarksEngine();
let store = engine._store;
@ -429,10 +418,59 @@ add_test(function test_bookmark_guidMap_fail() {
server.stop(run_next_test);
});
add_test(function test_bookmark_is_taggable() {
let engine = new BookmarksEngine();
let store = engine._store;
do_check_true(store.isTaggable("bookmark"));
do_check_true(store.isTaggable("microsummary"));
do_check_true(store.isTaggable("query"));
do_check_false(store.isTaggable("folder"));
do_check_false(store.isTaggable("livemark"));
do_check_false(store.isTaggable(null));
do_check_false(store.isTaggable(undefined));
do_check_false(store.isTaggable(""));
run_next_test();
});
add_test(function test_bookmark_tag_but_no_uri() {
_("Ensure that a bookmark record with tags, but no URI, doesn't throw an exception.");
let engine = new BookmarksEngine();
let store = engine._store;
// We're simply checking that no exception is thrown, so
// no actual checks in this test.
store._tagURI(null, ["foo"]);
store._tagURI(null, null);
store._tagURI(Utils.makeURI("about:fake"), null);
let record = {
_parent: PlacesUtils.bookmarks.toolbarFolder,
id: Utils.makeGUID(),
description: "",
tags: ["foo"],
title: "Taggy tag",
type: "folder"
};
// Because update() walks the cleartext.
record.cleartext = record;
store.create(record);
record.tags = ["bar"];
store.update(record);
run_next_test();
});
function run_test() {
initTestLogging("Trace");
Log4Moz.repository.getLogger("Sync.Engine.Bookmarks").level = Log4Moz.Level.Trace;
Log4Moz.repository.getLogger("Sync.Engine.Bookmarks").level = Log4Moz.Level.Trace;
Log4Moz.repository.getLogger("Sync.Store.Bookmarks").level = Log4Moz.Level.Trace;
Log4Moz.repository.getLogger("Sync.Tracker.Bookmarks").level = Log4Moz.Level.Trace;
generateNewKeys();

View File

@ -56,7 +56,7 @@ store.wipe();
function makeLivemark(p, mintGUID) {
let b = new Livemark("bookmarks", p.id);
// Copy here, because tests mutate the contents.
b.cleartext = Utils.deepCopy(p);
b.cleartext = deepCopy(p);
if (mintGUID)
b.id = Utils.makeGUID();

View File

@ -1,9 +1,13 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
Cu.import("resource://services-sync/identity.js");
Cu.import("resource://services-sync/keys.js");
Cu.import("resource://services-sync/record.js");
Cu.import("resource://services-sync/engines/bookmarks.js");
Cu.import("resource://services-sync/identity.js");
Cu.import("resource://services-sync/log4moz.js");
Cu.import("resource://services-sync/util.js");
function prepareBookmarkItem(collection, id) {
let b = new Bookmark(collection, id);
b.cleartext.stuff = "my payload here";
@ -11,11 +15,11 @@ function prepareBookmarkItem(collection, id) {
}
function run_test() {
let keyBundle = ID.set("WeaveCryptoID", new SyncKeyBundle(null, "john@example.com"));
keyBundle.keyStr = "abcdeabcdeabcdeabcdeabcdea";
Identity.username = "john@example.com";
Identity.syncKey = "abcdeabcdeabcdeabcdeabcdea";
generateNewKeys();
let keyBundle = Identity.syncKeyBundle;
let log = Log4Moz.repository.getLogger("Test");
Log4Moz.repository.rootLogger.addAppender(new Log4Moz.DumpAppender());

View File

@ -58,6 +58,8 @@ function serverForFoo(engine) {
// Verify that Places smart bookmarks have their annotation uploaded and
// handled locally.
add_test(function test_annotation_uploaded() {
new SyncTestingInfrastructure();
let startCount = smartBookmarkCount();
_("Start count is " + startCount);
@ -106,10 +108,6 @@ add_test(function test_annotation_uploaded() {
do_check_eq(smartBookmarkCount(), startCount + 1);
_("Sync record to the server.");
Svc.Prefs.set("username", "foo");
Service.serverURL = TEST_SERVER_URL;
Service.clusterURL = TEST_CLUSTER_URL;
let server = serverForFoo(engine);
let collection = server.user("foo").collection("bookmarks");
@ -178,6 +176,8 @@ add_test(function test_annotation_uploaded() {
});
add_test(function test_smart_bookmarks_duped() {
new SyncTestingInfrastructure();
let parent = PlacesUtils.toolbarFolderId;
let uri =
Utils.makeURI("place:redirectsMode=" +
@ -192,10 +192,6 @@ add_test(function test_smart_bookmarks_duped() {
let record = store.createRecord(mostVisitedGUID);
_("Prepare sync.");
Svc.Prefs.set("username", "foo");
Service.serverURL = TEST_SERVER_URL;
Service.clusterURL = TEST_CLUSTER_URL;
let server = serverForFoo(engine);
let collection = server.user("foo").collection("bookmarks");

View File

@ -1,3 +1,6 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
Cu.import("resource://services-sync/constants.js");
Cu.import("resource://services-sync/record.js");
Cu.import("resource://services-sync/identity.js");
@ -48,7 +51,7 @@ add_test(function test_bad_hmac() {
function uploadNewKeys() {
generateNewKeys();
let serverKeys = CollectionKeys.asWBO("crypto", "keys");
serverKeys.encrypt(Weave.Service.syncKeyBundle);
serverKeys.encrypt(Weave.Identity.syncKeyBundle);
do_check_true(serverKeys.upload(Weave.Service.cryptoKeysURL).success);
}
@ -77,7 +80,7 @@ add_test(function test_bad_hmac() {
Clients.resetClient();
generateNewKeys();
let serverKeys = CollectionKeys.asWBO("crypto", "keys");
serverKeys.encrypt(Weave.Service.syncKeyBundle);
serverKeys.encrypt(Weave.Identity.syncKeyBundle);
do_check_true(serverKeys.upload(Weave.Service.cryptoKeysURL).success);
_("Sync.");
@ -164,10 +167,8 @@ add_test(function test_properties() {
add_test(function test_sync() {
_("Ensure that Clients engine uploads a new client record once a week.");
Svc.Prefs.set("serverURL", TEST_SERVER_URL);
Svc.Prefs.set("clusterURL", TEST_CLUSTER_URL);
Svc.Prefs.set("username", "foo");
new SyncTestingInfrastructure();
generateNewKeys();
let contents = {
@ -405,9 +406,7 @@ add_test(function test_process_incoming_commands() {
add_test(function test_command_sync() {
_("Ensure that commands are synced across clients.");
Svc.Prefs.set("serverURL", TEST_SERVER_URL);
Svc.Prefs.set("clusterURL", TEST_CLUSTER_URL);
Svc.Prefs.set("username", "foo");
new SyncTestingInfrastructure();
Clients._store.wipe();
generateNewKeys();
@ -480,7 +479,8 @@ add_test(function test_send_uri_to_client_for_display() {
let initialScore = tracker.score;
let uri = "http://www.mozilla.org/";
Clients.sendURIToClientForDisplay(uri, remoteId);
let title = "Title of the Page";
Clients.sendURIToClientForDisplay(uri, remoteId, title);
let newRecord = store._remoteClients[remoteId];
@ -489,8 +489,10 @@ add_test(function test_send_uri_to_client_for_display() {
let command = newRecord.commands[0];
do_check_eq(command.command, "displayURI");
do_check_eq(command.args.length, 2);
do_check_eq(command.args.length, 3);
do_check_eq(command.args[0], uri);
do_check_eq(command.args[1], Clients.localID);
do_check_eq(command.args[2], title);
do_check_true(tracker.score > initialScore);
do_check_true(tracker.score - initialScore >= SCORE_INCREMENT_XLARGE);
@ -518,10 +520,11 @@ add_test(function test_receive_display_uri() {
let uri = "http://www.mozilla.org/";
let remoteId = Utils.makeGUID();
let title = "Page Title!";
let command = {
command: "displayURI",
args: [uri, remoteId],
args: [uri, remoteId, title],
};
Clients.localCommands = [command];
@ -535,6 +538,7 @@ add_test(function test_receive_display_uri() {
do_check_eq(subject.uri, uri);
do_check_eq(subject.client, remoteId);
do_check_eq(subject.title, title);
do_check_eq(data, null);
run_next_test();

View File

@ -1,19 +1,23 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
Cu.import("resource://services-sync/identity.js");
Cu.import("resource://services-sync/keys.js");
Cu.import("resource://services-sync/record.js");
Cu.import("resource://services-sync/engines/clients.js");
Cu.import("resource://services-sync/identity.js");
Cu.import("resource://services-sync/util.js");
Cu.import("resource://services-sync/identity.js");
function run_test() {
_("Set up test fixtures.");
ID.set('WeaveID', new Identity('Some Identity', 'foo'));
Identity.username = "john@example.com";
Svc.Prefs.set("clusterURL", "http://fakebase/");
let baseUri = "http://fakebase/1.1/foo/storage/";
let pubUri = baseUri + "keys/pubkey";
let privUri = baseUri + "keys/privkey";
let keyBundle = ID.set("WeaveCryptoID",
new SyncKeyBundle(null, "john@example.com", "abcdeabcdeabcdeabcdeabcdea"));
Identity.syncKey = "abcdeabcdeabcdeabcdeabcdea";
let keyBundle = Identity.syncKeyBundle;
try {
_("Test that serializing client records results in uploadable ascii");

View File

@ -19,11 +19,9 @@ add_test(function test_missing_crypto_collection() {
};
}
setBasicCredentials("johndoe", "ilovejane", "a-aaaaa-aaaaa-aaaaa-aaaaa-aaaaa");
Service.serverURL = TEST_SERVER_URL;
Service.clusterURL = TEST_CLUSTER_URL;
Service.username = "johndoe";
Service.password = "ilovejane";
Service.passphrase = "a-aaaaa-aaaaa-aaaaa-aaaaa-aaaaa";
let handlers = {
"/1.1/johndoe/info/collections": maybe_empty(johnHelper.handler),

View File

@ -50,14 +50,11 @@ add_test(function test_locally_changed_keys() {
Svc.Session = {
getBrowserState: function () JSON.stringify(myTabs)
};
Weave.Service.username = "johndoe";
Weave.Service.password = "ilovejane";
Weave.Service.passphrase = passphrase;
Weave.Service.serverURL = TEST_SERVER_URL;
Weave.Service.clusterURL = TEST_CLUSTER_URL;
setBasicCredentials("johndoe", "password", passphrase);
Service.serverURL = TEST_SERVER_URL;
Service.clusterURL = TEST_CLUSTER_URL;
Engines.register(HistoryEngine);
Weave.Service._registerEngines();
@ -79,7 +76,7 @@ add_test(function test_locally_changed_keys() {
// Upload keys.
generateNewKeys();
let serverKeys = CollectionKeys.asWBO("crypto", "keys");
serverKeys.encrypt(Weave.Service.syncKeyBundle);
serverKeys.encrypt(Weave.Identity.syncKeyBundle);
do_check_true(serverKeys.upload(Weave.Service.cryptoKeysURL).success);
// Check that login works.

View File

@ -3,10 +3,7 @@ Cu.import("resource://services-sync/util.js");
add_test(function test_processIncoming_abort() {
_("An abort exception, raised in applyIncoming, will abort _processIncoming.");
let syncTesting = new SyncTestingInfrastructure();
Svc.Prefs.set("serverURL", TEST_SERVER_URL);
Svc.Prefs.set("clusterURL", TEST_CLUSTER_URL);
Svc.Prefs.set("username", "foo");
new SyncTestingInfrastructure();
generateNewKeys();
let engine = new RotaryEngine();

View File

@ -3,6 +3,7 @@
Cu.import("resource://services-sync/engines/clients.js");
Cu.import("resource://services-sync/constants.js");
Cu.import("resource://services-sync/keys.js");
Cu.import("resource://services-sync/policies.js");
Cu.import("resource://services-sync/status.js");
@ -52,8 +53,7 @@ function run_test() {
function generateCredentialsChangedFailure() {
// Make sync fail due to changed credentials. We simply re-encrypt
// the keys with a different Sync Key, without changing the local one.
let newSyncKeyBundle = new SyncKeyBundle(PWDMGR_PASSPHRASE_REALM, Service.username);
newSyncKeyBundle.keyStr = "23456234562345623456234562";
let newSyncKeyBundle = new SyncKeyBundle("johndoe", "23456234562345623456234562");
let keys = CollectionKeys.asWBO();
keys.encrypt(newSyncKeyBundle);
keys.upload(Service.cryptoKeysURL);
@ -118,9 +118,7 @@ function sync_httpd_setup() {
}
function setUp() {
Service.username = "johndoe";
Service.password = "ilovejane";
Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
setBasicCredentials("johndoe", "ilovejane", "abcdeabcdeabcdeabcdeabcdea");
Service.serverURL = TEST_SERVER_URL;
Service.clusterURL = TEST_CLUSTER_URL;
@ -130,7 +128,7 @@ function setUp() {
function generateAndUploadKeys() {
generateNewKeys();
let serverKeys = CollectionKeys.asWBO("crypto", "keys");
serverKeys.encrypt(Service.syncKeyBundle);
serverKeys.encrypt(Identity.syncKeyBundle);
return serverKeys.upload(Service.cryptoKeysURL).success;
}
@ -172,7 +170,8 @@ add_test(function test_401_logout() {
}
// Make sync fail due to login rejected.
Service.username = "janedoe";
setBasicCredentials("janedoe", "irrelevant", "irrelevant");
Service._updateCachedURLs();
_("Starting first sync.");
Service.sync();
@ -425,7 +424,7 @@ add_test(function test_login_syncAndReportErrors_non_network_error() {
// when calling syncAndReportErrors
let server = sync_httpd_setup();
setUp();
Service.password = "";
Identity.basicPassword = null;
Svc.Obs.add("weave:ui:login:error", function onSyncError() {
Svc.Obs.remove("weave:ui:login:error", onSyncError);
@ -469,7 +468,7 @@ add_test(function test_login_syncAndReportErrors_prolonged_non_network_error() {
// reported when calling syncAndReportErrors.
let server = sync_httpd_setup();
setUp();
Service.password = "";
Identity.basicPassword = null;
Svc.Obs.add("weave:ui:login:error", function onSyncError() {
Svc.Obs.remove("weave:ui:login:error", onSyncError);
@ -510,9 +509,7 @@ add_test(function test_sync_syncAndReportErrors_prolonged_non_network_error() {
add_test(function test_login_syncAndReportErrors_network_error() {
// Test network errors are reported when calling syncAndReportErrors.
Service.username = "johndoe";
Service.password = "ilovejane";
Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
setBasicCredentials("broken.wipe", "ilovejane", "abcdeabcdeabcdeabcdeabcdea");
Service.serverURL = TEST_SERVER_URL;
Service.clusterURL = TEST_CLUSTER_URL;
@ -549,9 +546,7 @@ add_test(function test_sync_syncAndReportErrors_network_error() {
add_test(function test_login_syncAndReportErrors_prolonged_network_error() {
// Test prolonged, network errors are reported
// when calling syncAndReportErrors.
Service.username = "johndoe";
Service.password = "ilovejane";
Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
setBasicCredentials("johndoe", "ilovejane", "abcdeabcdeabcdeabcdeabcdea");
Service.serverURL = TEST_SERVER_URL;
Service.clusterURL = TEST_CLUSTER_URL;
@ -589,7 +584,7 @@ add_test(function test_login_prolonged_non_network_error() {
// Test prolonged, non-network errors are reported
let server = sync_httpd_setup();
setUp();
Service.password = "";
Identity.basicPassword = null;
Svc.Obs.add("weave:ui:login:error", function onSyncError() {
Svc.Obs.remove("weave:ui:login:error", onSyncError);
@ -629,9 +624,7 @@ add_test(function test_sync_prolonged_non_network_error() {
add_test(function test_login_prolonged_network_error() {
// Test prolonged, network errors are reported
Service.username = "johndoe";
Service.password = "ilovejane";
Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
setBasicCredentials("johndoe", "ilovejane", "abcdeabcdeabcdeabcdeabcdea");
Service.serverURL = TEST_SERVER_URL;
Service.clusterURL = TEST_CLUSTER_URL;
@ -668,7 +661,7 @@ add_test(function test_login_non_network_error() {
// Test non-network errors are reported
let server = sync_httpd_setup();
setUp();
Service.password = "";
Identity.basicPassword = null;
Svc.Obs.add("weave:ui:login:error", function onSyncError() {
Svc.Obs.remove("weave:ui:login:error", onSyncError);
@ -707,9 +700,7 @@ add_test(function test_sync_non_network_error() {
});
add_test(function test_login_network_error() {
Service.username = "johndoe";
Service.password = "ilovejane";
Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
setBasicCredentials("johndoe", "ilovejane", "abcdeabcdeabcdeabcdeabcdea");
Service.serverURL = TEST_SERVER_URL;
Service.clusterURL = TEST_CLUSTER_URL;
@ -784,6 +775,7 @@ add_test(function test_info_collections_login_server_maintenance_error() {
setUp();
Service.username = "broken.info";
setBasicCredentials("broken.info", "irrelevant", "irrelevant");
Service.serverURL = TEST_MAINTENANCE_URL;
Service.clusterURL = TEST_MAINTENANCE_URL;
@ -794,7 +786,7 @@ add_test(function test_info_collections_login_server_maintenance_error() {
});
function onUIUpdate() {
do_throw("Shouldn't get here!");
do_throw("Shouldn't experience UI update!");
}
Svc.Obs.add("weave:ui:login:error", onUIUpdate);
@ -823,7 +815,7 @@ add_test(function test_meta_global_login_server_maintenance_error() {
let server = sync_httpd_setup();
setUp();
Service.username = "broken.meta";
setBasicCredentials("broken.meta", "irrelevant", "irrelevant");
Service.serverURL = TEST_MAINTENANCE_URL;
Service.clusterURL = TEST_MAINTENANCE_URL;
@ -863,9 +855,10 @@ add_test(function test_crypto_keys_login_server_maintenance_error() {
let server = sync_httpd_setup();
setUp();
Service.username = "broken.keys";
setBasicCredentials("broken.keys", "irrelevant", "irrelevant");
Service.serverURL = TEST_MAINTENANCE_URL;
Service.clusterURL = TEST_MAINTENANCE_URL;
// Force re-download of keys
CollectionKeys.clear();
@ -931,7 +924,7 @@ add_test(function test_info_collections_login_prolonged_server_maintenance_error
let server = sync_httpd_setup();
setUp();
Service.username = "broken.info";
setBasicCredentials("broken.info", "irrelevant", "irrelevant");
Service.serverURL = TEST_MAINTENANCE_URL;
Service.clusterURL = TEST_MAINTENANCE_URL;
@ -964,7 +957,7 @@ add_test(function test_meta_global_login_prolonged_server_maintenance_error(){
let server = sync_httpd_setup();
setUp();
Service.username = "broken.meta";
setBasicCredentials("broken.meta", "irrelevant", "irrelevant");
Service.serverURL = TEST_MAINTENANCE_URL;
Service.clusterURL = TEST_MAINTENANCE_URL;
@ -997,7 +990,7 @@ add_test(function test_download_crypto_keys_login_prolonged_server_maintenance_e
let server = sync_httpd_setup();
setUp();
Service.username = "broken.keys";
setBasicCredentials("broken.keys", "irrelevant", "irrelevant");
Service.serverURL = TEST_MAINTENANCE_URL;
Service.clusterURL = TEST_MAINTENANCE_URL;
// Force re-download of keys
@ -1032,9 +1025,7 @@ add_test(function test_upload_crypto_keys_login_prolonged_server_maintenance_err
let server = sync_httpd_setup();
// Start off with an empty account, do not upload a key.
Service.username = "broken.keys";
Service.password = "ilovejane";
Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
setBasicCredentials("broken.keys", "ilovejane", "abcdeabcdeabcdeabcdeabcdea");
Service.serverURL = TEST_MAINTENANCE_URL;
Service.clusterURL = TEST_MAINTENANCE_URL;
@ -1068,9 +1059,7 @@ add_test(function test_wipeServer_login_prolonged_server_maintenance_error(){
let server = sync_httpd_setup();
// Start off with an empty account, do not upload a key.
Service.username = "broken.wipe";
Service.password = "ilovejane";
Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
setBasicCredentials("broken.wipe", "ilovejane", "abcdeabcdeabcdeabcdeabcdea");
Service.serverURL = TEST_MAINTENANCE_URL;
Service.clusterURL = TEST_MAINTENANCE_URL;
@ -1103,9 +1092,8 @@ add_test(function test_wipeRemote_prolonged_server_maintenance_error(){
// wiping all remote devices.
let server = sync_httpd_setup();
Service.username = "broken.wipe";
Service.password = "ilovejane";
Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
server.registerPathHandler("/1.1/broken.wipe/storage/catapult", service_unavailable);
setBasicCredentials("broken.wipe", "ilovejane", "abcdeabcdeabcdeabcdeabcdea");
Service.serverURL = TEST_MAINTENANCE_URL;
Service.clusterURL = TEST_MAINTENANCE_URL;
generateAndUploadKeys();
@ -1173,7 +1161,7 @@ add_test(function test_info_collections_login_syncAndReportErrors_server_mainten
let server = sync_httpd_setup();
setUp();
Service.username = "broken.info";
setBasicCredentials("broken.info", "irrelevant", "irrelevant");
Service.serverURL = TEST_MAINTENANCE_URL;
Service.clusterURL = TEST_MAINTENANCE_URL;
@ -1207,7 +1195,7 @@ add_test(function test_meta_global_login_syncAndReportErrors_server_maintenance_
let server = sync_httpd_setup();
setUp();
Service.username = "broken.meta";
setBasicCredentials("broken.meta", "irrelevant", "irrelevant");
Service.serverURL = TEST_MAINTENANCE_URL;
Service.clusterURL = TEST_MAINTENANCE_URL;
@ -1241,7 +1229,7 @@ add_test(function test_download_crypto_keys_login_syncAndReportErrors_server_mai
let server = sync_httpd_setup();
setUp();
Service.username = "broken.keys";
setBasicCredentials("broken.keys", "irrelevant", "irrelevant");
Service.serverURL = TEST_MAINTENANCE_URL;
Service.clusterURL = TEST_MAINTENANCE_URL;
// Force re-download of keys
@ -1277,9 +1265,7 @@ add_test(function test_upload_crypto_keys_login_syncAndReportErrors_server_maint
let server = sync_httpd_setup();
// Start off with an empty account, do not upload a key.
Service.username = "broken.keys";
Service.password = "ilovejane";
Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
setBasicCredentials("broken.keys", "ilovejane", "abcdeabcdeabcdeabcdeabcdea");
Service.serverURL = TEST_MAINTENANCE_URL;
Service.clusterURL = TEST_MAINTENANCE_URL;
@ -1313,9 +1299,7 @@ add_test(function test_wipeServer_login_syncAndReportErrors_server_maintenance_e
let server = sync_httpd_setup();
// Start off with an empty account, do not upload a key.
Service.username = "broken.wipe";
Service.password = "ilovejane";
Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
setBasicCredentials("broken.wipe", "ilovejane", "abcdeabcdeabcdeabcdeabcdea");
Service.serverURL = TEST_MAINTENANCE_URL;
Service.clusterURL = TEST_MAINTENANCE_URL;
@ -1348,9 +1332,7 @@ add_test(function test_wipeRemote_syncAndReportErrors_server_maintenance_error()
// wiping all remote devices.
let server = sync_httpd_setup();
Service.username = "broken.wipe";
Service.password = "ilovejane";
Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
setBasicCredentials("broken.wipe", "ilovejane", "abcdeabcdeabcdeabcdeabcdea");
Service.serverURL = TEST_MAINTENANCE_URL;
Service.clusterURL = TEST_MAINTENANCE_URL;
generateAndUploadKeys();
@ -1418,7 +1400,7 @@ add_test(function test_info_collections_login_syncAndReportErrors_prolonged_serv
let server = sync_httpd_setup();
setUp();
Service.username = "broken.info";
setBasicCredentials("broken.info", "irrelevant", "irrelevant");
Service.serverURL = TEST_MAINTENANCE_URL;
Service.clusterURL = TEST_MAINTENANCE_URL;
@ -1452,7 +1434,7 @@ add_test(function test_meta_global_login_syncAndReportErrors_prolonged_server_ma
let server = sync_httpd_setup();
setUp();
Service.username = "broken.meta";
setBasicCredentials("broken.meta", "irrelevant", "irrelevant");
Service.serverURL = TEST_MAINTENANCE_URL;
Service.clusterURL = TEST_MAINTENANCE_URL;
@ -1486,7 +1468,7 @@ add_test(function test_download_crypto_keys_login_syncAndReportErrors_prolonged_
let server = sync_httpd_setup();
setUp();
Service.username = "broken.keys";
setBasicCredentials("broken.keys", "irrelevant", "irrelevant");
Service.serverURL = TEST_MAINTENANCE_URL;
Service.clusterURL = TEST_MAINTENANCE_URL;
// Force re-download of keys
@ -1522,9 +1504,7 @@ add_test(function test_upload_crypto_keys_login_syncAndReportErrors_prolonged_se
let server = sync_httpd_setup();
// Start off with an empty account, do not upload a key.
Service.username = "broken.keys";
Service.password = "ilovejane";
Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
setBasicCredentials("broken.keys", "ilovejane", "abcdeabcdeabcdeabcdeabcdea");
Service.serverURL = TEST_MAINTENANCE_URL;
Service.clusterURL = TEST_MAINTENANCE_URL;
@ -1558,9 +1538,7 @@ add_test(function test_wipeServer_login_syncAndReportErrors_prolonged_server_mai
let server = sync_httpd_setup();
// Start off with an empty account, do not upload a key.
Service.username = "broken.wipe";
Service.password = "ilovejane";
Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
setBasicCredentials("broken.wipe", "ilovejane", "abcdeabcdeabcdeabcdeabcdea");
Service.serverURL = TEST_MAINTENANCE_URL;
Service.clusterURL = TEST_MAINTENANCE_URL;

View File

@ -48,9 +48,7 @@ function sync_httpd_setup() {
}
function setUp() {
Service.username = "johndoe";
Service.password = "ilovejane";
Service.passphrase = "aabcdeabcdeabcdeabcdeabcde";
setBasicCredentials("johndoe", "ilovejane", "aabcdeabcdeabcdeabcdeabcde");
Service.serverURL = TEST_SERVER_URL;
Service.clusterURL = TEST_CLUSTER_URL;
new FakeCryptoService();
@ -59,7 +57,7 @@ function setUp() {
function generateAndUploadKeys() {
generateNewKeys();
let serverKeys = CollectionKeys.asWBO("crypto", "keys");
serverKeys.encrypt(Weave.Service.syncKeyBundle);
serverKeys.encrypt(Weave.Identity.syncKeyBundle);
return serverKeys.upload("http://localhost:8080/1.1/johndoe/storage/crypto/keys").success;
}

View File

@ -1,3 +1,6 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
Cu.import("resource://services-sync/record.js");
Cu.import("resource://services-sync/engines/history.js");
Cu.import("resource://services-sync/constants.js");
@ -5,16 +8,13 @@ Cu.import("resource://services-sync/engines.js");
Cu.import("resource://services-sync/identity.js");
Cu.import("resource://services-sync/util.js");
var syncTesting = new SyncTestingInfrastructure();
add_test(function test_processIncoming_mobile_history_batched() {
_("SyncEngine._processIncoming works on history engine.");
let FAKE_DOWNLOAD_LIMIT = 100;
Svc.Prefs.set("serverURL", TEST_SERVER_URL);
Svc.Prefs.set("clusterURL", TEST_CLUSTER_URL);
Svc.Prefs.set("username", "foo");
new SyncTestingInfrastructure();
Svc.Prefs.set("client.type", "mobile");
PlacesUtils.history.removeAllPages();
Engines.register(HistoryEngine);

View File

@ -18,11 +18,9 @@ function shared_setup() {
hmacErrorCount = 0;
// Do not instantiate SyncTestingInfrastructure; we need real crypto.
setBasicCredentials("foo", "foo", "aabcdeabcdeabcdeabcdeabcde");
Service.serverURL = TEST_SERVER_URL;
Service.clusterURL = TEST_CLUSTER_URL;
Service.username = "foo";
Service.password = "foo";
Service.passphrase = "aabcdeabcdeabcdeabcdeabcde";
// Make sure RotaryEngine is the only one we sync.
Engines._engines = {};

View File

@ -0,0 +1,226 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
Cu.import("resource://services-sync/constants.js");
Cu.import("resource://services-sync/identity.js");
function run_test() {
initTestLogging("Trace");
Log4Moz.repository.getLogger("Sync.Identity").level =
Log4Moz.Level.Trace;
run_next_test();
}
add_test(function test_username_from_account() {
_("Ensure usernameFromAccount works properly.");
do_check_eq(Identity.usernameFromAccount(null), null);
do_check_eq(Identity.usernameFromAccount("user"), "user");
do_check_eq(Identity.usernameFromAccount("User"), "user");
do_check_eq(Identity.usernameFromAccount("john@doe.com"),
"7wohs32cngzuqt466q3ge7indszva4of");
run_next_test();
});
add_test(function test_account_username() {
_("Ensure the account and username attributes work properly.");
_("Verify initial state");
do_check_eq(Svc.Prefs.get("account"), undefined);
do_check_eq(Svc.Prefs.get("username"), undefined);
do_check_eq(Identity.account, null);
do_check_eq(Identity.username, null);
_("The 'username' attribute is normalized to lower case, updates preferences and identities.");
Identity.username = "TarZan";
do_check_eq(Identity.username, "tarzan");
do_check_eq(Svc.Prefs.get("username"), "tarzan");
do_check_eq(Identity.username, "tarzan");
_("If not set, the 'account attribute' falls back to the username for backwards compatibility.");
do_check_eq(Identity.account, "tarzan");
_("Setting 'username' to a non-truthy value resets the pref.");
Identity.username = null;
do_check_eq(Identity.username, null);
do_check_eq(Identity.account, null);
const default_marker = {};
do_check_eq(Svc.Prefs.get("username", default_marker), default_marker);
do_check_eq(Identity.username, null);
_("The 'account' attribute will set the 'username' if it doesn't contain characters that aren't allowed in the username.");
Identity.account = "johndoe";
do_check_eq(Identity.account, "johndoe");
do_check_eq(Identity.username, "johndoe");
do_check_eq(Svc.Prefs.get("username"), "johndoe");
do_check_eq(Identity.username, "johndoe");
_("If 'account' contains disallowed characters such as @, 'username' will the base32 encoded SHA1 hash of 'account'");
Identity.account = "John@Doe.com";
do_check_eq(Identity.account, "john@doe.com");
do_check_eq(Identity.username, "7wohs32cngzuqt466q3ge7indszva4of");
_("Setting 'account' to a non-truthy value resets the pref.");
Identity.account = null;
do_check_eq(Identity.account, null);
do_check_eq(Svc.Prefs.get("account", default_marker), default_marker);
do_check_eq(Identity.username, null);
do_check_eq(Svc.Prefs.get("username", default_marker), default_marker);
Svc.Prefs.resetBranch("");
run_next_test();
});
add_test(function test_basic_password() {
_("Ensure basic password setting works as expected.");
Identity.account = null;
do_check_eq(Identity.currentAuthState, LOGIN_FAILED_NO_USERNAME);
let thrown = false;
try {
Identity.basicPassword = "foobar";
} catch (ex) {
thrown = true;
}
do_check_true(thrown);
thrown = false;
Identity.account = "johndoe";
do_check_eq(Identity.currentAuthState, LOGIN_FAILED_NO_PASSWORD);
Identity.basicPassword = "password";
do_check_eq(Identity.basicPassword, "password");
do_check_eq(Identity.currentAuthState, LOGIN_FAILED_NO_PASSPHRASE);
do_check_true(Identity.hasBasicCredentials());
Identity.account = null;
run_next_test();
});
add_test(function test_basic_password_persistence() {
_("Ensure credentials are saved and restored to the login manager properly.");
// Just in case.
Identity.account = null;
Identity.deleteSyncCredentials();
Identity.account = "janesmith";
Identity.basicPassword = "ilovejohn";
Identity.persistCredentials();
let im1 = new IdentityManager();
do_check_eq(im1._basicPassword, null);
do_check_eq(im1.username, "janesmith");
do_check_eq(im1.basicPassword, "ilovejohn");
let im2 = new IdentityManager();
do_check_eq(im2._basicPassword, null);
_("Now remove the password and ensure it is deleted from storage.");
Identity.basicPassword = null;
Identity.persistCredentials(); // This should nuke from storage.
do_check_eq(im2.basicPassword, null);
_("Ensure that retrieving an unset but unpersisted removal returns null.");
Identity.account = "janesmith";
Identity.basicPassword = "myotherpassword";
Identity.persistCredentials();
Identity.basicPassword = null;
do_check_eq(Identity.basicPassword, null);
// Reset for next test.
Identity.account = null;
Identity.persistCredentials();
run_next_test();
});
add_test(function test_sync_key() {
_("Ensure Sync Key works as advertised.");
_("Ensure setting a Sync Key before an account throws.");
let thrown = false;
try {
Identity.syncKey = "blahblah";
} catch (ex) {
thrown = true;
}
do_check_true(thrown);
thrown = false;
Identity.account = "johnsmith";
Identity.basicPassword = "johnsmithpw";
do_check_eq(Identity.syncKey, null);
do_check_eq(Identity.syncKeyBundle, null);
_("An invalid Sync Key is silently accepted for historical reasons.");
Identity.syncKey = "synckey";
do_check_eq(Identity.syncKey, "synckey");
_("But the SyncKeyBundle should not be created from bad keys.");
do_check_eq(Identity.syncKeyBundle, null);
let syncKey = Utils.generatePassphrase();
Identity.syncKey = syncKey;
do_check_eq(Identity.syncKey, syncKey);
do_check_neq(Identity.syncKeyBundle, null);
let im = new IdentityManager();
im.account = "pseudojohn";
do_check_eq(im.syncKey, null);
do_check_eq(im.syncKeyBundle, null);
Identity.account = null;
run_next_test();
});
add_test(function test_sync_key_persistence() {
_("Ensure Sync Key persistence works as expected.");
Identity.account = "pseudojohn";
Identity.password = "supersecret";
let syncKey = Utils.generatePassphrase();
Identity.syncKey = syncKey;
Identity.persistCredentials();
let im = new IdentityManager();
im.account = "pseudojohn";
do_check_eq(im.syncKey, syncKey);
do_check_neq(im.syncKeyBundle, null);
let kb1 = Identity.syncKeyBundle;
let kb2 = im.syncKeyBundle;
do_check_eq(kb1.encryptionKeyB64, kb2.encryptionKeyB64);
do_check_eq(kb1.hmacKeyB64, kb2.hmacKeyB64);
Identity.account = null;
Identity.persistCredentials();
let im2 = new IdentityManager();
im2.account = "pseudojohn";
do_check_eq(im2.syncKey, null);
im2.account = null;
_("Ensure deleted but not persisted value is retrieved.");
Identity.account = "someoneelse";
Identity.syncKey = Utils.generatePassphrase();
Identity.persistCredentials();
Identity.syncKey = null;
do_check_eq(Identity.syncKey, null);
// Clean up.
Identity.account = null;
Identity.persistCredentials();
run_next_test();
});

View File

@ -32,15 +32,13 @@ function sync_httpd_setup() {
}
function setUp() {
Service.username = "johndoe";
Service.password = "ilovejane";
Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
setBasicCredentials("johndoe", "ilovejane", "abcdeabcdeabcdeabcdeabcdea");
Service.serverURL = TEST_SERVER_URL;
Service.clusterURL = TEST_CLUSTER_URL;
generateNewKeys();
let serverKeys = CollectionKeys.asWBO("crypto", "keys");
serverKeys.encrypt(Service.syncKeyBundle);
serverKeys.encrypt(Identity.syncKeyBundle);
return serverKeys.upload(Service.cryptoKeysURL);
}

View File

@ -183,9 +183,7 @@ function run_test() {
// Simulate Sync setup with credentials in place. We want to make
// sure the J-PAKE requests don't include those data.
let id = new Identity(PWDMGR_PASSWORD_REALM, "johndoe");
id.password = "ilovejane";
ID.set("WeaveID", id);
setBasicCredentials("johndoe", "ilovejane");
server = httpd_setup({"/new_channel": server_new_channel,
"/report": server_report});

View File

@ -1,38 +1,15 @@
var btoa;
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
Cu.import("resource://services-sync/record.js");
Cu.import("resource://services-sync/constants.js");
btoa = Cu.import("resource://services-sync/util.js").btoa;
Cu.import("resource://services-sync/identity.js");
Cu.import("resource://services-sync/keys.js");
function sha256HMAC(message, key) {
let h = Utils.makeHMACHasher(Ci.nsICryptoHMAC.SHA256, key);
return Utils.digestBytes(message, h);
}
function test_time_keyFromString(iterations) {
let k;
let o;
let b = new BulkKeyBundle();
let d = Utils.decodeKeyBase32("ababcdefabcdefabcdefabcdef");
b.generateRandom();
_("Running " + iterations + " iterations of hmacKeyObject + sha256HMAC.");
for (let i = 0; i < iterations; ++i) {
let k = b.hmacKeyObject;
o = sha256HMAC(d, k);
}
do_check_true(!!o);
_("Done.");
}
function test_repeated_hmac() {
let testKey = "ababcdefabcdefabcdefabcdef";
let k = Utils.makeHMACKey("foo");
let one = sha256HMAC(Utils.decodeKeyBase32(testKey), k);
let two = sha256HMAC(Utils.decodeKeyBase32(testKey), k);
do_check_eq(one, two);
}
function do_check_array_eq(a1, a2) {
do_check_eq(a1.length, a2.length);
for (let i = 0; i < a1.length; ++i) {
@ -40,31 +17,6 @@ function do_check_array_eq(a1, a2) {
}
}
function test_keymanager() {
let testKey = "ababcdefabcdefabcdefabcdef";
let username = "john@example.com";
// Decode the key here to mirror what generateEntry will do,
// but pass it encoded into the KeyBundle call below.
let sha256inputE = "" + HMAC_INPUT + username + "\x01";
let key = Utils.makeHMACKey(Utils.decodeKeyBase32(testKey));
let encryptKey = sha256HMAC(sha256inputE, key);
let sha256inputH = encryptKey + HMAC_INPUT + username + "\x02";
let hmacKey = sha256HMAC(sha256inputH, key);
// Encryption key is stored in base64 for WeaveCrypto convenience.
do_check_eq(btoa(encryptKey), new SyncKeyBundle(null, username, testKey).encryptionKey);
do_check_eq(hmacKey, new SyncKeyBundle(null, username, testKey).hmacKey);
// Test with the same KeyBundle for both.
let obj = new SyncKeyBundle(null, username, testKey);
do_check_eq(hmacKey, obj.hmacKey);
do_check_eq(btoa(encryptKey), obj.encryptionKey);
}
function do_check_keypair_eq(a, b) {
do_check_eq(2, a.length);
do_check_eq(2, b.length);
@ -72,57 +24,202 @@ function do_check_keypair_eq(a, b) {
do_check_eq(a[1], b[1]);
}
function test_collections_manager() {
function test_time_keyFromString(iterations) {
let k;
let o;
let b = new BulkKeyBundle("dummy");
let d = Utils.decodeKeyBase32("ababcdefabcdefabcdefabcdef");
b.generateRandom();
_("Running " + iterations + " iterations of hmacKeyObject + sha256HMAC.");
for (let i = 0; i < iterations; ++i) {
let k = b.hmacKeyObject;
o = sha256HMAC(d, k);
}
do_check_true(!!o);
_("Done.");
}
add_test(function test_set_invalid_values() {
_("Ensure that setting invalid encryption and HMAC key values is caught.");
let bundle = new BulkKeyBundle("foo");
let thrown = false;
try {
bundle.encryptionKey = null;
} catch (ex) {
thrown = true;
do_check_eq(ex.message.indexOf("Encryption key can only be set to"), 0);
} finally {
do_check_true(thrown);
thrown = false;
}
try {
bundle.encryptionKey = ["trollololol"];
} catch (ex) {
thrown = true;
do_check_eq(ex.message.indexOf("Encryption key can only be set to"), 0);
} finally {
do_check_true(thrown);
thrown = false;
}
try {
bundle.hmacKey = Utils.generateRandomBytes(15);
} catch (ex) {
thrown = true;
do_check_eq(ex.message.indexOf("HMAC key must be at least 128"), 0);
} finally {
do_check_true(thrown);
thrown = false;
}
try {
bundle.hmacKey = null;
} catch (ex) {
thrown = true;
do_check_eq(ex.message.indexOf("HMAC key can only be set to string"), 0);
} finally {
do_check_true(thrown);
thrown = false;
}
try {
bundle.hmacKey = ["trollolol"];
} catch (ex) {
thrown = true;
do_check_eq(ex.message.indexOf("HMAC key can only be set to"), 0);
} finally {
do_check_true(thrown);
thrown = false;
}
try {
bundle.hmacKey = Utils.generateRandomBytes(15);
} catch (ex) {
thrown = true;
do_check_eq(ex.message.indexOf("HMAC key must be at least 128"), 0);
} finally {
do_check_true(thrown);
thrown = false;
}
run_next_test();
});
add_test(function test_repeated_hmac() {
let testKey = "ababcdefabcdefabcdefabcdef";
let k = Utils.makeHMACKey("foo");
let one = sha256HMAC(Utils.decodeKeyBase32(testKey), k);
let two = sha256HMAC(Utils.decodeKeyBase32(testKey), k);
do_check_eq(one, two);
run_next_test();
});
add_test(function test_sync_key_bundle_derivation() {
_("Ensure derivation from known values works.");
// The known values in this test were originally verified against Firefox
// Home.
let bundle = new SyncKeyBundle("st3fan", "q7ynpwq7vsc9m34hankbyi3s3i");
// These should be compared to the results from Home, as they once were.
let e = "14b8c09fa84e92729ee695160af6e0385f8f6215a25d14906e1747bdaa2de426";
let h = "370e3566245d79fe602a3adb5137e42439cd2a571235197e0469d7d541b07875";
let realE = Utils.bytesAsHex(bundle.encryptionKey);
let realH = Utils.bytesAsHex(bundle.hmacKey);
_("Real E: " + realE);
_("Real H: " + realH);
do_check_eq(realH, h);
do_check_eq(realE, e);
run_next_test();
});
add_test(function test_keymanager() {
let testKey = "ababcdefabcdefabcdefabcdef";
let username = "john@example.com";
// Decode the key here to mirror what generateEntry will do,
// but pass it encoded into the KeyBundle call below.
let sha256inputE = "" + HMAC_INPUT + username + "\x01";
let key = Utils.makeHMACKey(Utils.decodeKeyBase32(testKey));
let encryptKey = sha256HMAC(sha256inputE, key);
let sha256inputH = encryptKey + HMAC_INPUT + username + "\x02";
let hmacKey = sha256HMAC(sha256inputH, key);
// Encryption key is stored in base64 for WeaveCrypto convenience.
do_check_eq(encryptKey, new SyncKeyBundle(username, testKey).encryptionKey);
do_check_eq(hmacKey, new SyncKeyBundle(username, testKey).hmacKey);
// Test with the same KeyBundle for both.
let obj = new SyncKeyBundle(username, testKey);
do_check_eq(hmacKey, obj.hmacKey);
do_check_eq(encryptKey, obj.encryptionKey);
run_next_test();
});
add_test(function test_collections_manager() {
let log = Log4Moz.repository.getLogger("Test");
Log4Moz.repository.rootLogger.addAppender(new Log4Moz.DumpAppender());
let keyBundle = ID.set("WeaveCryptoID",
new SyncKeyBundle(PWDMGR_PASSPHRASE_REALM, "john@example.com", "a-bbbbb-ccccc-ddddd-eeeee-fffff"));
Identity.account = "john@example.com";
Identity.syncKey = "a-bbbbb-ccccc-ddddd-eeeee-fffff";
let keyBundle = Identity.syncKeyBundle;
/*
* Build a test version of storage/crypto/keys.
* Encrypt it with the sync key.
* Pass it into the CollectionKeyManager.
*/
log.info("Building storage keys...");
let storage_keys = new CryptoWrapper("crypto", "keys");
let default_key64 = Svc.Crypto.generateRandomKey();
let default_hmac64 = Svc.Crypto.generateRandomKey();
let bookmarks_key64 = Svc.Crypto.generateRandomKey();
let bookmarks_hmac64 = Svc.Crypto.generateRandomKey();
storage_keys.cleartext = {
"default": [default_key64, default_hmac64],
"collections": {"bookmarks": [bookmarks_key64, bookmarks_hmac64]},
};
storage_keys.modified = Date.now()/1000;
storage_keys.id = "keys";
log.info("Encrypting storage keys...");
// Use passphrase (sync key) itself to encrypt the key bundle.
storage_keys.encrypt(keyBundle);
// Sanity checking.
do_check_true(null == storage_keys.cleartext);
do_check_true(null != storage_keys.ciphertext);
log.info("Updating CollectionKeys.");
// updateContents decrypts the object, releasing the payload for us to use.
// Returns true, because the default key has changed.
do_check_true(CollectionKeys.updateContents(keyBundle, storage_keys));
let payload = storage_keys.cleartext;
_("CK: " + JSON.stringify(CollectionKeys._collections));
// Test that the CollectionKeyManager returns a similar WBO.
let wbo = CollectionKeys.asWBO("crypto", "keys");
_("WBO: " + JSON.stringify(wbo));
_("WBO cleartext: " + JSON.stringify(wbo.cleartext));
// Check the individual contents.
do_check_eq(wbo.collection, "crypto");
do_check_eq(wbo.id, "keys");
@ -131,36 +228,36 @@ function test_collections_manager() {
do_check_true(!!wbo.cleartext.default);
do_check_keypair_eq(payload.default, wbo.cleartext.default);
do_check_keypair_eq(payload.collections.bookmarks, wbo.cleartext.collections.bookmarks);
do_check_true('bookmarks' in CollectionKeys._collections);
do_check_false('tabs' in CollectionKeys._collections);
_("Updating contents twice with the same data doesn't proceed.");
storage_keys.encrypt(keyBundle);
do_check_false(CollectionKeys.updateContents(keyBundle, storage_keys));
/*
* Test that we get the right keys out when we ask for
* a collection's tokens.
*/
let b1 = new BulkKeyBundle(null, "bookmarks");
b1.keyPair = [bookmarks_key64, bookmarks_hmac64];
let b1 = new BulkKeyBundle("bookmarks");
b1.keyPairB64 = [bookmarks_key64, bookmarks_hmac64];
let b2 = CollectionKeys.keyForCollection("bookmarks");
do_check_keypair_eq(b1.keyPair, b2.keyPair);
// Check key equality.
do_check_true(b1.equals(b2));
do_check_true(b2.equals(b1));
b1 = new BulkKeyBundle(null, "[default]");
b1.keyPair = [default_key64, default_hmac64];
b1 = new BulkKeyBundle("[default]");
b1.keyPairB64 = [default_key64, default_hmac64];
do_check_false(b1.equals(b2));
do_check_false(b2.equals(b1));
b2 = CollectionKeys.keyForCollection(null);
do_check_keypair_eq(b1.keyPair, b2.keyPair);
/*
* Checking for update times.
*/
@ -170,15 +267,15 @@ function test_collections_manager() {
do_check_false(CollectionKeys.updateNeeded(info_collections));
info_collections["crypto"] = 1 + (Date.now()/1000); // Add one in case computers are fast!
do_check_true(CollectionKeys.updateNeeded(info_collections));
CollectionKeys.lastModified = null;
do_check_true(CollectionKeys.updateNeeded({}));
/*
* Check _compareKeyBundleCollections.
*/
function newBundle(name) {
let r = new BulkKeyBundle(null, name);
let r = new BulkKeyBundle(name);
r.generateRandom();
return r;
}
@ -193,7 +290,7 @@ function test_collections_manager() {
let coll4 = {"foo": k4};
let coll5 = {"baz": k5, "bar": k2};
let coll6 = {};
let d1 = CollectionKeys._compareKeyBundleCollections(coll1, coll2); // []
let d2 = CollectionKeys._compareKeyBundleCollections(coll1, coll3); // ["bar"]
let d3 = CollectionKeys._compareKeyBundleCollections(coll3, coll2); // ["bar"]
@ -202,7 +299,7 @@ function test_collections_manager() {
let d6 = CollectionKeys._compareKeyBundleCollections(coll6, coll1); // ["bar", "foo"]
let d7 = CollectionKeys._compareKeyBundleCollections(coll5, coll5); // []
let d8 = CollectionKeys._compareKeyBundleCollections(coll6, coll6); // []
do_check_true(d1.same);
do_check_false(d2.same);
do_check_false(d3.same);
@ -211,50 +308,20 @@ function test_collections_manager() {
do_check_false(d6.same);
do_check_true(d7.same);
do_check_true(d8.same);
do_check_array_eq(d1.changed, []);
do_check_array_eq(d2.changed, ["bar"]);
do_check_array_eq(d3.changed, ["bar"]);
do_check_array_eq(d4.changed, ["bar", "foo"]);
do_check_array_eq(d5.changed, ["baz", "foo"]);
do_check_array_eq(d6.changed, ["bar", "foo"]);
}
// Make sure that KeyBundles work when persisted through Identity.
function test_key_persistence() {
_("Testing key persistence.");
// Create our sync key bundle and persist it.
let k = new SyncKeyBundle(null, null, "abcdeabcdeabcdeabcdeabcdea");
k.username = "john@example.com";
ID.set("WeaveCryptoID", k);
let id = ID.get("WeaveCryptoID");
do_check_eq(k, id);
id.persist();
// Now erase any memory of it.
ID.del("WeaveCryptoID");
k = id = null;
// Now recreate via the persisted value.
id = new SyncKeyBundle();
id.username = "john@example.com";
// The password should have been fetched from storage...
do_check_eq(id.password, "abcdeabcdeabcdeabcdeabcdea");
// ... and we should be able to grab these by derivation.
do_check_true(!!id.hmacKeyObject);
do_check_true(!!id.hmacKey);
do_check_true(!!id.encryptionKey);
}
run_next_test();
});
function run_test() {
test_keymanager();
test_collections_manager();
test_key_persistence();
test_repeated_hmac();
// Only do 1,000 to avoid a 5-second pause in test runs.
test_time_keyFromString(1000);
run_next_test();
}

View File

@ -15,6 +15,7 @@ const modules = [
"ext/Preferences.js",
"identity.js",
"jpakeclient.js",
"keys.js",
"log4moz.js",
"main.js",
"notifications.js",

View File

@ -73,9 +73,7 @@ function installNodeHandler(server, next) {
}
function prepareServer() {
Service.username = "johndoe";
Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
Service.password = "ilovejane";
setBasicCredentials("johndoe", "ilovejane", "abcdeabcdeabcdeabcdeabcdea");
Service.serverURL = TEST_SERVER_URL;
Service.clusterURL = TEST_CLUSTER_URL;

View File

@ -1,8 +1,12 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
Cu.import("resource://services-sync/constants.js");
Cu.import("resource://services-sync/identity.js");
Cu.import("resource://services-sync/keys.js");
Cu.import("resource://services-sync/record.js");
Cu.import("resource://services-sync/resource.js");
Cu.import("resource://services-sync/log4moz.js");
Cu.import("resource://services-sync/identity.js");
Cu.import("resource://services-sync/util.js");
let cryptoWrap;
@ -26,8 +30,9 @@ function run_test() {
let server;
do_test_pending();
let keyBundle = ID.set("WeaveCryptoID", new SyncKeyBundle(PWDMGR_PASSPHRASE_REALM, "john@example.com"));
keyBundle.keyStr = "a-abcde-abcde-abcde-abcde-abcde";
Identity.username = "john@example.com";
Identity.syncKey = "a-abcde-abcde-abcde-abcde-abcde";
let keyBundle = Identity.syncKeyBundle;
try {
let log = Log4Moz.repository.getLogger("Test");
@ -37,14 +42,11 @@ function run_test() {
server = httpd_setup({"/steam/resource": crypted_resource_handler});
let auth = new BasicAuthenticator(new Identity("secret", "guest", "guest"));
Auth.defaultAuthenticator = auth;
log.info("Creating a record");
let cryptoUri = "http://localhost:8080/crypto/steam";
cryptoWrap = prepareCryptoWrap("steam", "resource");
log.info("cryptoWrap: " + cryptoWrap.toString());
log.info("Encrypting a record");
@ -107,7 +109,7 @@ function run_test() {
do_check_eq(error.substr(0, 42), "Record SHA256 HMAC mismatch: should be foo");
// Checking per-collection keys and default key handling.
generateNewKeys();
let bu = "http://localhost:8080/storage/bookmarks/foo";
let bookmarkItem = prepareCryptoWrap("bookmarks", "foo");
@ -116,22 +118,18 @@ function run_test() {
do_check_true(bookmarkItem.ciphertext != null);
log.info("Decrypting the record explicitly with the default key.");
do_check_eq(bookmarkItem.decrypt(CollectionKeys._default).stuff, "my payload here");
// Per-collection keys.
// Generate a key for "bookmarks".
generateNewKeys(["bookmarks"]);
bookmarkItem = prepareCryptoWrap("bookmarks", "foo");
do_check_eq(bookmarkItem.collection, "bookmarks");
// Encrypt. This'll use the "bookmarks" encryption key, because we have a
// special key for it. The same key will need to be used for decryption.
bookmarkItem.encrypt();
do_check_true(bookmarkItem.ciphertext != null);
_("Default key is " + CollectionKeys._default.keyStr);
_("Bookmarks key is " + CollectionKeys.keyForCollection("bookmarks").keyStr);
_("Bookmarks key is " + CollectionKeys._collections["bookmarks"].keyStr);
// Attempt to use the default key, because this is a collision that could
// conceivably occur in the real world. Decryption will error, because
// it's not the bookmarks key.
@ -142,7 +140,7 @@ function run_test() {
err = ex;
}
do_check_eq("Record SHA256 HMAC mismatch", err.substr(0, 27));
// Explicitly check that it's using the bookmarks key.
// This should succeed.
do_check_eq(bookmarkItem.decrypt(CollectionKeys.keyForCollection("bookmarks")).stuff,

View File

@ -1,26 +0,0 @@
let atob = Cu.import("resource://services-sync/util.js").atob;
Cu.import("resource://services-sync/constants.js");
Cu.import("resource://services-sync/record.js");
/**
* Testing the SHA256-HMAC key derivation process against test vectors
* verified with the Firefox Home implementation.
*/
function run_test() {
// Test the production of keys from a sync key.
let bundle = new SyncKeyBundle(PWDMGR_PASSPHRASE_REALM, "st3fan", "q7ynpwq7vsc9m34hankbyi3s3i");
// These should be compared to the results from Home, as they once were.
let e = "14b8c09fa84e92729ee695160af6e0385f8f6215a25d14906e1747bdaa2de426";
let h = "370e3566245d79fe602a3adb5137e42439cd2a571235197e0469d7d541b07875";
// The encryption key is stored as base64 for handing off to WeaveCrypto.
let realE = Utils.bytesAsHex(atob(bundle.encryptionKey));
let realH = Utils.bytesAsHex(bundle.hmacKey);
_("Real E: " + realE);
_("Real H: " + realH);
do_check_eq(realH, h);
do_check_eq(realE, e);
}

View File

@ -1,3 +1,6 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
Cu.import("resource://services-sync/ext/Observers.js");
Cu.import("resource://services-sync/identity.js");
Cu.import("resource://services-sync/log4moz.js");
@ -146,6 +149,8 @@ function server_headers(metadata, response) {
}
function run_test() {
initTestLogging("Trace");
do_test_pending();
logger = Log4Moz.repository.getLogger('Test');
@ -227,19 +232,7 @@ function run_test() {
_("Test that the BasicAuthenticator doesn't screw up header case.");
let res1 = new Resource("http://localhost:8080/foo");
res1.setHeader("Authorization", "Basic foobar");
res1.authenticator = new NoOpAuthenticator();
do_check_eq(res1._headers["authorization"], "Basic foobar");
do_check_eq(res1.headers["authorization"], "Basic foobar");
let id = new Identity("secret", "guest", "guest");
res1.authenticator = new BasicAuthenticator(id);
// In other words... it correctly overwrites our downcased version
// when accessed through .headers.
do_check_eq(res1._headers["authorization"], "Basic foobar");
do_check_eq(res1.headers["authorization"], "Basic Z3Vlc3Q6Z3Vlc3Q=");
do_check_eq(res1._headers["authorization"], "Basic Z3Vlc3Q6Z3Vlc3Q=");
do_check_true(!res1._headers["Authorization"]);
do_check_true(!res1.headers["Authorization"]);
_("GET a password protected resource (test that it'll fail w/o pass, no throw)");
let res2 = new Resource("http://localhost:8080/protected");
@ -249,8 +242,8 @@ function run_test() {
do_check_false(content.success);
_("GET a password protected resource");
let auth = new BasicAuthenticator(new Identity("secret", "guest", "guest"));
let res3 = new Resource("http://localhost:8080/protected");
let auth = Identity.getBasicResourceAuthenticator("guest", "guest");
res3.authenticator = auth;
do_check_eq(res3.authenticator, auth);
content = res3.get();
@ -495,8 +488,10 @@ function run_test() {
let query = "?" + args.join("&");
let uri1 = Utils.makeURL("http://foo/" + query);
let uri2 = Utils.makeURL("http://foo/");
let uri1 = Utils.makeURI("http://foo/" + query)
.QueryInterface(Ci.nsIURL);
let uri2 = Utils.makeURI("http://foo/")
.QueryInterface(Ci.nsIURL);
uri2.query = query;
do_check_eq(uri1.query, uri2.query);
server.stop(do_test_finished);

View File

@ -1,3 +1,6 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
Cu.import("resource://services-sync/ext/Observers.js");
Cu.import("resource://services-sync/identity.js");
Cu.import("resource://services-sync/log4moz.js");
@ -261,19 +264,8 @@ add_test(function test_basicauth() {
_("Test that the BasicAuthenticator doesn't screw up header case.");
let res1 = new AsyncResource("http://localhost:8080/foo");
res1.setHeader("Authorization", "Basic foobar");
res1.authenticator = new NoOpAuthenticator();
do_check_eq(res1._headers["authorization"], "Basic foobar");
do_check_eq(res1.headers["authorization"], "Basic foobar");
let id = new Identity("secret", "guest", "guest");
res1.authenticator = new BasicAuthenticator(id);
// In other words... it correctly overwrites our downcased version
// when accessed through .headers.
do_check_eq(res1._headers["authorization"], "Basic foobar");
do_check_eq(res1.headers["authorization"], "Basic Z3Vlc3Q6Z3Vlc3Q=");
do_check_eq(res1._headers["authorization"], "Basic Z3Vlc3Q6Z3Vlc3Q=");
do_check_true(!res1._headers["Authorization"]);
do_check_true(!res1.headers["Authorization"]);
run_next_test();
});
@ -292,7 +284,7 @@ add_test(function test_get_protected_fail() {
add_test(function test_get_protected_success() {
_("GET a password protected resource");
let auth = new BasicAuthenticator(new Identity("secret", "guest", "guest"));
let auth = Identity.getBasicResourceAuthenticator("guest", "guest");
let res3 = new AsyncResource("http://localhost:8080/protected");
res3.authenticator = auth;
do_check_eq(res3.authenticator, auth);
@ -661,8 +653,10 @@ add_test(function test_uri_construction() {
let query = "?" + args.join("&");
let uri1 = Utils.makeURL("http://foo/" + query);
let uri2 = Utils.makeURL("http://foo/");
let uri1 = Utils.makeURI("http://foo/" + query)
.QueryInterface(Ci.nsIURL);
let uri2 = Utils.makeURI("http://foo/")
.QueryInterface(Ci.nsIURL);
uri2.query = query;
do_check_eq(uri1.query, uri2.query);

View File

@ -25,10 +25,9 @@ function test_resource_user_agent() {
"/1.1/johndoe/storage/meta/global": uaHandler(meta_global.handler()),
});
setBasicCredentials("johndoe", "ilovejane");
Weave.Service.serverURL = TEST_SERVER_URL;
Weave.Service.clusterURL = TEST_CLUSTER_URL;
Weave.Service.username = "johndoe";
Weave.Service.password = "ilovejane";
let expectedUA = Services.appinfo.name + "/" + Services.appinfo.version +
" FxSync/" + WEAVE_VERSION + "." +

View File

@ -43,12 +43,7 @@ function sync_httpd_setup() {
}
function setUp() {
Service.username = "johndoe";
Service.password = "ilovejane";
Service.passphrase = "sekrit";
Service.serverURL = TEST_SERVER_URL;
Service.clusterURL = TEST_CLUSTER_URL;
new FakeCryptoService();
new SyncTestingInfrastructure("johndoe", "ilovejane", "sekrit");
}
function run_test() {

View File

@ -7,9 +7,7 @@ Cu.import("resource://services-sync/service.js");
Cu.import("resource://services-sync/util.js");
function run_test() {
Service.account = "johndoe";
Service.password = "ilovejane";
Service.passphrase = Utils.generatePassphrase();
setBasicCredentials("johndoe", "ilovejane", Utils.generatePassphrase());
Service.serverURL = "http://weave.server/";
initTestLogging("Trace");
@ -31,9 +29,9 @@ function make_sendCredentials_test(topic) {
sendAndCompleteCalled = true;
// Verify it sends the correct data.
do_check_eq(data.account, Service.account);
do_check_eq(data.password, Service.password);
do_check_eq(data.synckey, Service.passphrase);
do_check_eq(data.account, Identity.account);
do_check_eq(data.password, Identity.basicPassword);
do_check_eq(data.synckey, Identity.syncKey);
do_check_eq(data.serverURL, Service.serverURL);
this.controller.onComplete();

View File

@ -1,63 +1,12 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
Cu.import("resource://services-sync/constants.js");
Cu.import("resource://services-sync/identity.js");
Cu.import("resource://services-sync/service.js");
Cu.import("resource://services-sync/status.js");
Cu.import("resource://services-sync/util.js");
function test_identities() {
_("Account related Service properties correspond to preference settings and update other object properties upon being set.");
try {
_("Verify initial state");
do_check_eq(Svc.Prefs.get("account"), undefined);
do_check_eq(Svc.Prefs.get("username"), undefined);
do_check_eq(ID.get("WeaveID").username, "");
do_check_eq(ID.get("WeaveCryptoID").username, "");
_("The 'username' attribute is normalized to lower case, updates preferences and identities.");
Service.username = "TarZan";
do_check_eq(Service.username, "tarzan");
do_check_eq(Svc.Prefs.get("username"), "tarzan");
do_check_eq(ID.get("WeaveID").username, "tarzan");
do_check_eq(ID.get("WeaveCryptoID").username, "tarzan");
_("If not set, the 'account attribute' falls back to the username for backwards compatibility.");
do_check_eq(Service.account, "tarzan");
_("Setting 'username' to a non-truthy value resets the pref.");
Service.username = null;
do_check_eq(Service.username, "");
do_check_eq(Service.account, "");
const default_marker = {};
do_check_eq(Svc.Prefs.get("username", default_marker), default_marker);
do_check_eq(ID.get("WeaveID").username, null);
do_check_eq(ID.get("WeaveCryptoID").username, null);
_("The 'account' attribute will set the 'username' if it doesn't contain characters that aren't allowed in the username.");
Service.account = "johndoe";
do_check_eq(Service.account, "johndoe");
do_check_eq(Service.username, "johndoe");
do_check_eq(Svc.Prefs.get("username"), "johndoe");
do_check_eq(ID.get("WeaveID").username, "johndoe");
do_check_eq(ID.get("WeaveCryptoID").username, "johndoe");
_("If 'account' contains disallowed characters such as @, 'username' will the base32 encoded SHA1 hash of 'account'");
Service.account = "John@Doe.com";
do_check_eq(Service.account, "john@doe.com");
do_check_eq(Service.username, "7wohs32cngzuqt466q3ge7indszva4of");
_("Setting 'account' to a non-truthy value resets the pref.");
Service.account = null;
do_check_eq(Service.account, "");
do_check_eq(Svc.Prefs.get("account", default_marker), default_marker);
do_check_eq(Service.username, "");
do_check_eq(Svc.Prefs.get("username", default_marker), default_marker);
} finally {
Svc.Prefs.resetBranch("");
}
}
function test_urls() {
_("URL related Service properties corresopnd to preference settings.");
try {
@ -69,7 +18,7 @@ function test_urls() {
do_check_eq(Service.metaURL, undefined);
_("The 'clusterURL' attribute updates preferences and cached URLs.");
Service.username = "johndoe";
Identity.username = "johndoe";
// Since we don't have a cluster URL yet, these will still not be defined.
do_check_eq(Service.infoURL, undefined);
@ -106,10 +55,9 @@ function test_urls() {
"http://weave.server/weave-password-reset");
_("Empty/false value for 'username' resets preference.");
Service.username = "";
Identity.username = "";
do_check_eq(Svc.Prefs.get("username"), undefined);
do_check_eq(ID.get("WeaveID").username, "");
do_check_eq(ID.get("WeaveCryptoID").username, "");
do_check_eq(Identity.username, null);
_("The 'serverURL' attributes updates/resets preferences.");
// Identical value doesn't do anything
@ -164,7 +112,6 @@ function test_locked() {
}
function run_test() {
test_identities();
test_urls();
test_syncID();
test_locked();

View File

@ -1,3 +1,7 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
Cu.import("resource://services-sync/identity.js");
Cu.import("resource://services-sync/main.js");
Cu.import("resource://services-sync/util.js");
Cu.import("resource://services-sync/constants.js");
@ -28,13 +32,12 @@ add_test(function test_change_password() {
try {
Weave.Service.serverURL = TEST_SERVER_URL;
Weave.Service.clusterURL = TEST_CLUSTER_URL;
Weave.Service.username = "johndoe";
Weave.Service.password = "ilovejane";
setBasicCredentials("johndoe", "ilovejane");
_("changePassword() returns false for a network error, the password won't change.");
let res = Weave.Service.changePassword("ILoveJane83");
do_check_false(res);
do_check_eq(Weave.Service.password, "ilovejane");
do_check_eq(Identity.basicPassword, "ilovejane");
_("Let's fire up the server and actually change the password.");
server = httpd_setup({
@ -44,28 +47,28 @@ add_test(function test_change_password() {
res = Weave.Service.changePassword("ILoveJane83");
do_check_true(res);
do_check_eq(Weave.Service.password, "ILoveJane83");
do_check_eq(Identity.basicPassword, "ILoveJane83");
do_check_eq(requestBody, "ILoveJane83");
_("Make sure the password has been persisted in the login manager.");
let logins = Services.logins.findLogins({}, PWDMGR_HOST, null,
PWDMGR_PASSWORD_REALM);
do_check_eq(logins.length, 1);
do_check_eq(logins[0].password, "ILoveJane83");
_("A non-ASCII password is UTF-8 encoded.");
const moneyPassword = "moneyislike$£¥";
res = Weave.Service.changePassword(moneyPassword);
do_check_true(res);
do_check_eq(Weave.Service.password, moneyPassword);
do_check_eq(Identity.basicPassword, Utils.encodeUTF8(moneyPassword));
do_check_eq(requestBody, Utils.encodeUTF8(moneyPassword));
_("changePassword() returns false for a server error, the password won't change.");
Services.logins.removeAllLogins();
Weave.Service.username = "janedoe";
Weave.Service.password = "ilovejohn";
setBasicCredentials("janedoe", "ilovejohn");
res = Weave.Service.changePassword("ILoveJohn86");
do_check_false(res);
do_check_eq(Weave.Service.password, "ilovejohn");
do_check_eq(Identity.basicPassword, "ilovejohn");
} finally {
Weave.Svc.Prefs.resetBranch("");

View File

@ -16,7 +16,7 @@ function test_findCluster() {
let server;
try {
Service.serverURL = TEST_SERVER_URL;
Service.username = "johndoe";
Identity.account = "johndoe";
_("_findCluster() throws on network errors (e.g. connection refused).");
do_check_throws(function() {
@ -36,23 +36,23 @@ function test_findCluster() {
do_check_eq(cluster, "http://weave.user.node/");
_("A 'null' response is converted to null.");
Service.username = "jimdoe";
Identity.account = "jimdoe";
cluster = Service._findCluster();
do_check_eq(cluster, null);
_("If a 404 is encountered, the server URL is taken as the cluster URL");
Service.username = "janedoe";
Identity.account = "janedoe";
cluster = Service._findCluster();
do_check_eq(cluster, Service.serverURL);
_("A 400 response will throw an error.");
Service.username = "juliadoe";
Identity.account = "juliadoe";
do_check_throws(function() {
Service._findCluster();
});
_("Any other server response (e.g. 500) will throw an error.");
Service.username = "joedoe";
Identity.account = "joedoe";
do_check_throws(function() {
Service._findCluster();
});
@ -74,7 +74,7 @@ function test_setCluster() {
});
try {
Service.serverURL = TEST_SERVER_URL;
Service.username = "johndoe";
Identity.account = "johndoe";
_("Check initial state.");
do_check_eq(Service.clusterURL, "");
@ -88,9 +88,9 @@ function test_setCluster() {
do_check_eq(Service.clusterURL, "http://weave.user.node/");
_("A 'null' response won't make a difference either.");
Service.username = "jimdoe";
Identity.account = "jimdoe";
do_check_false(Service._setCluster());
do_check_eq(Service.clusterURL, "http://weave.user.node/");
do_check_eq(Service.clusterURL, "http://weave.user.node/");
} finally {
Svc.Prefs.resetBranch("");
@ -106,7 +106,7 @@ function test_updateCluster() {
});
try {
Service.serverURL = TEST_SERVER_URL;
Service.username = "johndoe";
Identity.account = "johndoe";
_("Check initial state.");
do_check_eq(Service.clusterURL, "");
@ -125,7 +125,7 @@ function test_updateCluster() {
do_check_eq(parseFloat(Svc.Prefs.get("lastClusterUpdate")), lastUpdate);
_("Time travel 30 mins into the past and the update will work.");
Service.username = "janedoe";
Identity.account = "janedoe";
Svc.Prefs.set("lastClusterUpdate", (lastUpdate - 30*60*1000).toString());
before = Date.now();

View File

@ -1,3 +1,6 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
Cu.import("resource://services-sync/main.js");
Cu.import("resource://services-sync/service.js");
Cu.import("resource://services-sync/engines.js");
@ -5,6 +8,7 @@ Cu.import("resource://services-sync/util.js");
Cu.import("resource://services-sync/status.js");
Cu.import("resource://services-sync/constants.js");
Cu.import("resource://services-sync/record.js");
Cu.import("resource://services-sync/keys.js");
Cu.import("resource://services-sync/engines/tabs.js");
Cu.import("resource://services-sync/log4moz.js");
@ -114,24 +118,24 @@ add_test(function v4_upgrade() {
let serverKeys;
let serverResp;
function retrieve_server_default() {
serverKeys = serverResp = serverDecrypted = null;
serverKeys = new CryptoWrapper("crypto", "keys");
serverResp = serverKeys.fetch(Weave.Service.cryptoKeysURL).response;
do_check_true(serverResp.success);
serverDecrypted = serverKeys.decrypt(Weave.Service.syncKeyBundle);
serverDecrypted = serverKeys.decrypt(Weave.Identity.syncKeyBundle);
_("Retrieved WBO: " + JSON.stringify(serverDecrypted));
_("serverKeys: " + JSON.stringify(serverKeys));
return serverDecrypted.default;
}
function retrieve_and_compare_default(should_succeed) {
let serverDefault = retrieve_server_default();
let localDefault = CollectionKeys.keyForCollection().keyPair;
let localDefault = CollectionKeys.keyForCollection().keyPairB64;
_("Retrieved keyBundle: " + JSON.stringify(serverDefault));
_("Local keyBundle: " + JSON.stringify(localDefault));
@ -141,15 +145,15 @@ add_test(function v4_upgrade() {
else
do_check_neq(JSON.stringify(serverDefault), JSON.stringify(localDefault));
}
// Uses the objects set above.
function set_server_keys(pair) {
serverDecrypted.default = pair;
serverKeys.cleartext = serverDecrypted;
serverKeys.encrypt(Weave.Service.syncKeyBundle);
serverKeys.encrypt(Weave.Identity.syncKeyBundle);
serverKeys.upload(Weave.Service.cryptoKeysURL);
}
_("Checking we have the latest keys.");
retrieve_and_compare_default(true);
@ -232,21 +236,15 @@ add_test(function v5_upgrade() {
Svc.Session = {
getBrowserState: function () JSON.stringify(myTabs)
};
Status.resetSync();
Weave.Service.username = "johndoe";
Weave.Service.password = "ilovejane";
Weave.Service.passphrase = passphrase;
setBasicCredentials("johndoe", "ilovejane", passphrase);
Weave.Service.serverURL = TEST_SERVER_URL;
Weave.Service.clusterURL = TEST_CLUSTER_URL;
//
// Test an upgrade where the contents of the server would cause us to error
// -- keys decrypted with a different sync key, for example.
//
_("Testing v4 -> v5 (or similar) upgrade.");
function update_server_keys(syncKeyBundle, wboName, collWBO) {
generateNewKeys();
@ -254,28 +252,27 @@ add_test(function v5_upgrade() {
serverKeys.encrypt(syncKeyBundle);
do_check_true(serverKeys.upload(Weave.Service.storageURL + collWBO).success);
}
_("Bumping version.");
// Bump version on the server.
let m = new WBORecord("meta", "global");
m.payload = {"syncID": "foooooooooooooooooooooooooo",
"storageVersion": STORAGE_VERSION + 1};
m.upload(Weave.Service.metaURL);
_("New meta/global: " + JSON.stringify(meta_global));
// Fill the keys with bad data.
let badKeys = new SyncKeyBundle(null, null, "aaaaaaaaaaaaaaaaaaaaaaaaaa");
badKeys.generateEntry();
let badKeys = new SyncKeyBundle("foobar", "aaaaaaaaaaaaaaaaaaaaaaaaaa");
update_server_keys(badKeys, "keys", "crypto/keys"); // v4
update_server_keys(badKeys, "bulk", "crypto/bulk"); // v5
// ... and get new ones.
_("Generating new keys.");
generateNewKeys();
// Now sync and see what happens. It should be a version fail, not a crypto
// fail.
_("Logging in.");
try {
Weave.Service.login("johndoe", "ilovejane", passphrase);
@ -289,7 +286,7 @@ add_test(function v5_upgrade() {
// Clean up.
Weave.Service.startOver();
} finally {
Weave.Svc.Prefs.resetBranch("");
server.stop(run_next_test);

View File

@ -11,8 +11,7 @@ let collections = {steam: 65.11328,
diesel: 2.25488281};
function run_test() {
Service.username = "johndoe";
Service.password = "ilovejane";
setBasicCredentials("johndoe", "ilovejane");
Service.serverURL = TEST_SERVER_URL;
Service.clusterURL = TEST_CLUSTER_URL;
@ -32,8 +31,9 @@ add_test(function test_success() {
do_check_true(Utils.deepEquals(info, collections));
// Ensure that the request is sent off with the right bits.
do_check_true(basic_auth_matches(handler.request, Service.username,
Service.password));
do_check_true(basic_auth_matches(handler.request,
Identity.username,
Identity.basicPassword));
let expectedUA = Services.appinfo.name + "/" + Services.appinfo.version +
" FxSync/" + WEAVE_VERSION + "." +
Services.appinfo.appBuildID + ".desktop";

View File

@ -79,15 +79,15 @@ add_test(function test_login_logout() {
do_check_false(Service.isLoggedIn);
_("Try again with username and password set.");
Service.username = "johndoe";
Service.password = "ilovejane";
Identity.account = "johndoe";
Identity.basicPassword = "ilovejane";
Service.login();
do_check_eq(Status.service, CLIENT_NOT_CONFIGURED);
do_check_eq(Status.login, LOGIN_FAILED_NO_PASSPHRASE);
do_check_false(Service.isLoggedIn);
_("Success if passphrase is set.");
Service.passphrase = "foo";
Identity.syncKey = "foo";
Service.login();
do_check_eq(Status.service, STATUS_OK);
do_check_eq(Status.login, LOGIN_SUCCEEDED);
@ -95,9 +95,7 @@ add_test(function test_login_logout() {
_("We can also pass username, password and passphrase to login().");
Service.login("janedoe", "incorrectpassword", "bar");
do_check_eq(Service.username, "janedoe");
do_check_eq(Service.password, "incorrectpassword");
do_check_eq(Service.passphrase, "bar");
setBasicCredentials("janedoe", "incorrectpassword", "bar");
do_check_eq(Status.service, LOGIN_FAILED);
do_check_eq(Status.login, LOGIN_FAILED_LOGIN_REJECTED);
do_check_false(Service.isLoggedIn);
@ -113,9 +111,7 @@ add_test(function test_login_logout() {
Svc.Obs.add("weave:service:setup-complete", function() {
notified = true;
});
Service.username = "";
Service.password = "";
Service.passphrase = "";
setBasicCredentials(null, null, null);
Service.login("janedoe", "ilovejohn", "bar");
do_check_true(notified);
do_check_eq(Status.service, STATUS_OK);
@ -138,9 +134,7 @@ add_test(function test_login_logout() {
add_test(function test_login_on_sync() {
let server = setup();
Service.username = "johndoe";
Service.password = "ilovejane";
Service.passphrase = "bar";
setBasicCredentials("johndoe", "ilovejane", "bar");
try {
_("Sync calls login.");
@ -202,10 +196,11 @@ add_test(function test_login_on_sync() {
mpLocked = true;
// Testing exception handling if master password dialog is canceled.
// Do this by stubbing out Service.passphrase.
let oldPP = Service.__lookupGetter__("passphrase");
_("Old passphrase function is " + oldPP);
Service.__defineGetter__("passphrase",
// Do this by monkeypatching.
let oldGetter = Identity.__lookupGetter__("syncKey");
let oldSetter = Identity.__lookupSetter__("syncKey");
_("Old passphrase function is " + oldGetter);
Identity.__defineGetter__("syncKey",
function() {
throw "User canceled Master Password entry";
});
@ -233,6 +228,9 @@ add_test(function test_login_on_sync() {
do_check_true(cSTCalled);
do_check_false(lockedSyncCalled);
Identity.__defineGetter__("syncKey", oldGetter);
Identity.__defineSetter__("syncKey", oldSetter);
// N.B., a bunch of methods are stubbed at this point. Be careful putting
// new tests after this point!

View File

@ -62,9 +62,7 @@ function run_test() {
"/user/1.0/johndoe/password": change_password
});
Service.username = "johndoe";
Service.password = JAPANESE;
Service.passphrase = "cantentsveryrelevantabbbb";
setBasicCredentials("johndoe", JAPANESE, "irrelevant");
Service.serverURL = TEST_SERVER_URL;
try {
@ -73,13 +71,14 @@ function run_test() {
do_check_false(Service.verifyLogin());
do_check_eq(server_password, "foobar");
_("Make the server password the low byte version of our password. Login should work and have transparently changed the password to the UTF8 version.");
_("Make the server password the low byte version of our password.");
server_password = LOWBYTES;
do_check_true(Service.verifyLogin());
do_check_eq(server_password, Utils.encodeUTF8(JAPANESE));
do_check_false(Service.verifyLogin());
do_check_eq(server_password, LOWBYTES);
_("Can't use a password that has the same low bytes as ours.");
Service.password = APPLES;
server_password = Utils.encodeUTF8(JAPANESE);
Identity.basicPassword = APPLES;
do_check_false(Service.verifyLogin());
do_check_eq(server_password, Utils.encodeUTF8(JAPANESE));

View File

@ -6,10 +6,8 @@ function run_test() {
try {
// Ensure we have a blank slate to start.
Services.logins.removeAllLogins();
Weave.Service.username = "johndoe";
Weave.Service.password = "ilovejane";
Weave.Service.passphrase = "abbbbbcccccdddddeeeeefffff";
setBasicCredentials("johndoe", "ilovejane", "abbbbbcccccdddddeeeeefffff");
_("Confirm initial environment is empty.");
let logins = Services.logins.findLogins({}, PWDMGR_HOST, null,

View File

@ -1,3 +1,6 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
Cu.import("resource://services-sync/engines.js");
Cu.import("resource://services-sync/service.js");
Cu.import("resource://services-sync/status.js");
@ -27,9 +30,8 @@ function run_test() {
add_test(function test_resetLocalData() {
// Set up.
Service.username = "foobar";
Service.password = "blablabla";
Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
setBasicCredentials("foobar", "blablabla", // Law Blog
"abcdeabcdeabcdeabcdeabcdea");
Status.enforceBackoff = true;
Status.backoffInterval = 42;
Status.minimumNextSync = 23;
@ -52,8 +54,8 @@ add_test(function test_resetLocalData() {
// Verify the site was nuked from orbit.
do_check_eq(Svc.Prefs.get("username"), undefined);
do_check_eq(Service.password, "");
do_check_eq(Service.passphrase, "");
do_check_eq(Identity.basicPassword, null);
do_check_eq(Identity.syncKey, null);
do_check_eq(Status.service, CLIENT_NOT_CONFIGURED);
do_check_false(Status.enforceBackoff);

View File

@ -1,3 +1,6 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
Cu.import("resource://services-sync/ext/Observers.js");
Cu.import("resource://services-sync/status.js");
Cu.import("resource://services-sync/identity.js");
@ -7,9 +10,11 @@ Cu.import("resource://services-sync/engines.js");
function run_test() {
_("When imported, Service.onStartup is called");
// Test fixtures
Svc.Prefs.set("registerEngines", "Tab,Bookmarks,Form,History");
Svc.Prefs.set("username", "johndoe");
new SyncTestingInfrastructure();
// Test fixtures
Identity.username = "johndoe";
Cu.import("resource://services-sync/service.js");
@ -21,10 +26,6 @@ function run_test() {
do_check_true(Utils.deepEquals([engine.name for each (engine in engines)],
['tabs', 'bookmarks', 'forms', 'history']));
_("Identities are registered.");
do_check_eq(ID.get('WeaveID').username, "johndoe");
do_check_eq(ID.get('WeaveCryptoID').username, "johndoe");
_("Observers are notified of startup");
do_test_pending();
do_check_false(Status.ready);

View File

@ -32,11 +32,7 @@ function run_test() {
try {
_("Set up test fixtures.");
Weave.Service.serverURL = TEST_SERVER_URL;
Weave.Service.clusterURL = TEST_CLUSTER_URL;
Weave.Service.username = "johndoe";
Weave.Service.password = "ilovejane";
Weave.Service.passphrase = "foo";
new SyncTestingInfrastructure("johndoe", "ilovejane", "foo");
SyncScheduler.globalScore = GLOBAL_SCORE;
// Avoid daily ping
Weave.Svc.Prefs.set("lastPing", Math.floor(Date.now() / 1000));
@ -52,7 +48,7 @@ function run_test() {
do_check_eq(Weave.Status.login, Weave.LOGIN_SUCCEEDED);
_("Simulate having changed the password somewhere else.");
Weave.Service.password = "ilovejosephine";
Identity.basicPassword = "ilovejosephine";
_("Let's try to sync.");
Weave.Service.sync();

View File

@ -1,8 +1,11 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
Cu.import("resource://services-sync/main.js");
Cu.import("resource://services-sync/util.js");
Cu.import("resource://services-sync/status.js");
Cu.import("resource://services-sync/constants.js");
Cu.import("resource://services-sync/record.js");
Cu.import("resource://services-sync/keys.js");
Cu.import("resource://services-sync/log4moz.js");
function run_test() {
@ -16,7 +19,7 @@ function run_test() {
let collectionsHelper = track_collections_helper();
let upd = collectionsHelper.with_updated_collection;
let collections = collectionsHelper.collections;
function wasCalledHandler(wbo) {
let handler = wbo.handler();
return function() {
@ -63,18 +66,18 @@ function run_test() {
_("Log in.");
Weave.Service.serverURL = TEST_SERVER_URL;
Weave.Service.clusterURL = TEST_CLUSTER_URL;
_("Checking Status.sync with no credentials.");
Weave.Service.verifyAndFetchSymmetricKeys();
do_check_eq(Status.sync, CREDENTIALS_CHANGED);
do_check_eq(Status.login, LOGIN_FAILED_INVALID_PASSPHRASE);
do_check_eq(Status.login, LOGIN_FAILED_NO_PASSPHRASE);
_("Log in with an old secret phrase, is upgraded to Sync Key.");
Weave.Service.login("johndoe", "ilovejane", "my old secret phrase!!1!");
_("End of login");
do_check_true(Weave.Service.isLoggedIn);
do_check_true(Utils.isPassphrase(Weave.Service.passphrase));
do_check_true(Utils.isPassphrase(Weave.Service.syncKeyBundle.keyStr));
let syncKey = Weave.Service.passphrase;
do_check_true(Utils.isPassphrase(Identity.syncKey));
let syncKey = Identity.syncKey;
Weave.Service.startOver();
Weave.Service.serverURL = TEST_SERVER_URL;
@ -85,7 +88,7 @@ function run_test() {
_("Checking that remoteSetup returns true when credentials have changed.");
Records.get(Weave.Service.metaURL).payload.syncID = "foobar";
do_check_true(Weave.Service._remoteSetup());
_("Do an initial sync.");
let beforeSync = Date.now()/1000;
Weave.Service.sync();
@ -120,25 +123,25 @@ function run_test() {
do_check_eq(metaModified, meta_global.modified);
_("Checking bad passphrases.");
let pp = Weave.Service.passphrase;
Weave.Service.passphrase = "notvalid";
let pp = Identity.syncKey;
Identity.syncKey = "notvalid";
do_check_false(Weave.Service.verifyAndFetchSymmetricKeys());
do_check_eq(Status.sync, CREDENTIALS_CHANGED);
do_check_eq(Status.login, LOGIN_FAILED_INVALID_PASSPHRASE);
Weave.Service.passphrase = pp;
Identity.syncKey = pp;
do_check_true(Weave.Service.verifyAndFetchSymmetricKeys());
// changePassphrase wipes our keys, and they're regenerated on next sync.
_("Checking changed passphrase.");
let existingDefault = CollectionKeys.keyForCollection();
let existingKeysPayload = keysWBO.payload;
let newPassphrase = "bbbbbabcdeabcdeabcdeabcdea";
Weave.Service.changePassphrase(newPassphrase);
_("Local key cache is full, but different.");
do_check_true(!!CollectionKeys._default);
do_check_false(CollectionKeys._default.equals(existingDefault));
_("Server has new keys.");
do_check_true(!!keysWBO.payload);
do_check_true(!!keysWBO.modified);
@ -149,12 +152,12 @@ function run_test() {
// server, just as might happen with a second client.
_("Attempting to screw up HMAC by re-encrypting keys.");
let keys = CollectionKeys.asWBO();
let b = new BulkKeyBundle("hmacerror", "hmacerror");
let b = new BulkKeyBundle("hmacerror");
b.generateRandom();
collections.crypto = keys.modified = 100 + (Date.now()/1000); // Future modification time.
keys.encrypt(b);
keys.upload(Weave.Service.cryptoKeysURL);
do_check_false(Weave.Service.verifyAndFetchSymmetricKeys());
do_check_eq(Status.login, LOGIN_FAILED_INVALID_PASSPHRASE);

View File

@ -66,19 +66,13 @@ function sync_httpd_setup(handlers) {
}
function setUp() {
Service.username = "johndoe";
Service.password = "ilovejane";
Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
Service.serverURL = TEST_SERVER_URL;
Service.clusterURL = TEST_CLUSTER_URL;
// So that we can poke at meta/global.
new FakeCryptoService();
new SyncTestingInfrastructure("johndoe", "ilovejane",
"abcdeabcdeabcdeabcdeabcdea");
// Ensure that the server has valid keys so that logging in will work and not
// result in a server wipe, rendering many of these tests useless.
generateNewKeys();
let serverKeys = CollectionKeys.asWBO("crypto", "keys");
serverKeys.encrypt(Service.syncKeyBundle);
serverKeys.encrypt(Identity.syncKeyBundle);
return serverKeys.upload(Service.cryptoKeysURL).success;
}
@ -260,7 +254,7 @@ add_test(function test_enabledRemotely() {
try {
_("Upload some keys to avoid a fresh start.");
let wbo = CollectionKeys.generateNewKeysWBO();
wbo.encrypt(Service.syncKeyBundle);
wbo.encrypt(Identity.syncKeyBundle);
do_check_eq(200, wbo.upload(Service.cryptoKeysURL).status);
_("Engine is disabled.");

View File

@ -58,8 +58,7 @@ function run_test() {
_("Try again with username and password set.");
Status.resetSync();
Service.username = "johndoe";
Service.password = "ilovejane";
setBasicCredentials("johndoe", "ilovejane", null);
do_check_false(Service.verifyLogin());
do_check_eq(Status.service, CLIENT_NOT_CONFIGURED);
do_check_eq(Status.login, LOGIN_FAILED_NO_PASSPHRASE);
@ -69,14 +68,15 @@ function run_test() {
_("Success if passphrase is set.");
Status.resetSync();
Service.passphrase = "foo";
Identity.syncKey = "foo";
do_check_true(Service.verifyLogin());
do_check_eq(Status.service, STATUS_OK);
do_check_eq(Status.login, LOGIN_SUCCEEDED);
_("If verifyLogin() encounters a server error, it flips on the backoff flag and notifies observers on a 503 with Retry-After.");
Status.resetSync();
Service.username = "janedoe";
Identity.account = "janedoe";
Service._updateCachedURLs();
do_check_false(Status.enforceBackoff);
let backoffInterval;
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {

View File

@ -31,8 +31,8 @@ function setUpTestFixtures() {
Service.serverURL = TEST_SERVER_URL;
Service.clusterURL = TEST_CLUSTER_URL;
Service.username = "johndoe";
Service.passphrase = "aabcdeabcdeabcdeabcdeabcde";
setBasicCredentials("johndoe", null, "aabcdeabcdeabcdeabcdeabcde");
}
@ -55,6 +55,7 @@ add_test(function test_wipeServer_list_success() {
try {
setUpTestFixtures();
new SyncTestingInfrastructure("johndoe", "irrelevant", "irrelevant");
_("Confirm initial environment.");
do_check_false(steam_coll.deleted);
@ -88,6 +89,7 @@ add_test(function test_wipeServer_list_503() {
try {
setUpTestFixtures();
new SyncTestingInfrastructure("johndoe", "irrelevant", "irrelevant");
_("Confirm initial environment.");
do_check_false(steam_coll.deleted);
@ -135,6 +137,7 @@ add_test(function test_wipeServer_all_success() {
setUpTestFixtures();
_("Try deletion.");
new SyncTestingInfrastructure("johndoe", "irrelevant", "irrelevant");
let returnedTimestamp = Service.wipeServer();
do_check_true(deleted);
do_check_eq(returnedTimestamp, serverTimestamp);
@ -166,6 +169,7 @@ add_test(function test_wipeServer_all_404() {
setUpTestFixtures();
_("Try deletion.");
new SyncTestingInfrastructure("johndoe", "irrelevant", "irrelevant");
let returnedTimestamp = Service.wipeServer();
do_check_true(deleted);
do_check_eq(returnedTimestamp, serverTimestamp);
@ -194,6 +198,7 @@ add_test(function test_wipeServer_all_503() {
_("Try deletion.");
let error;
try {
new SyncTestingInfrastructure("johndoe", "irrelevant", "irrelevant");
Service.wipeServer();
do_throw("Should have thrown!");
} catch (ex) {

View File

@ -1,13 +1,17 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
Cu.import("resource://services-sync/constants.js");
Cu.import("resource://services-sync/identity.js");
Cu.import("resource://services-sync/status.js");
Cu.import("resource://services-sync/util.js");
function run_test() {
initTestLogging("Trace");
try {
_("Verify initial setup.");
do_check_eq(ID.get("WeaveID"), null);
do_check_eq(ID.get("WeaveCryptoID"), null);
_("Ensure fresh config.");
Identity.deleteSyncCredentials();
_("Fresh setup, we're not configured.");
do_check_eq(Status.checkSetup(), CLIENT_NOT_CONFIGURED);
@ -15,27 +19,22 @@ function run_test() {
Status.resetSync();
_("Let's provide a username.");
Svc.Prefs.set("username", "johndoe");
Identity.username = "johndoe";
do_check_eq(Status.checkSetup(), CLIENT_NOT_CONFIGURED);
do_check_eq(Status.login, LOGIN_FAILED_NO_PASSWORD);
Status.resetSync();
_("checkSetup() created a WeaveID identity.");
let id = ID.get("WeaveID");
do_check_true(!!id);
do_check_neq(Identity.username, null);
_("Let's provide a password.");
id.password = "carotsalad";
Identity.basicPassword = "carotsalad";
do_check_eq(Status.checkSetup(), CLIENT_NOT_CONFIGURED);
do_check_eq(Status.login, LOGIN_FAILED_NO_PASSPHRASE);
Status.resetSync();
_("checkSetup() created a WeaveCryptoID identity");
id = ID.get("WeaveCryptoID");
do_check_true(!!id);
_("Let's provide a passphrase");
id.keyStr = "a-bcdef-abcde-acbde-acbde-acbde";
Identity.syncKey = "a-bcdef-abcde-acbde-acbde-acbde";
_("checkSetup()");
do_check_eq(Status.checkSetup(), STATUS_OK);
Status.resetSync();

View File

@ -95,7 +95,7 @@ function test_toFetch() {
do_check_eq(engine.toFetch[0], toFetch[0]);
do_check_eq(engine.toFetch[1], toFetch[1]);
} finally {
syncTesting = new SyncTestingInfrastructure(makeSteamEngine);
Svc.Prefs.resetBranch("");
}
}
@ -125,7 +125,7 @@ function test_previousFailed() {
do_check_eq(engine.previousFailed[0], previousFailed[0]);
do_check_eq(engine.previousFailed[1], previousFailed[1]);
} finally {
syncTesting = new SyncTestingInfrastructure(makeSteamEngine);
Svc.Prefs.resetBranch("");
}
}
@ -150,7 +150,6 @@ function test_resetClient() {
do_check_eq(engine.toFetch.length, 0);
do_check_eq(engine.previousFailed.length, 0);
} finally {
syncTesting = new SyncTestingInfrastructure(makeSteamEngine);
Svc.Prefs.resetBranch("");
}
}
@ -182,7 +181,6 @@ function test_wipeServer() {
} finally {
server.stop(do_test_finished);
syncTesting = new SyncTestingInfrastructure(makeSteamEngine);
Svc.Prefs.resetBranch("");
}
}

View File

@ -18,6 +18,13 @@ function cleanAndGo(server) {
server.stop(run_next_test);
}
function configureService(username, password) {
Service.clusterURL = TEST_CLUSTER_URL;
Identity.account = username || "foo";
Identity.basicPassword = password || "password";
}
function createServerAndConfigureClient() {
let engine = new RotaryEngine();

View File

@ -49,14 +49,12 @@ function sync_httpd_setup() {
}
function setUp() {
Service.username = "johndoe";
Service.password = "ilovejane";
Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
setBasicCredentials("johndoe", "ilovejane", "abcdeabcdeabcdeabcdeabcdea");
Service.clusterURL = TEST_CLUSTER_URL;
generateNewKeys();
let serverKeys = CollectionKeys.asWBO("crypto", "keys");
serverKeys.encrypt(Service.syncKeyBundle);
serverKeys.encrypt(Identity.syncKeyBundle);
return serverKeys.upload(Service.cryptoKeysURL).success;
}
@ -213,14 +211,16 @@ add_test(function test_calculateBackoff() {
// Test no interval larger than the maximum backoff is used if
// Status.backoffInterval is smaller.
Status.backoffInterval = 5;
let backoffInterval = Utils.calculateBackoff(50, MAXIMUM_BACKOFF_INTERVAL);
let backoffInterval = Utils.calculateBackoff(50, MAXIMUM_BACKOFF_INTERVAL,
Status.backoffInterval);
do_check_eq(backoffInterval, MAXIMUM_BACKOFF_INTERVAL);
// Test Status.backoffInterval is used if it is
// larger than MAXIMUM_BACKOFF_INTERVAL.
Status.backoffInterval = MAXIMUM_BACKOFF_INTERVAL + 10;
backoffInterval = Utils.calculateBackoff(50, MAXIMUM_BACKOFF_INTERVAL);
backoffInterval = Utils.calculateBackoff(50, MAXIMUM_BACKOFF_INTERVAL,
Status.backoffInterval);
do_check_eq(backoffInterval, MAXIMUM_BACKOFF_INTERVAL + 10);
@ -466,9 +466,7 @@ add_test(function test_autoconnect_nextSync_future() {
cleanUpAndGo();
});
Service.username = "johndoe";
Service.password = "ilovejane";
Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
setBasicCredentials("johndoe", "ilovejane", "abcdeabcdeabcdeabcdeabcdea");
SyncScheduler.delayedAutoConnect(0);
});
@ -480,9 +478,10 @@ add_test(function test_autoconnect_mp_locked() {
let origLocked = Utils.mpLocked;
Utils.mpLocked = function() true;
let origPP = Service.__lookupGetter__("passphrase");
delete Service.passphrase;
Service.__defineGetter__("passphrase", function() {
let origGetter = Identity.__lookupGetter__("syncKey");
let origSetter = Identity.__lookupSetter__("syncKey");
delete Identity.syncKey;
Identity.__defineGetter__("syncKey", function() {
_("Faking Master Password entry cancelation.");
throw "User canceled Master Password entry";
});
@ -495,8 +494,9 @@ add_test(function test_autoconnect_mp_locked() {
do_check_eq(Status.login, MASTER_PASSWORD_LOCKED);
Utils.mpLocked = origLocked;
delete Service.passphrase;
Service.__defineGetter__("passphrase", origPP);
delete Identity.syncKey;
Identity.__defineGetter__("syncKey", origGetter);
Identity.__defineSetter__("syncKey", origSetter);
cleanUpAndGo(server);
});
@ -849,10 +849,8 @@ add_test(function test_sync_503_Retry_After() {
add_test(function test_loginError_recoverable_reschedules() {
_("Verify that a recoverable login error schedules a new sync.");
Service.username = "johndoe";
Service.password = "ilovejane";
Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
Service.serverURL = TEST_SERVER_URL;
setBasicCredentials("johndoe", "ilovejane", "abcdeabcdeabcdeabcdeabcdea");
Service.serverURL = TEST_SERVER_URL;
Service.clusterURL = TEST_CLUSTER_URL;
Service.persistLogin();
Status.resetSync(); // reset Status.login
@ -893,10 +891,8 @@ add_test(function test_loginError_recoverable_reschedules() {
add_test(function test_loginError_fatal_clearsTriggers() {
_("Verify that a fatal login error clears sync triggers.");
Service.username = "johndoe";
Service.password = "ilovejane";
Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
Service.serverURL = TEST_SERVER_URL;
setBasicCredentials("johndoe", "ilovejane", "abcdeabcdeabcdeabcdeabcdea");
Service.serverURL = TEST_SERVER_URL;
Service.clusterURL = TEST_CLUSTER_URL;
Service.persistLogin();
Status.resetSync(); // reset Status.login

View File

@ -57,9 +57,7 @@ add_test(function test_auth() {
let handler = httpd_handler(200, "OK");
let server = httpd_setup({"/resource": handler});
let id = new Identity(PWDMGR_PASSWORD_REALM, "johndoe");
id.password = "ilovejane";
ID.set("WeaveID", id);
setBasicCredentials("johndoe", "ilovejane", "XXXXXXXXX");
let request = new SyncStorageRequest(STORAGE_REQUEST_RESOURCE_URL);
request.get(function (error) {
@ -67,7 +65,8 @@ add_test(function test_auth() {
do_check_eq(this.response.status, 200);
do_check_true(basic_auth_matches(handler.request, "johndoe", "ilovejane"));
ID.del("WeaveID");
Svc.Prefs.reset("");
server.stop(run_next_test);
});
});

View File

@ -1,6 +1,25 @@
Cu.import("resource://services-sync/engines/tabs.js");
Cu.import("resource://services-sync/util.js");
function test_lastUsed() {
let store = new TabEngine()._store;
_("Check extraction of last used times from tab objects.");
let expected = [
[0, {}],
[0, {extData: null}],
[0, {extData: {}}],
[0, {extData: {weaveLastUsed: null}}],
[123456789, {extData: {weaveLastUsed: "123456789"}}],
[123456789, {extData: {weaveLastUsed: 123456789}}],
[123456789, {extData: {weaveLastUsed: 123456789.12}}]
];
for each (let [ex, input] in expected) {
do_check_eq(ex, store.tabLastUsed(input));
}
}
function test_create() {
let store = new TabEngine()._store;
@ -61,7 +80,7 @@ function fakeSessionSvc(url, numtabs) {
if (numtabs) {
let tabs = obj.windows[0].tabs;
for (let i = 0; i < numtabs-1; i++)
tabs.push(Utils.deepCopy(tabs[0]));
tabs.push(deepCopy(tabs[0]));
}
return JSON.stringify(obj);
}
@ -112,6 +131,7 @@ function test_createRecord() {
}
function run_test() {
test_lastUsed();
test_create();
test_getAllTabs();
test_createRecord();

View File

@ -1,4 +1,8 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
Cu.import("resource://services-sync/constants.js");
Cu.import("resource://services-sync/identity.js");
Cu.import("resource://services-sync/main.js");
var btoa = Cu.import("resource://services-sync/util.js").btoa;
@ -21,11 +25,12 @@ function run_test() {
do_check_eq(normalized, "abcdeabcdeabcdeabcde");
// Now run through the upgrade.
Identity.account = "johndoe";
Weave.Service.syncID = "1234567890";
Weave.Service.passphrase = normalized; // UI normalizes.
do_check_false(Utils.isPassphrase(Weave.Service.passphrase));
Identity.syncKey = normalized; // UI normalizes.
do_check_false(Utils.isPassphrase(Identity.syncKey));
Weave.Service.upgradeSyncKey(Weave.Service.syncID);
let upgraded = Weave.Service.passphrase;
let upgraded = Identity.syncKey;
_("Upgraded: " + upgraded);
do_check_true(Utils.isPassphrase(upgraded));

View File

@ -2,7 +2,7 @@ Cu.import("resource://services-sync/util.js");
function run_test() {
let thing = {o: {foo: "foo", bar: ["bar"]}, a: ["foo", {bar: "bar"}]};
let ret = Utils.deepCopy(thing);
let ret = deepCopy(thing);
do_check_neq(ret, thing)
do_check_neq(ret.o, thing.o);
do_check_neq(ret.o.bar, thing.o.bar);

View File

@ -1,35 +0,0 @@
Cu.import("resource://services-sync/util.js");
function WinMock(href) {
this.location = {href: href};
this._open = true;
};
WinMock.prototype = {
addEventListener: function(type, listener) {
this._listener = listener;
},
close: function() {
if (this._listener) {
this._listener();
}
this._open = false;
}
};
function run_test() {
let w1 = new WinMock("chrome://win/win.xul");
Utils.ensureOneOpen(w1);
do_check_true(w1._open);
let w2 = new WinMock("chrome://win/win.xul");
Utils.ensureOneOpen(w2);
do_check_false(w1._open);
// close w2 and test that ensureOneOpen doesn't
// close it again
w2.close();
w2._open = true;
let w3 = new WinMock("chrome://win/win.xul");
Utils.ensureOneOpen(w3);
do_check_true(w2._open);
}

View File

@ -69,7 +69,8 @@ add_test(function test_load_logging() {
_("Verify that reads and read errors are logged.");
// Write a file with some invalid JSON
let file = Utils.getProfileFile("weave/log.json");
let filePath = "weave/log.json";
let file = FileUtils.getFile("ProfD", filePath.split("/"), true);
let fos = Cc["@mozilla.org/network/file-output-stream;1"]
.createInstance(Ci.nsIFileOutputStream);
let flags = FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE

View File

@ -1,12 +1,11 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
_("Make sure uri strings are converted to nsIURIs");
Cu.import("resource://services-sync/util.js");
// both the makeURI and makeURL functions are tested
// in this file
function run_test() {
_test_makeURI();
_test_makeURL();
}
function _test_makeURI() {
@ -65,35 +64,3 @@ function _test_makeURI() {
do_check_eq(Utils.makeURI("chrome://badstuff"), undefined);
do_check_eq(Utils.makeURI("this is a test"), undefined);
}
function _test_makeURL() {
_("Check http uri");
let uri = "http://mozillalabs.com/";
do_check_true(Utils.makeURL(uri) instanceof Ci.nsIURL);
_("Check https uri");
let uris = "https://mozillalabs.com/";
do_check_true(Utils.makeURL(uris) instanceof Ci.nsIURL);
let uric = "chrome://browser/content/browser.xul";
do_check_true(Utils.makeURL(uric) instanceof Ci.nsIURL);
_("Check about uri");
let uria = "about:weave";
let except1;
try {
Utils.makeURL(uria);
} catch(e) {
except1 = e;
}
do_check_true(!!except1);
_("Check invalid uri");
let except2;
try {
Utils.makeURL("mozillalabs.com");
} catch(e) {
except2 = e;
}
do_check_true(!!except2);
}

View File

@ -4,70 +4,76 @@ tail =
[test_load_modules.js]
# The manifest is roughly ordered from low-level to high-level. When making
# systemic sweeping changes, this makes it easier to identify errors closer to
# the source.
# Ensure we can import everything.
[test_load_modules.js]
# util contains a bunch of functionality used throughout.
[test_utils_atob.js]
[test_utils_catch.js]
[test_utils_deepCopy.js]
[test_utils_deepEquals.js]
[test_utils_deferGetSet.js]
[test_utils_deriveKey.js]
[test_utils_encodeBase32.js]
[test_utils_getErrorString.js]
[test_utils_getIcon.js]
[test_utils_hkdfExpand.js]
[test_utils_httpmac.js]
[test_utils_json.js]
[test_utils_lazyStrings.js]
[test_utils_lock.js]
[test_utils_makeGUID.js]
[test_utils_makeURI.js]
[test_utils_namedTimer.js]
[test_utils_notify.js]
[test_utils_passphrase.js]
[test_utils_pbkdf2.js]
[test_utils_sha1.js]
[test_utils_stackTrace.js]
[test_utils_utf8.js]
# We have a number of other libraries that are pretty much standalone.
[test_Observers.js]
[test_Preferences.js]
[test_addons_engine.js]
[test_addons_reconciler.js]
[test_addons_store.js]
[test_addons_tracker.js]
[test_async_chain.js]
[test_async_querySpinningly.js]
[test_auth_manager.js]
[test_bookmark_batch_fail.js]
[test_bookmark_engine.js]
[test_bookmark_legacy_microsummaries_support.js]
[test_bookmark_livemarks.js]
[test_bookmark_order.js]
[test_bookmark_places_query_rewriting.js]
[test_bookmark_record.js]
[test_bookmark_smart_bookmarks.js]
[test_bookmark_store.js]
[test_bookmark_tracker.js]
[test_clients_engine.js]
[test_clients_escape.js]
[test_collection_inc_get.js]
[test_collections_recovery.js]
[test_corrupt_keys.js]
[test_engine.js]
[test_engine_abort.js]
[test_enginemanager.js]
[test_errorhandler.js]
[test_errorhandler_filelog.js]
# Bug 676978: test hangs on Android (see also testing/xpcshell/xpcshell.ini)
skip-if = os == "android"
[test_errorhandler_sync_checkServerError.js]
# Bug 676978: test hangs on Android (see also testing/xpcshell/xpcshell.ini)
skip-if = os == "android"
[test_forms_store.js]
[test_forms_tracker.js]
[test_history_engine.js]
[test_history_store.js]
[test_history_tracker.js]
[test_hmac_error.js]
[test_httpd_sync_server.js]
[test_interval_triggers.js]
[test_log4moz.js]
[test_restrequest.js]
[test_jpakeclient.js]
# Bug 618233: this test produces random failures on Windows 7.
# Bug 676978: test hangs on Android (see also testing/xpcshell/xpcshell.ini)
skip-if = os == "win" || os == "android"
[test_keys.js]
[test_log4moz.js]
[test_node_reassignment.js]
[test_notifications.js]
[test_password_store.js]
[test_password_tracker.js]
[test_places_guid_downgrade.js]
[test_prefs_store.js]
[test_prefs_tracker.js]
[test_records_crypto.js]
[test_records_crypto_generateEntry.js]
[test_records_wbo.js]
# HTTP layers.
[test_resource.js]
[test_resource_async.js]
[test_resource_ua.js]
[test_restrequest.js]
[test_score_triggers.js]
[test_sendcredentials_controller.js]
[test_syncstoragerequest.js]
# Generic Sync types.
[test_collection_inc_get.js]
[test_collections_recovery.js]
[test_identity_manager.js]
[test_keys.js]
[test_records_crypto.js]
[test_records_wbo.js]
# Engine APIs.
[test_engine.js]
[test_engine_abort.js]
[test_enginemanager.js]
[test_syncengine.js]
[test_syncengine_sync.js]
# Bug 676978: test hangs on Android (see also testing/xpcshell/xpcshell.ini)
skip-if = os == "android"
[test_tracker_addChanged.js]
# Service semantics.
[test_service_attributes.js]
[test_service_changePassword.js]
[test_service_checkAccount.js]
@ -92,40 +98,53 @@ skip-if = os == "android"
[test_service_verifyLogin.js]
[test_service_wipeClient.js]
[test_service_wipeServer.js]
[test_status.js]
[test_status_checkSetup.js]
[test_syncengine.js]
[test_syncengine_sync.js]
[test_corrupt_keys.js]
[test_errorhandler.js]
[test_errorhandler_filelog.js]
# Bug 676978: test hangs on Android (see also testing/xpcshell/xpcshell.ini)
skip-if = os == "android"
[test_errorhandler_sync_checkServerError.js]
# Bug 676978: test hangs on Android (see also testing/xpcshell/xpcshell.ini)
skip-if = os == "android"
[test_hmac_error.js]
[test_interval_triggers.js]
[test_node_reassignment.js]
[test_notifications.js]
[test_score_triggers.js]
[test_sendcredentials_controller.js]
[test_status.js]
[test_status_checkSetup.js]
[test_syncscheduler.js]
[test_syncstoragerequest.js]
[test_upgrade_old_sync_key.js]
# Finally, we test each engine.
[test_addons_engine.js]
[test_addons_reconciler.js]
[test_addons_store.js]
[test_addons_tracker.js]
[test_bookmark_batch_fail.js]
[test_bookmark_engine.js]
[test_bookmark_legacy_microsummaries_support.js]
[test_bookmark_livemarks.js]
[test_bookmark_order.js]
[test_bookmark_places_query_rewriting.js]
[test_bookmark_record.js]
[test_bookmark_smart_bookmarks.js]
[test_bookmark_store.js]
[test_bookmark_tracker.js]
[test_clients_engine.js]
[test_clients_escape.js]
[test_forms_store.js]
[test_forms_tracker.js]
[test_history_engine.js]
[test_history_store.js]
[test_history_tracker.js]
[test_places_guid_downgrade.js]
[test_password_store.js]
[test_password_tracker.js]
[test_prefs_store.js]
[test_prefs_tracker.js]
[test_tab_engine.js]
[test_tab_store.js]
[test_tab_tracker.js]
[test_tracker_addChanged.js]
[test_upgrade_old_sync_key.js]
[test_utils_atob.js]
[test_utils_catch.js]
[test_utils_deepCopy.js]
[test_utils_deepEquals.js]
[test_utils_deferGetSet.js]
[test_utils_deriveKey.js]
[test_utils_encodeBase32.js]
[test_utils_ensureOneOpen.js]
[test_utils_getErrorString.js]
[test_utils_getIcon.js]
[test_utils_hkdfExpand.js]
[test_utils_httpmac.js]
[test_utils_json.js]
[test_utils_lazyStrings.js]
[test_utils_lock.js]
[test_utils_makeGUID.js]
[test_utils_makeURI.js]
[test_utils_namedTimer.js]
[test_utils_notify.js]
[test_utils_passphrase.js]
[test_utils_pbkdf2.js]
[test_utils_sha1.js]
[test_utils_stackTrace.js]
[test_utils_utf8.js]

View File

@ -102,9 +102,9 @@ var TPS = {
}
}
catch(e) {}
Weave.Service.account = prefs.getCharPref('tps.account.username');
Weave.Service.password = prefs.getCharPref('tps.account.password');
Weave.Service.passphrase = prefs.getCharPref('tps.account.passphrase');
Weave.Identity.account = prefs.getCharPref('tps.account.username');
Weave.Identity.basicPassword = prefs.getCharPref('tps.account.password');
Weave.Identity.syncKey = prefs.getCharPref('tps.account.passphrase');
Weave.Svc.Obs.notify("weave:service:setup-complete");
},

View File

@ -784,17 +784,17 @@ let TPS =
// a new sync account
Weave.Svc.Prefs.set("admin-secret", account["admin-secret"]);
let suffix = account["account-suffix"];
Service.account = "tps" + suffix + "@mozilla.com";
Service.password = "tps" + suffix + "tps" + suffix;
Service.passphrase = Weave.Utils.generatePassphrase();
Service.createAccount(Service.account,
Service.password,
Weave.Identity.account = "tps" + suffix + "@mozilla.com";
Weave.Identity.basicPassword = "tps" + suffix + "tps" + suffix;
Weave.Identity.syncKey = Weave.Utils.generatePassphrase();
Service.createAccount(Weave.Identity.account,
Weave.Identity.basicPassword,
"dummy1", "dummy2");
} else if (account["username"] && account["password"] &&
account["passphrase"]) {
Service.account = account["username"];
Service.password = account["password"];
Service.passphrase = account["passphrase"];
Weave.Identity.account = account["username"];
Weave.Identity.basicPassword = account["password"];
Weave.Identity.syncKey = account["passphrase"];
} else {
this.DumpError("Must specify admin-secret, or " +
"username/password/passphrase in the config file");

View File

@ -1 +1 @@
1.15.0
1.16.0

View File

@ -121,7 +121,7 @@ FormHistory.prototype = {
},
log : function (message) {
log : function log(message) {
if (!this.debug)
return;
dump("FormHistory: " + message + "\n");
@ -129,7 +129,7 @@ FormHistory.prototype = {
},
init : function() {
init : function init() {
Services.prefs.addObserver("browser.formfill.", this, true);
this.updatePrefs();
@ -175,7 +175,7 @@ FormHistory.prototype = {
},
addEntry : function (name, value) {
addEntry : function addEntry(name, value) {
if (!this.enabled ||
this.privBrowsingSvc && this.privBrowsingSvc.privateBrowsingEnabled)
return;
@ -188,7 +188,7 @@ FormHistory.prototype = {
let stmt;
if (id != -1) {
// Update existing entry
// Update existing entry.
let query = "UPDATE moz_formhistory SET timesUsed = timesUsed + 1, lastUsed = :lastUsed WHERE id = :id";
let params = {
lastUsed : now,
@ -209,7 +209,7 @@ FormHistory.prototype = {
}
} else {
// Add new entry
// Add new entry.
guid = this.generateGUID();
let query = "INSERT INTO moz_formhistory (fieldname, value, timesUsed, firstUsed, lastUsed, guid) " +
@ -239,7 +239,7 @@ FormHistory.prototype = {
},
removeEntry : function (name, value) {
removeEntry : function removeEntry(name, value) {
this.log("removeEntry for " + name + "=" + value);
let [id, guid] = this.getExistingEntryID(name, value);
@ -273,7 +273,7 @@ FormHistory.prototype = {
},
removeEntriesForName : function (name) {
removeEntriesForName : function removeEntriesForName(name) {
this.log("removeEntriesForName with name=" + name);
this.sendStringNotification("before-removeEntriesForName", name);
@ -307,7 +307,7 @@ FormHistory.prototype = {
},
removeAllEntries : function () {
removeAllEntries : function removeAllEntries() {
this.log("removeAllEntries");
this.sendNotification("before-removeAllEntries", null);
@ -327,7 +327,7 @@ FormHistory.prototype = {
this.sendNotification("removeAllEntries", null);
} catch (e) {
this.dbConnection.rollbackTransaction();
this.log("removeEntriesForName failed: " + e);
this.log("removeAllEntries failed: " + e);
throw e;
} finally {
if (stmt) {
@ -338,7 +338,7 @@ FormHistory.prototype = {
},
nameExists : function (name) {
nameExists : function nameExists(name) {
this.log("nameExists for name=" + name);
let stmt;
let query = "SELECT COUNT(1) AS numEntries FROM moz_formhistory WHERE fieldname = :fieldname";
@ -357,14 +357,14 @@ FormHistory.prototype = {
}
},
entryExists : function (name, value) {
entryExists : function entryExists(name, value) {
this.log("entryExists for " + name + "=" + value);
let [id, guid] = this.getExistingEntryID(name, value);
this.log("entryExists: id=" + id);
return (id != -1);
},
removeEntriesByTimeframe : function (beginTime, endTime) {
removeEntriesByTimeframe : function removeEntriesByTimeframe(beginTime, endTime) {
this.log("removeEntriesByTimeframe for " + beginTime + " to " + endTime);
this.sendIntNotification("before-removeEntriesByTimeframe", beginTime, endTime);
@ -399,20 +399,20 @@ FormHistory.prototype = {
this.dbConnection.commitTransaction();
},
moveToDeletedTable : function (values, params) {
moveToDeletedTable : function moveToDeletedTable(values, params) {
#ifdef ANDROID
this.log("move entries to deleted");
this.log("Moving entries to deleted table.");
let stmt;
try {
// move the entry to the deleted items table
// Move the entries to the deleted items table.
let query = "INSERT INTO moz_deleted_formhistory (guid, timeDeleted) ";
if (values) query += values;
stmt = this.dbCreateStatement(query, params);
stmt.execute();
} catch (e) {
this.log("move entry failed: " + e);
this.log("Moving deleted entries failed: " + e);
throw e;
} finally {
if (stmt) {
@ -456,7 +456,7 @@ FormHistory.prototype = {
/* ---- nsIObserver interface ---- */
observe : function (subject, topic, data) {
observe : function observe(subject, topic, data) {
switch(topic) {
case "nsPref:changed":
this.updatePrefs();