--- a/browser/base/content/sync/addDevice.js
+++ b/browser/base/content/sync/addDevice.js
@@ -78,17 +78,17 @@ let gSyncAddDevice = {
this.pin1.focus();
break;
case SYNC_KEY_PAGE:
this.wizard.canAdvance = false;
this.wizard.canRewind = true;
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;
this.wizard.canRewind = false;
this.wizard.getButton("cancel").hidden = true;
break;
}
},
@@ -108,19 +108,19 @@ let gSyncAddDevice = {
startTransfer: function startTransfer() {
this.errorRow.hidden = true;
// When onAbort is called, Weave may already be gone.
const JPAKE_ERROR_USERABORT = Weave.JPAKE_ERROR_USERABORT;
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);
},
onComplete: function onComplete() {
delete self._jpakeclient;
self.wizard.pageIndex = DEVICE_CONNECTED_PAGE;
// Schedule a Sync for soonish to fetch the data uploaded by the
--- a/browser/base/content/sync/genericChange.js
+++ b/browser/base/content/sync/genericChange.js
@@ -101,17 +101,17 @@ let Change = {
introText.textContent = this._str("new.recoverykey.introText");
this._dialog.getButton("finish").label
= this._str("new.recoverykey.acceptButton");
}
else {
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;
this._passphraseBox.focus();
document.title = this._str("change.recoverykey.title");
introText.textContent = this._str("change.synckey.introText2");
warningText.textContent = this._str("change.recoverykey.warningText");
this._dialog.getButton("finish").label
@@ -188,17 +188,17 @@ let Change = {
let passphrase = Weave.Utils.generatePassphrase();
this._passphraseBox.value = Weave.Utils.hyphenatePassphrase(passphrase);
this._dialog.getButton("finish").disabled = false;
},
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();
Weave.SyncScheduler.delayedAutoConnect(0);
}
else {
this._updateStatus("new.passphrase.status.incorrect", "error");
}
@@ -212,17 +212,17 @@ let Change = {
this._updateStatus("change.recoverykey.error", "error");
}
return false;
},
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();
}
else {
this._updateStatus("new.password.status.incorrect", "error");
}
}
--- a/browser/base/content/sync/setup.js
+++ b/browser/base/content/sync/setup.js
@@ -174,23 +174,23 @@ var gSyncSetup = {
} else {
this.wizard.pageIndex = EXISTING_ACCOUNT_CONNECT_PAGE;
}
},
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();
if ([Weave.LOGIN_FAILED_INVALID_PASSPHRASE,
Weave.LOGIN_FAILED_NO_PASSPHRASE,
Weave.LOGIN_SUCCEEDED].indexOf(Weave.Status.login) == -1) {
return;
}
@@ -204,37 +204,37 @@ var gSyncSetup = {
// changePassphrase() will sync, make sure we set the "firstSync" pref
// according to the user's pref.
Weave.Svc.Prefs.reset("firstSync");
this.setupInitialSync();
gSyncUtils.resetPassphrase(true);
},
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();
},
onLoginStart: function () {
this.toggleLoginFeedback(false);
},
onLoginEnd: function () {
this.toggleLoginFeedback(true);
},
sendCredentialsAfterSync: function () {
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);
Services.obs.addObserver("weave:service:sync:finish", send, false);
Services.obs.addObserver("weave:service:sync:error", send, false);
},
toggleLoginFeedback: function (stop) {
@@ -363,17 +363,17 @@ var gSyncSetup = {
else
str = availCheck;
}
}
this._setFeedbackMessage(feedback, valid, str);
this.status.email = valid;
if (valid)
- Weave.Service.account = value;
+ Weave.Identity.account = value;
this.checkFields();
},
onPasswordChange: function () {
let password = document.getElementById("weavePassword");
let pwconfirm = document.getElementById("weavePasswordConfirm");
let [valid, errorString] = gSyncUtils.validatePassword(password, pwconfirm);
@@ -499,34 +499,35 @@ var gSyncSetup = {
document.getElementById("weaveEmail").value);
let challenge = getField("challenge");
let response = getField("response");
let error = Weave.Service.createAccount(email, password,
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();
return false;
}
image.setAttribute("status", "error");
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.basicPssword =
+ 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();
}
return false;
case OPTIONS_PAGE:
let desc = document.getElementById("mergeChoiceRadio").selectedIndex;
// No confirmation needed on new account setup or merge option
// with existing account.
@@ -695,19 +696,19 @@ var gSyncSetup = {
document.getElementById("easySetupPIN1").value = pin.slice(0, 4);
document.getElementById("easySetupPIN2").value = pin.slice(4, 8);
document.getElementById("easySetupPIN3").value = pin.slice(8);
},
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();
},
onAbort: function onAbort(error) {
delete self._jpakeclient;
// Ignore if wizard is aborted.
--- a/browser/base/content/sync/utils.js
+++ b/browser/base/content/sync/utils.js
@@ -222,23 +222,23 @@ let gSyncUtils = {
validatePassword: function (el1, el2) {
let valid = false;
let val1 = el1.value;
let val2 = el2 ? el2.value : "";
let error = "";
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)
valid = true;
else if (val1.length < Weave.MIN_PASS_LENGTH)
error = "change.password.tooShort";
else if (val1 != val2)
error = "change.password.mismatch";
--- a/services/sync/modules/engines.js
+++ b/services/sync/modules/engines.js
@@ -568,17 +568,17 @@ SyncEngine.prototype = {
// URI length limitations.
guidFetchBatchSize: DEFAULT_GUID_FETCH_BATCH_SIZE,
mobileGUIDFetchBatchSize: DEFAULT_MOBILE_GUID_FETCH_BATCH_SIZE,
// How many records to process in a single batch.
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,
get cryptoKeysURL() this.storageURL + "crypto/keys",
get metaURL() this.storageURL + "meta/global",
get syncID() {
--- a/services/sync/modules/identity.js
+++ b/services/sync/modules/identity.js
@@ -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, "ID", function () {
- return new IDManager();
+XPCOMUtils.defineLazyGetter(this, "Identity", function() {
+ return new IdentityManager();
});
-// 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];
- }
-};
+/**
+ * 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")];
-/*
- * 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
- */
+ this._basicPassword = null;
+ this._basicPasswordAllowLookup = true;
+ this._basicPasswordUpdated = false;
+ this._syncKey = null;
+ this._syncKeyAllowLookup = true;
+ this._syncKeySet = false;
+ this._syncKeyBundle = null;
+}
+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);
+ },
-function Identity(realm, username, password) {
- this.realm = realm;
- this.username = username;
- this._password = password;
- if (password)
- this._passwordUTF8 = Utils.encodeUTF8(password);
-}
-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;
+ /**
+ * 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");
+ }
+
+ this.username = this.usernameFromAccount(value);
+ },
+
+ get username() {
+ return Svc.Prefs.get("username", null);
},
- set password(value) {
- this._password = value;
- this._passwordUTF8 = Utils.encodeUTF8(value);
+ /**
+ * 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.
},
- get passwordUTF8() {
- if (!this._passwordUTF8)
- this.password; // invoke password getter
- return this._passwordUTF8;
+ /**
+ * 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;
},
- 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);
+ /**
+ * 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;
+ }
+
+ let username = this.username;
+ if (!username) {
+ throw new Error("basicPassword cannot be set before username.");
}
- // 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._log.info("Basic password being updated.");
+ this._basicPassword = Utils.encodeUTF8(value);
+ this._basicPasswordUpdated = true;
+ },
+
+ /**
+ * 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;
}
- // 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);
+ 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();
},
- get _logins() Services.logins.findLogins({}, PWDMGR_HOST, null, this.realm)
+ 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));
+ }
};
new file mode 100644
--- /dev/null
+++ b/services/sync/modules/keys.js
@@ -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);
+ },
+};
+
--- a/services/sync/modules/main.js
+++ b/services/sync/modules/main.js
@@ -35,34 +35,34 @@
*
* ***** END LICENSE BLOCK ***** */
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'],
"engines/clients.js": ["Clients"],
"engines/forms.js": ["FormEngine"],
"engines/history.js": ["HistoryEngine"],
"engines/prefs.js": ["PrefsEngine"],
"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']
};
function lazyImport(module, dest, props) {
function getter(prop) function() {
let ns = {};
--- a/services/sync/modules/policies.js
+++ b/services/sync/modules/policies.js
@@ -415,17 +415,19 @@ let SyncScheduler = {
},
/**
* Incorporates the backoff/retry logic used in error handling and elective
* 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);
}
this._log.debug("Starting client-initiated backoff. Next sync in " +
interval + " ms.");
this.scheduleNextSync(interval);
},
@@ -902,19 +904,19 @@ SendCredentialsController.prototype = {
// This will call onAbort which will call unload().
this.jpakeclient.abort();
break;
}
},
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);
},
// JPAKEClient controller API
onComplete: function onComplete() {
this._log.debug("Exchange was completed successfully!");
--- a/services/sync/modules/record.js
+++ b/services/sync/modules/record.js
@@ -33,29 +33,29 @@
* 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 ***** */
const EXPORTED_SYMBOLS = ["WBORecord", "RecordManager", "Records",
- "CryptoWrapper", "CollectionKeys", "BulkKeyBundle",
- "SyncKeyBundle", "Collection"];
+ "CryptoWrapper", "CollectionKeys", "Collection"];
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
const Cu = Components.utils;
const CRYPTO_COLLECTION = "crypto";
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");
function WBORecord(collection, id) {
this.data = {};
this.payload = {};
this.collection = collection; // Optional.
@@ -193,63 +193,66 @@ function CryptoWrapper(collection, id) {
this.id = id;
}
CryptoWrapper.prototype = {
__proto__: WBORecord.prototype,
_logName: "Sync.Record.CryptoWrapper",
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));
},
/*
* Don't directly use the sync key. Instead, grab a key for this
* collection, which is decrypted with the sync key.
*
* Cache those keys; invalidate the cache if the time on the keys collection
* changes, or other auth events occur.
*
* Optional key bundle overrides the collection key lookup.
*/
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;
},
// Optional key bundle.
decrypt: function decrypt(keyBundle) {
if (!this.ciphertext) {
throw "No ciphertext: nothing to decrypt?";
}
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);
if (computedHMAC != this.hmac) {
Utils.throwHMACMismatch(this.hmac, computedHMAC);
}
// 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)) {
this.cleartext = json_result;
this.ciphertext = null;
} else {
throw "Decryption failed: result is <" + json_result + ">, not an object.";
}
@@ -288,18 +291,17 @@ Utils.deferGetSet(CryptoWrapper, "payloa
Utils.deferGetSet(CryptoWrapper, "cleartext", "deleted");
XPCOMUtils.defineLazyGetter(this, "CollectionKeys", function () {
return new CollectionKeyManager();
});
/**
- * 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.
*/
function CollectionKeyManager() {
this.lastModified = 0;
this._collections = {};
this._default = null;
@@ -360,20 +362,20 @@ CollectionKeyManager.prototype = {
* If `collections` (an array of strings) is provided, iterate
* over it and generate random keys for each collection.
* Create a WBO for the given data.
*/
_makeWBO: function(collections, defaultBundle) {
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
};
return wbo;
},
/**
@@ -381,23 +383,23 @@ CollectionKeyManager.prototype = {
*/
asWBO: function(collection, id)
this._makeWBO(this._collections, this._default),
/**
* 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;
});
}
return [newDefaultKey, newColls];
},
/**
@@ -454,30 +456,30 @@ CollectionKeyManager.prototype = {
if (!payload.default) {
this._log.warn("No downloaded default key: this should not occur.");
this._log.warn("Not clearing local keys.");
throw "No default key in CollectionKeys.setContents(). Cannot proceed.";
}
// 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.
let newCollections = {};
if ("collections" in payload) {
this._log.info("Processing downloaded per-collection keys.");
let colls = payload.collections;
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;
}
}
}
}
// Check to see if these are already our keys.
@@ -523,212 +525,16 @@ CollectionKeyManager.prototype = {
}
let r = this.setContents(payload, storage_keys.modified);
log.info("Collection keys updated.");
return r;
}
}
-/**
- * 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;
this._full = false;
this._ids = null;
this._limit = 0;
this._older = 0;
--- a/services/sync/modules/resource.js
+++ b/services/sync/modules/resource.js
@@ -32,91 +32,34 @@
* 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 ***** */
-const EXPORTED_SYMBOLS = ["Resource", "AsyncResource",
- "Auth", "BrokenBasicAuthenticator",
- "BasicAuthenticator", "NoOpAuthenticator"];
+const EXPORTED_SYMBOLS = [
+ "AsyncResource",
+ "Resource"
+];
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
const Cu = Components.utils;
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:
*
* let resource = new AsyncResource("http://foobar.com/path/to/resource");
*
* The 'resource' object has the following methods to issue HTTP requests
* of the corresponding HTTP methods:
@@ -145,16 +88,24 @@ function AsyncResource(uri) {
AsyncResource.prototype = {
_logName: "Sync.AsyncResource",
// ** {{{ AsyncResource.serverTime }}} **
//
// 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
//
// Firefox/4.0 FxSync/1.8.0.20100101.mobile
//
// or
//
// Firefox Aurora/5.0a1 FxSync/1.9.0.20110409.desktop
@@ -162,39 +113,23 @@ AsyncResource.prototype = {
_userAgent:
Services.appinfo.name + "/" + Services.appinfo.version + // Product.
" FxSync/" + WEAVE_VERSION + "." + // Sync.
Services.appinfo.appBuildID + ".", // Build.
// 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;
},
setHeader: function Res_setHeader(header, value) {
this._headers[header.toLowerCase()] = value;
},
@@ -230,17 +165,17 @@ AsyncResource.prototype = {
},
// ** {{{ AsyncResource._createRequest }}} **
//
// This method returns a new IO Channel for requests to be made
// 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);
// Always validate the cache:
channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
channel.loadFlags |= Ci.nsIRequest.INHIBIT_CACHING;
@@ -248,34 +183,49 @@ AsyncResource.prototype = {
channel.notificationCallbacks = new ChannelNotificationListener();
// Compose a UA string fragment from the various available identifiers.
if (Svc.Prefs.get("sendVersionInfo", true)) {
let ua = this._userAgent + Svc.Prefs.get("client.type", "desktop");
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
this._log.trace("HTTP Header " + key + ": " + headers[key]);
channel.setRequestHeader(key, headers[key], false);
}
return channel;
},
_onProgress: function Res__onProgress(channel) {},
_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;
// PUT and POST are treated differently because they have payload data.
if ("PUT" == action || "POST" == action) {
// Convert non-string bodies into JSON
if (this._data.constructor.toString() != String)
--- a/services/sync/modules/rest.js
+++ b/services/sync/modules/rest.js
@@ -649,23 +649,21 @@ SyncStorageRequest.prototype = {
dispatch: function dispatch(method, data, onComplete, onProgress) {
// Compose a UA string fragment from the various available identifiers.
if (Svc.Prefs.get("sendVersionInfo", true)) {
let ua = this.userAgent + Svc.Prefs.get("client.type", "desktop");
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);
},
onStartRequest: function onStartRequest(channel) {
RESTRequest.prototype.onStartRequest.call(this, channel);
if (this.status == this.ABORTED) {
--- a/services/sync/modules/service.js
+++ b/services/sync/modules/service.js
@@ -86,63 +86,23 @@ const STORAGE_INFO_TYPES = [INFO_COLLECT
function WeaveSvc() {
this._notify = Utils.notify("weave:service:");
}
WeaveSvc.prototype = {
_lock: Utils.lock,
_locked: false,
_loggedIn: false,
-
- 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;
- },
+ _identity: Weave.Identity,
- 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) {
// Only do work if it's actually changing
if (value == this.serverURL)
return;
// A new server most likely uses a different cluster, so clear that
@@ -213,21 +173,21 @@ WeaveSvc.prototype = {
}
}
return Utils.catch.call(this, func, lockExceptions);
},
_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
this.infoURL = this.userBaseURL + "info/collections";
this.storageURL = this.userBaseURL + "storage/";
this.metaURL = this.storageURL + "meta/global";
this.cryptoKeysURL = this.storageURL + CRYPTO_COLLECTION + "/" + KEYS_WBO;
},
@@ -296,17 +256,17 @@ WeaveSvc.prototype = {
// CollectionKeys, this will prevent us from uploading junk.
let cipherText = cryptoKeys.ciphertext;
if (!cryptoResp.success) {
this._log.warn("Failed to download keys.");
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.
this._log.info("Suggesting retry.");
return true; // Try again.
}
// If not, reupload them and continue the current sync.
@@ -385,47 +345,40 @@ WeaveSvc.prototype = {
"Weave, since it will not work correctly.");
}
Svc.Obs.add("weave:service:setup-complete", this);
Svc.Prefs.observe("engine.", this);
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
// registering an observer.
Utils.nextTick(function() {
Status.ready = true;
Svc.Obs.notify("weave:service:ready");
});
},
- _checkSetup: function WeaveSvc__checkSetup() {
- if (!this.enabled)
+ _checkSetup: function _checkSetup() {
+ if (!this.enabled) {
return Status.service = STATUS_DISABLED;
+ }
return Status.checkSetup();
},
_migratePrefs: function _migratePrefs() {
// Migrate old debugLog prefs.
let logLevel = Svc.Prefs.get("log.appender.debugLog");
if (logLevel) {
Svc.Prefs.set("log.appender.file.level", logLevel);
@@ -502,20 +455,20 @@ WeaveSvc.prototype = {
} else {
// Remember that the engine status changed locally until the next sync.
Svc.Prefs.set("engineStatusChanged." + engine, true);
}
},
// 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) {
case 400:
Status.login = LOGIN_FAILED_LOGIN_REJECTED;
fail = "Find cluster denied: " + ErrorHandler.errorStr(node);
break;
case 404:
@@ -596,26 +549,26 @@ WeaveSvc.prototype = {
verifyAndFetchSymmetricKeys: function verifyAndFetchSymmetricKeys(infoResponse) {
this._log.debug("Fetching and verifying -- or generating -- symmetric keys.");
// Don't allow empty/missing passphrase.
// 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.
return false;
}
// 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;
return false;
}
try {
if (!infoResponse)
@@ -642,17 +595,17 @@ WeaveSvc.prototype = {
let cryptoKeys;
if (infoCollections && (CRYPTO_COLLECTION in infoCollections)) {
try {
cryptoKeys = new CryptoWrapper(CRYPTO_COLLECTION, KEYS_WBO);
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) {
// On failure, ask CollectionKeys to generate new keys and upload them.
// Fall through to the behavior below.
this._log.warn("Got 404 for crypto/keys, but 'crypto' in info/collections. Regenerating.");
cryptoKeys = null;
}
@@ -710,29 +663,29 @@ WeaveSvc.prototype = {
+ Utils.exceptionStr(ex));
ErrorHandler.checkServerError(ex);
return false;
}
},
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;
}
// Unlock master password, or return.
// Attaching auth credentials to a request requires access to
// passwords, which means that Resource.get can throw MP-related
// 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.");
Status.login = MASTER_PASSWORD_LOCKED;
return false;
}
try {
@@ -750,17 +703,17 @@ WeaveSvc.prototype = {
switch (test.status) {
case 200:
// The user is authenticated.
// 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;
}
// Go ahead and do remote setup, so that we can determine
// conclusively that our passphrase is correct.
if (this._remoteSetup()) {
@@ -770,36 +723,18 @@ WeaveSvc.prototype = {
}
this._log.warn("Remote setup failed.");
// Remote setup must have failed.
return false;
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())
return this.verifyLogin();
// We must have the right cluster, but the server doesn't expect us
Status.login = LOGIN_FAILED_LOGIN_REJECTED;
return false;
@@ -820,17 +755,17 @@ WeaveSvc.prototype = {
}
})(),
generateNewSymmetricKeys:
function WeaveSvc_generateNewSymmetricKeys() {
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);
if (uploadRes.status != 200) {
this._log.warn("Got status " + uploadRes.status + " uploading new keys. What to do? Throw!");
ErrorHandler.checkServerError(uploadRes);
throw new Error("Unable to upload symmetric keys.");
}
@@ -866,74 +801,74 @@ WeaveSvc.prototype = {
// Download and install them.
let cryptoKeys = new CryptoWrapper(CRYPTO_COLLECTION, KEYS_WBO);
let cryptoResp = cryptoKeys.fetch(this.cryptoKeysURL).response;
if (cryptoResp.status != 200) {
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.");
}
},
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) {
this._log.debug("Password change failed: " + resp);
return false;
}
}
catch(ex) {
// Must have failed on some network issue
this._log.debug("changePassword failed: " + Utils.exceptionStr(ex));
return false;
}
// Save the new password for requests and login manager.
- this.password = newpass;
+ this._identity.basicPassword = newpass;
this.persistLogin();
return true;
})(),
changePassphrase: function WeaveSvc_changePassphrase(newphrase)
this._catch(this._notify("changepph", "", function() {
/* Wipe. */
this.wipeServer();
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. */
this.resetClient();
CollectionKeys.clear();
/* Login and sync. This also generates new keys. */
this.sync();
return true;
}))(),
- startOver: function() {
+ startOver: function startOver() {
this._log.trace("Invoking Service.startOver.");
Svc.Obs.notify("weave:engine:stop-tracking");
Status.resetSync();
// 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");
// Deletion doesn't make sense if we aren't set up yet!
if (this.clusterURL != "") {
// Clear client-specific data from the server, including disabled engines.
for each (let engine in [Clients].concat(Engines.getAll())) {
@@ -954,86 +889,87 @@ WeaveSvc.prototype = {
Status.resetBackoff();
// Reset Weave prefs.
this._ignorePrefObserver = true;
Svc.Prefs.resetBranch("");
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;
if (Services.io.offline) {
Status.login = LOGIN_FAILED_NETWORK_ERROR;
throw "Application is offline, login should not be called";
}
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.
throw "Login failed: " + Status.login;
}
this._loggedIn = true;
return true;
})))(),
- logout: function WeaveSvc_logout() {
+ logout: function logout() {
// No need to do anything if we're already logged out.
if (!this._loggedIn)
return;
this._log.info("Logging out");
this._loggedIn = false;
Svc.Obs.notify("weave:service:logout:finish");
},
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 {
data = res.get();
if (data.status == 200) {
if (data == "0")
return "available";
else if (data == "1")
@@ -1044,27 +980,26 @@ WeaveSvc.prototype = {
catch(ex) {}
// Convert to the error string, or default to generic on exception.
return ErrorHandler.errorStr(data);
},
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,
"captcha-challenge": captchaChallenge,
"captcha-response": captchaResponse
});
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.
if (Svc.Prefs.isSet("admin-secret"))
res.setHeader("X-Weave-Secret", Svc.Prefs.get("admin-secret", ""));
let error = "generic-server-error";
try {
@@ -1101,17 +1036,17 @@ WeaveSvc.prototype = {
// Delete the cached meta record...
this._log.debug("Clearing cached meta record. metaModified is " +
JSON.stringify(this.metaModified) + ", setting to " +
JSON.stringify(infoResponse.obj.meta));
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.");
newMeta = new WBORecord("meta", "global");
newMeta.payload.syncID = this.syncID;
newMeta.payload.storageVersion = STORAGE_VERSION;
newMeta.isNew = true;
@@ -1504,60 +1439,51 @@ WeaveSvc.prototype = {
// appropriate value.
return false;
}
}
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.
*
* Store the new 'passphrase' back into the identity manager.
*
* We can check this as often as we want, because once it's done the
* check will no longer succeed. It only matters that it happens after
* 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...
let s = btoa(syncID); // It's what WeaveCrypto expects. *sigh*
let k = Utils.derivePresentableKeyFromPassphrase(p, s, PBKDF2_KEY_BYTES); // Base 32.
if (!k) {
this._log.error("No key resulted from derivePresentableKeyFromPassphrase. Failing upgrade.");
return false;
}
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.");
return true;
},
_freshStart: function WeaveSvc__freshStart() {
this._log.info("Fresh start. Resetting client and considering key upgrade.");
--- a/services/sync/modules/status.js
+++ b/services/sync/modules/status.js
@@ -37,20 +37,22 @@ const EXPORTED_SYMBOLS = ["Status"];
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
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() {
return this._service;
},
set service(code) {
this._log.debug("Status.service: " + this._service + " => " + code);
@@ -60,20 +62,20 @@ let Status = {
get login() {
return this._login;
},
set login(code) {
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 {
this.service = STATUS_OK;
}
},
get sync() {
@@ -104,56 +106,24 @@ let Status = {
toString: function toString() {
return "<Status" +
": login: " + Status.login +
", service: " + Status.service +
", sync: " + Status.sync + ">";
},
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() {
this.enforceBackoff = false;
this.backoffInterval = 0;
this.minimumNextSync = 0;
},
--- a/services/sync/modules/util.js
+++ b/services/sync/modules/util.js
@@ -44,17 +44,16 @@ const Cr = Components.results;
const Cu = Components.utils;
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/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");
Cu.import("resource://gre/modules/NetUtil.jsm");
Cu.import("resource://gre/modules/FileUtils.jsm");
/*
* Utility functions
@@ -1228,22 +1227,24 @@ let Utils = {
return false;
},
/**
* Return a value for a backoff interval. Maximum is eight hours, unless
* 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() {
let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
.createInstance(Ci.nsIScriptableUnicodeConverter);
converter.charset = "UTF-8";
return converter;
});
--- a/services/sync/tests/unit/head_helpers.js
+++ b/services/sync/tests/unit/head_helpers.js
@@ -1,12 +1,13 @@
/* Any copyright is dedicated to the Public Domain.
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");
let btoa;
let atob;
let provider = {
getFile: function(prop, persistent) {
@@ -255,24 +256,32 @@ FakeCryptoService.prototype = {
return "some derived key string composed of bytes";
},
generateRandomBytes: function(aByteCount) {
return "not-so-random-now-are-we-HA-HA-HA! >:)".slice(aByteCount);
}
};
-
-function SyncTestingInfrastructure() {
- Cu.import("resource://services-sync/identity.js");
+function setBasicCredentials(username, password, syncKey) {
+ let auth = Identity;
+ auth.username = username;
+ auth.basicPassword = password;
+ auth.syncKey = syncKey;
+}
- ID.set('WeaveID',
- new Identity('Mozilla Services Encryption Passphrase', 'foo'));
- ID.set('WeaveCryptoID',
- new Identity('Mozilla Services Encryption Passphrase', 'foo'));
+function SyncTestingInfrastructure(username, password, syncKey) {
+ Cu.import("resource://services-sync/service.js");
+
+ 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({});
this.fakeGUIDService = new FakeGUIDService();
this.fakeCryptoService = new FakeCryptoService();
}
/*
--- a/services/sync/tests/unit/head_http_server.js
+++ b/services/sync/tests/unit/head_http_server.js
@@ -59,18 +59,22 @@ function httpd_handler(statusCode, statu
};
}
function basic_auth_header(user, password) {
return "Basic " + btoa(user + ":" + Utils.encodeUTF8(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) {
if (basic_auth_matches(metadata, "guest", "guest")) {
response.setStatusLine(metadata.httpVersion, 200, "OK, authorized");
response.setHeader("WWW-Authenticate", 'Basic realm="secret"', false);
} else {
body = "This path exists and is protected - failed";
--- a/services/sync/tests/unit/test_addons_engine.js
+++ b/services/sync/tests/unit/test_addons_engine.js
@@ -149,21 +149,17 @@ add_test(function test_disabled_install_
Svc.Prefs.set("addons.ignoreRepositoryChecking", true);
const USER = "foo";
const PASSWORD = "password";
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();
let contents = {
meta: {global: {engines: {addons: {version: engine.version,
syncID: engine.syncID}}}},
crypto: {},
addons: {}
deleted file mode 100644
--- a/services/sync/tests/unit/test_auth_manager.js
+++ /dev/null
@@ -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);
- }
-}
--- a/services/sync/tests/unit/test_bookmark_engine.js
+++ b/services/sync/tests/unit/test_bookmark_engine.js
@@ -87,20 +87,17 @@ function serverForFoo(engine) {
meta: {global: {engines: {bookmarks: {version: engine.version,
syncID: engine.syncID}}}},
bookmarks: {}
});
}
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;
let server = serverForFoo(engine);
let collection = server.user("foo").collection("bookmarks");
try {
@@ -160,20 +157,17 @@ add_test(function test_processIncoming_e
Svc.Prefs.resetBranch("");
Records.clearCache();
server.stop(run_next_test);
}
});
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;
let server = serverForFoo(engine);
let collection = server.user("foo").collection("bookmarks");
Svc.Obs.notify("weave:engine:start-tracking"); // We skip usual startup...
@@ -328,20 +322,17 @@ add_test(function test_mismatched_types(
"description":null,
"children":
["HCRq40Rnxhrd", "YeyWCV1RVsYw", "GCceVZMhvMbP", "sYi2hevdArlF",
"vjbZlPlSyGY8", "UtjUhVyrpeG6", "rVq8WMG2wfZI", "Lx0tcy43ZKhZ",
"oT74WwV8_j4P", "IztsItWVSo3-"],
"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;
let server = serverForFoo(engine);
_("GUID: " + store.GUIDForId(6, true));
try {
@@ -374,20 +365,18 @@ add_test(function test_mismatched_types(
Records.clearCache();
server.stop(run_next_test);
}
});
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;
let store = engine._store;
let server = serverForFoo(engine);
let coll = server.user("foo").collection("bookmarks");
// Add one item to the server.
--- a/services/sync/tests/unit/test_bookmark_record.js
+++ b/services/sync/tests/unit/test_bookmark_record.js
@@ -1,26 +1,30 @@
+/* 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";
return b;
}
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());
log.info("Creating a record");
let u = "http://localhost:8080/storage/bookmarks/foo";
let placesItem = new PlacesItem("bookmarks", "foo", "bookmark");
let bookmarkItem = prepareBookmarkItem("bookmarks", "foo");
--- a/services/sync/tests/unit/test_bookmark_smart_bookmarks.js
+++ b/services/sync/tests/unit/test_bookmark_smart_bookmarks.js
@@ -53,16 +53,18 @@ function serverForFoo(engine) {
syncID: engine.syncID}}}},
bookmarks: {}
});
}
// 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);
if (startCount > 0) {
// This can happen in XULRunner.
clearBookmarks();
_("Start count is now " + startCount);
@@ -101,20 +103,16 @@ add_test(function test_annotation_upload
_("Make sure the new record carries with it the annotation.");
do_check_eq("MostVisited", record.queryId);
_("Our count has increased since we started.");
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");
try {
engine.sync();
let wbos = collection.keys(function (id) {
return ["menu", "toolbar", "mobile"].indexOf(id) == -1;
});
@@ -173,34 +171,32 @@ add_test(function test_annotation_upload
store.wipe();
Svc.Prefs.resetBranch("");
Records.clearCache();
server.stop(run_next_test);
}
});
add_test(function test_smart_bookmarks_duped() {
+ new SyncTestingInfrastructure();
+
let parent = PlacesUtils.toolbarFolderId;
let uri =
Utils.makeURI("place:redirectsMode=" +
Ci.nsINavHistoryQueryOptions.REDIRECTS_MODE_TARGET +
"&sort=" +
Ci.nsINavHistoryQueryOptions.SORT_BY_VISITCOUNT_DESCENDING +
"&maxResults=10");
let title = "Most Visited";
let mostVisitedID = newSmartBookmark(parent, uri, -1, title, "MostVisited");
let mostVisitedGUID = store.GUIDForId(mostVisitedID);
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");
try {
engine._syncStartup();
_("Verify that mapDupe uses the anno, discovering a dupe regardless of URI.");
do_check_eq(mostVisitedGUID, engine._mapDupe(record));
--- a/services/sync/tests/unit/test_clients_engine.js
+++ b/services/sync/tests/unit/test_clients_engine.js
@@ -1,8 +1,11 @@
+/* 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");
Cu.import("resource://services-sync/util.js");
Cu.import("resource://services-sync/engines.js");
Cu.import("resource://services-sync/engines/clients.js");
Cu.import("resource://services-sync/service.js");
@@ -43,17 +46,17 @@ add_test(function test_bad_hmac() {
let coll = user.collection("clients");
let wbo = coll.wbo(id);
return !wbo || !wbo.payload;
}
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);
}
try {
let passphrase = "abcdeabcdeabcdeabcdeabcdea";
Service.serverURL = TEST_SERVER_URL;
Service.clusterURL = TEST_CLUSTER_URL;
Service.login("foo", "ilovejane", passphrase);
@@ -72,17 +75,17 @@ add_test(function test_bad_hmac() {
deletedItems = [];
_("Change our keys and our client ID, reupload keys.");
let oldLocalID = Clients.localID; // Preserve to test for deletion!
Clients.localID = Utils.makeGUID();
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.");
Clients._sync();
_("Old record " + oldLocalID + " was deleted, new one uploaded.");
check_clients_count(1);
check_client_deleted(oldLocalID);
@@ -159,20 +162,18 @@ add_test(function test_properties() {
} finally {
Svc.Prefs.resetBranch("");
run_next_test();
}
});
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 = {
meta: {global: {engines: {clients: {version: Clients.version,
syncID: Clients.syncID}}}},
clients: {},
crypto: {}
};
@@ -400,19 +401,17 @@ add_test(function test_process_incoming_
// logout command causes processIncomingCommands to return explicit false.
do_check_false(Clients.processIncomingCommands());
});
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();
let contents = {
meta: {global: {engines: {clients: {version: Clients.version,
syncID: Clients.syncID}}}},
clients: {},
--- a/services/sync/tests/unit/test_clients_escape.js
+++ b/services/sync/tests/unit/test_clients_escape.js
@@ -1,24 +1,28 @@
+/* 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");
Clients.localID = "ascii";
Clients.localName = "wéävê";
_("Make sure we have the expected record");
let record = Clients._createRecord("ascii");
--- a/services/sync/tests/unit/test_collections_recovery.js
+++ b/services/sync/tests/unit/test_collections_recovery.js
@@ -14,21 +14,19 @@ add_test(function test_missing_crypto_co
response.setStatusLine(request.httpVersion, 200, "OK");
response.bodyOutputStream.write(body, body.length);
} else {
handler(request, response);
}
};
}
+ 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),
"/1.1/johndoe/storage/crypto/keys": johnU("crypto", new ServerWBO("keys").handler()),
"/1.1/johndoe/storage/meta/global": johnU("meta", new ServerWBO("global").handler())
};
let collections = ["clients", "bookmarks", "forms", "history",
"passwords", "prefs", "tabs"];
--- a/services/sync/tests/unit/test_corrupt_keys.js
+++ b/services/sync/tests/unit/test_corrupt_keys.js
@@ -45,24 +45,21 @@ add_test(function test_locally_changed_k
},
extData: {
weaveLastUsed: 1
}}]}]};
delete Svc.Session;
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();
function corrupt_local_keys() {
CollectionKeys._default.keyPair = [Svc.Crypto.generateRandomKey(),
Svc.Crypto.generateRandomKey()];
}
@@ -74,17 +71,17 @@ add_test(function test_locally_changed_k
"storageVersion": STORAGE_VERSION};
m.upload(Weave.Service.metaURL);
_("New meta/global: " + JSON.stringify(johndoe.collection("meta").wbo("global")));
// 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.
do_check_true(Weave.Service.login("johndoe", "ilovejane", passphrase));
do_check_true(Weave.Service.isLoggedIn);
// Sync should upload records.
Weave.Service.sync();
--- a/services/sync/tests/unit/test_engine_abort.js
+++ b/services/sync/tests/unit/test_engine_abort.js
@@ -1,17 +1,14 @@
Cu.import("resource://services-sync/engines.js");
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();
_("Create some server data.");
let meta_global = Records.set(engine.metaURL, new WBORecord(engine.metaURL));
meta_global.payload.engines = {rotary: {version: engine.version,
syncID: engine.syncID}};
--- a/services/sync/tests/unit/test_errorhandler.js
+++ b/services/sync/tests/unit/test_errorhandler.js
@@ -1,13 +1,14 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
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");
Svc.DefaultPrefs.set("registerEngines", "");
Cu.import("resource://services-sync/service.js");
const TEST_MAINTENANCE_URL = "http://localhost:8080/maintenance/";
const logsdir = FileUtils.getDir("ProfD", ["weave", "logs"], true);
@@ -47,18 +48,17 @@ function run_test() {
Log4Moz.repository.getLogger("Sync.ErrorHandler").level = Log4Moz.Level.Trace;
run_next_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);
}
function service_unavailable(request, response) {
let body = "Service Unavailable";
response.setStatusLine(request.httpVersion, 503, "Service Unavailable");
@@ -113,29 +113,27 @@ function sync_httpd_setup() {
upd("crypto", (new ServerWBO("keys")).handler()),
"/maintenance/1.1/broken.wipe/storage": service_unavailable,
"/maintenance/1.1/broken.wipe/storage/clients": upd("clients", clientsColl.handler()),
"/maintenance/1.1/broken.wipe/storage/catapult": service_unavailable
});
}
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;
return generateAndUploadKeys();
}
function generateAndUploadKeys() {
generateNewKeys();
let serverKeys = CollectionKeys.asWBO("crypto", "keys");
- serverKeys.encrypt(Service.syncKeyBundle);
+ serverKeys.encrypt(Identity.syncKeyBundle);
return serverKeys.upload(Service.cryptoKeysURL).success;
}
function clean() {
Service.startOver();
Status.resetSync();
Status.resetBackoff();
}
@@ -167,17 +165,18 @@ add_test(function test_401_logout() {
Service.startOver();
server.stop(run_next_test);
});
}
Svc.Obs.add("weave:service:login:error", onLoginError);
}
// Make sync fail due to login rejected.
- Service.username = "janedoe";
+ setBasicCredentials("janedoe", "irrelevant", "irrelevant");
+ Service._updateCachedURLs();
_("Starting first sync.");
Service.sync();
_("First sync done.");
});
add_test(function test_credentials_changed_logout() {
let server = sync_httpd_setup();
@@ -420,17 +419,17 @@ add_test(function test_shouldReportError
server.stop(run_next_test);
});
add_test(function test_login_syncAndReportErrors_non_network_error() {
// Test non-network errors are 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);
do_check_eq(Status.login, LOGIN_FAILED_NO_PASSWORD);
clean();
server.stop(run_next_test);
});
@@ -464,17 +463,17 @@ add_test(function test_sync_syncAndRepor
ErrorHandler.syncAndReportErrors();
});
add_test(function test_login_syncAndReportErrors_prolonged_non_network_error() {
// Test prolonged, non-network errors are
// 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);
do_check_eq(Status.login, LOGIN_FAILED_NO_PASSWORD);
clean();
server.stop(run_next_test);
});
@@ -505,19 +504,17 @@ add_test(function test_sync_syncAndRepor
});
setLastSync(PROLONGED_ERROR_DURATION);
ErrorHandler.syncAndReportErrors();
});
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;
Svc.Obs.add("weave:ui:login:error", function onSyncError() {
Svc.Obs.remove("weave:ui:login:error", onSyncError);
do_check_eq(Status.login, LOGIN_FAILED_NETWORK_ERROR);
clean();
@@ -544,19 +541,17 @@ add_test(function test_sync_syncAndRepor
setLastSync(NON_PROLONGED_ERROR_DURATION);
ErrorHandler.syncAndReportErrors();
});
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;
Svc.Obs.add("weave:ui:login:error", function onSyncError() {
Svc.Obs.remove("weave:ui:login:error", onSyncError);
do_check_eq(Status.login, LOGIN_FAILED_NETWORK_ERROR);
clean();
@@ -584,17 +579,17 @@ add_test(function test_sync_syncAndRepor
setLastSync(PROLONGED_ERROR_DURATION);
ErrorHandler.syncAndReportErrors();
});
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);
do_check_eq(Status.sync, PROLONGED_SYNC_FAILURE);
clean();
server.stop(run_next_test);
});
@@ -624,19 +619,17 @@ add_test(function test_sync_prolonged_no
});
setLastSync(PROLONGED_ERROR_DURATION);
Service.sync();
});
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;
Svc.Obs.add("weave:ui:login:error", function onSyncError() {
Svc.Obs.remove("weave:ui:login:error", onSyncError);
do_check_eq(Status.sync, PROLONGED_SYNC_FAILURE);
clean();
@@ -663,17 +656,17 @@ add_test(function test_sync_prolonged_ne
setLastSync(PROLONGED_ERROR_DURATION);
Service.sync();
});
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);
do_check_eq(Status.login, LOGIN_FAILED_NO_PASSWORD);
clean();
server.stop(run_next_test);
});
@@ -702,19 +695,17 @@ add_test(function test_sync_non_network_
server.stop(run_next_test);
});
setLastSync(NON_PROLONGED_ERROR_DURATION);
Service.sync();
});
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;
// Test network errors are not reported.
Svc.Obs.add("weave:ui:clear-error", function onClearError() {
Svc.Obs.remove("weave:ui:clear-error", onClearError);
do_check_eq(Status.login, LOGIN_FAILED_NETWORK_ERROR);
@@ -779,27 +770,28 @@ add_test(function test_sync_server_maint
});
add_test(function test_info_collections_login_server_maintenance_error() {
// Test info/collections server maintenance errors are not reported.
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;
let backoffInterval;
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
Svc.Obs.remove("weave:service:backoff:interval", observe);
backoffInterval = subject;
});
function onUIUpdate() {
- do_throw("Shouldn't get here!");
+ do_throw("Shouldn't experience UI update!");
}
Svc.Obs.add("weave:ui:login:error", onUIUpdate);
do_check_false(Status.enforceBackoff);
do_check_eq(Status.service, STATUS_OK);
Svc.Obs.add("weave:ui:clear-error", function onLoginFinish() {
Svc.Obs.remove("weave:ui:clear-error", onLoginFinish);
@@ -818,17 +810,17 @@ add_test(function test_info_collections_
Service.sync();
});
add_test(function test_meta_global_login_server_maintenance_error() {
// Test meta/global server maintenance errors are not reported.
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;
let backoffInterval;
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
Svc.Obs.remove("weave:service:backoff:interval", observe);
backoffInterval = subject;
});
@@ -858,19 +850,20 @@ add_test(function test_meta_global_login
Service.sync();
});
add_test(function test_crypto_keys_login_server_maintenance_error() {
// Test crypto/keys server maintenance errors are not reported.
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();
let backoffInterval;
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
Svc.Obs.remove("weave:service:backoff:interval", observe);
backoffInterval = subject;
});
@@ -926,17 +919,17 @@ add_test(function test_sync_prolonged_se
Service.sync();
});
add_test(function test_info_collections_login_prolonged_server_maintenance_error(){
// Test info/collections prolonged server maintenance errors are reported.
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;
let backoffInterval;
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
Svc.Obs.remove("weave:service:backoff:interval", observe);
backoffInterval = subject;
});
@@ -959,17 +952,17 @@ add_test(function test_info_collections_
Service.sync();
});
add_test(function test_meta_global_login_prolonged_server_maintenance_error(){
// Test meta/global prolonged server maintenance errors are reported.
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;
let backoffInterval;
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
Svc.Obs.remove("weave:service:backoff:interval", observe);
backoffInterval = subject;
});
@@ -992,17 +985,17 @@ add_test(function test_meta_global_login
Service.sync();
});
add_test(function test_download_crypto_keys_login_prolonged_server_maintenance_error(){
// Test crypto/keys prolonged server maintenance errors are reported.
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();
let backoffInterval;
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
Svc.Obs.remove("weave:service:backoff:interval", observe);
@@ -1027,19 +1020,17 @@ add_test(function test_download_crypto_k
Service.sync();
});
add_test(function test_upload_crypto_keys_login_prolonged_server_maintenance_error(){
// Test crypto/keys prolonged server maintenance errors are reported.
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;
let backoffInterval;
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
Svc.Obs.remove("weave:service:backoff:interval", observe);
backoffInterval = subject;
});
@@ -1063,19 +1054,17 @@ add_test(function test_upload_crypto_key
});
add_test(function test_wipeServer_login_prolonged_server_maintenance_error(){
// Test that we report prolonged server maintenance errors that occur whilst
// wiping the server.
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;
let backoffInterval;
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
Svc.Obs.remove("weave:service:backoff:interval", observe);
backoffInterval = subject;
});
@@ -1098,19 +1087,18 @@ add_test(function test_wipeServer_login_
Service.sync();
});
add_test(function test_wipeRemote_prolonged_server_maintenance_error(){
// Test that we report prolonged server maintenance errors that occur whilst
// 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();
let engine = Engines.get("catapult");
engine.exception = null;
engine.enabled = true;
@@ -1168,17 +1156,17 @@ add_test(function test_sync_syncAndRepor
});
add_test(function test_info_collections_login_syncAndReportErrors_server_maintenance_error() {
// Test info/collections server maintenance errors are reported
// when calling syncAndReportErrors.
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;
let backoffInterval;
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
Svc.Obs.remove("weave:service:backoff:interval", observe);
backoffInterval = subject;
});
@@ -1202,17 +1190,17 @@ add_test(function test_info_collections_
});
add_test(function test_meta_global_login_syncAndReportErrors_server_maintenance_error() {
// Test meta/global server maintenance errors are reported
// when calling syncAndReportErrors.
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;
let backoffInterval;
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
Svc.Obs.remove("weave:service:backoff:interval", observe);
backoffInterval = subject;
});
@@ -1236,17 +1224,17 @@ add_test(function test_meta_global_login
});
add_test(function test_download_crypto_keys_login_syncAndReportErrors_server_maintenance_error() {
// Test crypto/keys server maintenance errors are reported
// when calling syncAndReportErrors.
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();
let backoffInterval;
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
Svc.Obs.remove("weave:service:backoff:interval", observe);
@@ -1272,19 +1260,17 @@ add_test(function test_download_crypto_k
});
add_test(function test_upload_crypto_keys_login_syncAndReportErrors_server_maintenance_error() {
// Test crypto/keys server maintenance errors are reported
// when calling syncAndReportErrors.
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;
let backoffInterval;
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
Svc.Obs.remove("weave:service:backoff:interval", observe);
backoffInterval = subject;
});
@@ -1308,19 +1294,17 @@ add_test(function test_upload_crypto_key
});
add_test(function test_wipeServer_login_syncAndReportErrors_server_maintenance_error() {
// Test crypto/keys server maintenance errors are reported
// when calling syncAndReportErrors.
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;
let backoffInterval;
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
Svc.Obs.remove("weave:service:backoff:interval", observe);
backoffInterval = subject;
});
@@ -1343,19 +1327,17 @@ add_test(function test_wipeServer_login_
ErrorHandler.syncAndReportErrors();
});
add_test(function test_wipeRemote_syncAndReportErrors_server_maintenance_error(){
// Test that we report prolonged server maintenance errors that occur whilst
// 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();
let engine = Engines.get("catapult");
engine.exception = null;
engine.enabled = true;
@@ -1413,17 +1395,17 @@ add_test(function test_sync_syncAndRepor
});
add_test(function test_info_collections_login_syncAndReportErrors_prolonged_server_maintenance_error() {
// Test info/collections server maintenance errors are reported
// when calling syncAndReportErrors.
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;
let backoffInterval;
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
Svc.Obs.remove("weave:service:backoff:interval", observe);
backoffInterval = subject;
});
@@ -1447,17 +1429,17 @@ add_test(function test_info_collections_
});
add_test(function test_meta_global_login_syncAndReportErrors_prolonged_server_maintenance_error() {
// Test meta/global server maintenance errors are reported
// when calling syncAndReportErrors.
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;
let backoffInterval;
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
Svc.Obs.remove("weave:service:backoff:interval", observe);
backoffInterval = subject;
});
@@ -1481,17 +1463,17 @@ add_test(function test_meta_global_login
});
add_test(function test_download_crypto_keys_login_syncAndReportErrors_prolonged_server_maintenance_error() {
// Test crypto/keys server maintenance errors are reported
// when calling syncAndReportErrors.
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();
let backoffInterval;
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
Svc.Obs.remove("weave:service:backoff:interval", observe);
@@ -1517,19 +1499,17 @@ add_test(function test_download_crypto_k
});
add_test(function test_upload_crypto_keys_login_syncAndReportErrors_prolonged_server_maintenance_error() {
// Test crypto/keys server maintenance errors are reported
// when calling syncAndReportErrors.
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;
let backoffInterval;
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
Svc.Obs.remove("weave:service:backoff:interval", observe);
backoffInterval = subject;
});
@@ -1553,19 +1533,17 @@ add_test(function test_upload_crypto_key
});
add_test(function test_wipeServer_login_syncAndReportErrors_prolonged_server_maintenance_error() {
// Test crypto/keys server maintenance errors are reported
// when calling syncAndReportErrors.
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;
let backoffInterval;
Svc.Obs.add("weave:service:backoff:interval", function observe(subject, data) {
Svc.Obs.remove("weave:service:backoff:interval", observe);
backoffInterval = subject;
});
--- a/services/sync/tests/unit/test_errorhandler_sync_checkServerError.js
+++ b/services/sync/tests/unit/test_errorhandler_sync_checkServerError.js
@@ -43,28 +43,26 @@ function sync_httpd_setup() {
"/1.1/johndoe/storage/meta/global": upd("meta", globalWBO.handler()),
"/1.1/johndoe/storage/clients": upd("clients", clientsColl.handler()),
"/1.1/johndoe/storage/crypto/keys": upd("crypto", keysWBO.handler())
};
return httpd_setup(handlers);
}
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();
}
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;
}
add_test(function test_backoff500() {
_("Test: HTTP 500 sets backoff status.");
setUp();
let server = sync_httpd_setup();
--- a/services/sync/tests/unit/test_history_engine.js
+++ b/services/sync/tests/unit/test_history_engine.js
@@ -1,25 +1,25 @@
+/* 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");
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);
// A collection that logs each GET
let collection = new ServerCollection();
collection.get_log = [];
collection._get = collection.get;
--- a/services/sync/tests/unit/test_hmac_error.js
+++ b/services/sync/tests/unit/test_hmac_error.js
@@ -13,21 +13,19 @@ let hmacErrorCount = 0;
return hHE.call(Service);
};
})();
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 = {};
Engines.register(RotaryEngine);
let engine = Engines.get("rotary");
engine.enabled = true;
engine.lastSync = 123; // Needs to be non-zero so that tracker is queried.
engine._store.items = {flying: "LNER Class A3 4472",
new file mode 100644
--- /dev/null
+++ b/services/sync/tests/unit/test_identity_manager.js
@@ -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();
+});
--- a/services/sync/tests/unit/test_interval_triggers.js
+++ b/services/sync/tests/unit/test_interval_triggers.js
@@ -27,25 +27,23 @@ function sync_httpd_setup() {
"/1.1/johndoe/info/collections": collectionsHelper.handler,
"/1.1/johndoe/storage/crypto/keys":
upd("crypto", (new ServerWBO("keys")).handler()),
"/1.1/johndoe/storage/clients": upd("clients", clientsColl.handler())
});
}
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);
}
function run_test() {
initTestLogging("Trace");
Log4Moz.repository.getLogger("Sync.Service").level = Log4Moz.Level.Trace;
Log4Moz.repository.getLogger("Sync.SyncScheduler").level = Log4Moz.Level.Trace;
--- a/services/sync/tests/unit/test_jpakeclient.js
+++ b/services/sync/tests/unit/test_jpakeclient.js
@@ -178,19 +178,17 @@ function run_test() {
Svc.Prefs.resetBranch("");
});
// Ensure PSM is initialized.
Cc["@mozilla.org/psm;1"].getService(Ci.nsISupports);
// 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});
initTestLogging("Trace");
Log4Moz.repository.getLogger("Sync.JPAKEClient").level = Log4Moz.Level.Trace;
Log4Moz.repository.getLogger("Sync.RESTRequest").level = Log4Moz.Level.Trace;
run_next_test();
--- a/services/sync/tests/unit/test_keys.js
+++ b/services/sync/tests/unit/test_keys.js
@@ -1,260 +1,327 @@
-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) {
do_check_eq(a1[i], a2[i]);
}
}
-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);
do_check_eq(a[0], b[0]);
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");
do_check_eq(undefined, wbo.modified);
do_check_eq(CollectionKeys.lastModified, storage_keys.modified);
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.
*/
let info_collections = {};
do_check_true(CollectionKeys.updateNeeded(info_collections));
info_collections["crypto"] = 5000;
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;
}
let k1 = newBundle("k1");
let k2 = newBundle("k2");
let k3 = newBundle("k3");
let k4 = newBundle("k4");
let k5 = newBundle("k5");
let coll1 = {"foo": k1, "bar": k2};
let coll2 = {"foo": k1, "bar": k2};
let coll3 = {"foo": k1, "bar": k3};
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"]
let d4 = CollectionKeys._compareKeyBundleCollections(coll1, coll4); // ["bar", "foo"]
let d5 = CollectionKeys._compareKeyBundleCollections(coll5, coll2); // ["baz", "foo"]
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);
do_check_false(d4.same);
do_check_false(d5.same);
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();
}
--- a/services/sync/tests/unit/test_load_modules.js
+++ b/services/sync/tests/unit/test_load_modules.js
@@ -10,16 +10,17 @@ const modules = [
"engines/passwords.js",
"engines/prefs.js",
"engines/tabs.js",
"engines.js",
"ext/Observers.js",
"ext/Preferences.js",
"identity.js",
"jpakeclient.js",
+ "keys.js",
"log4moz.js",
"main.js",
"notifications.js",
"policies.js",
"record.js",
"resource.js",
"rest.js",
"service.js",
--- a/services/sync/tests/unit/test_node_reassignment.js
+++ b/services/sync/tests/unit/test_node_reassignment.js
@@ -68,19 +68,17 @@ function installNodeHandler(server, next
Utils.nextTick(next);
}
let nodePath = "/user/1.0/johndoe/node/weave";
server.server.registerPathHandler(nodePath, handleNodeRequest);
_("Registered node handler at " + nodePath);
}
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;
do_check_eq(Service.userAPI, "http://localhost:8080/user/1.0/");
let server = new SyncServer();
server.registerUser("johndoe");
server.start();
return server;
--- a/services/sync/tests/unit/test_records_crypto.js
+++ b/services/sync/tests/unit/test_records_crypto.js
@@ -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/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;
function crypted_resource_handler(metadata, response) {
let obj = {id: "resource",
modified: cryptoWrap.modified,
payload: JSON.stringify(cryptoWrap.payload)};
@@ -21,35 +25,33 @@ function prepareCryptoWrap(collection, i
w.id = id;
return w;
}
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");
Log4Moz.repository.rootLogger.addAppender(new Log4Moz.DumpAppender());
log.info("Setting up server and authenticator");
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");
cryptoWrap.encrypt(keyBundle);
log.info("Ciphertext is " + cryptoWrap.ciphertext);
do_check_true(cryptoWrap.ciphertext != null);
@@ -102,52 +104,48 @@ function run_test() {
cryptoWrap.decrypt(keyBundle);
}
catch(ex) {
error = ex;
}
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");
bookmarkItem.encrypt();
log.info("Ciphertext is " + bookmarkItem.ciphertext);
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.
let err;
try {
bookmarkItem.decrypt(CollectionKeys._default);
} catch (ex) {
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,
"my payload here");
log.info("Done!");
}
finally {
deleted file mode 100644
--- a/services/sync/tests/unit/test_records_crypto_generateEntry.js
+++ /dev/null
@@ -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);
-}
--- a/services/sync/tests/unit/test_resource.js
+++ b/services/sync/tests/unit/test_resource.js
@@ -1,8 +1,11 @@
+/* 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");
Cu.import("resource://services-sync/resource.js");
Cu.import("resource://services-sync/util.js");
let logger;
@@ -141,16 +144,18 @@ function server_headers(metadata, respon
headers[header] = metadata.getHeader(header);
}
let body = JSON.stringify(headers);
response.setStatusLine(metadata.httpVersion, 200, "OK");
response.bodyOutputStream.write(body, body.length);
}
function run_test() {
+ initTestLogging("Trace");
+
do_test_pending();
logger = Log4Moz.repository.getLogger('Test');
Log4Moz.repository.rootLogger.addAppender(new Log4Moz.DumpAppender());
let server = httpd_setup({
"/open": server_open,
"/protected": server_protected,
@@ -222,40 +227,28 @@ function run_test() {
do_check_eq(debugMessages.length, 1);
do_check_eq(debugMessages[0],
"Parse fail: Response body starts: \"\"This path exists\"\".");
logger.debug = dbg;
_("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");
content = res2.get();
do_check_eq(content, "This path exists and is protected - failed");
do_check_eq(content.status, 401);
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();
do_check_eq(content, "This path exists and is protected");
do_check_eq(content.status, 200);
do_check_true(content.success);
_("GET a non-existent resource (test that it'll fail, but not throw)");
--- a/services/sync/tests/unit/test_resource_async.js
+++ b/services/sync/tests/unit/test_resource_async.js
@@ -1,8 +1,11 @@
+/* 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");
Cu.import("resource://services-sync/resource.js");
Cu.import("resource://services-sync/util.js");
const RES_UPLOAD_URL = "http://localhost:8080/upload";
const RES_HEADERS_URL = "http://localhost:8080/headers";
@@ -256,29 +259,18 @@ add_test(function test_get() {
run_next_test();
});
});
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();
});
add_test(function test_get_protected_fail() {
_("GET a password protected resource (test that it'll fail w/o pass, no throw)");
let res2 = new AsyncResource("http://localhost:8080/protected");
res2.get(function (error, content) {
@@ -287,17 +279,17 @@ add_test(function test_get_protected_fai
do_check_eq(content.status, 401);
do_check_false(content.success);
run_next_test();
});
});
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);
res3.get(function (error, content) {
do_check_eq(error, null);
do_check_eq(content, "This path exists and is protected");
do_check_eq(content.status, 200);
do_check_true(content.success);
--- a/services/sync/tests/unit/test_resource_ua.js
+++ b/services/sync/tests/unit/test_resource_ua.js
@@ -20,20 +20,19 @@ function test_resource_user_agent() {
}
do_test_pending();
let server = httpd_setup({
"/1.1/johndoe/info/collections": uaHandler(collectionsHelper.handler),
"/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 + "." +
Services.appinfo.appBuildID;
function test_fetchInfo(next) {
_("Testing _fetchInfo.");
Weave.Service._fetchInfo();
--- a/services/sync/tests/unit/test_score_triggers.js
+++ b/services/sync/tests/unit/test_score_triggers.js
@@ -38,22 +38,17 @@ function sync_httpd_setup() {
let cl = new ServerCollection();
handlers["/1.1/johndoe/storage/clients"] =
upd("clients", cl.handler());
return httpd_setup(handlers);
}
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() {
initTestLogging("Trace");
Log4Moz.repository.getLogger("Sync.Service").level = Log4Moz.Level.Trace;
run_next_test();
--- a/services/sync/tests/unit/test_sendcredentials_controller.js
+++ b/services/sync/tests/unit/test_sendcredentials_controller.js
@@ -2,19 +2,17 @@
http://creativecommons.org/publicdomain/zero/1.0/ */
Cu.import("resource://services-sync/policies.js");
Cu.import("resource://services-sync/constants.js");
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");
Log4Moz.repository.getLogger("Sync.SendCredentialsController").level = Log4Moz.Level.Trace;
Log4Moz.repository.getLogger("Sync.SyncScheduler").level = Log4Moz.Level.Trace;
run_next_test();
}
@@ -26,19 +24,19 @@ function make_sendCredentials_test(topic
let jpakeclient = {
sendAndComplete: function sendAndComplete(data) {
// Verify that the controller unregisters itself as an observer
// when the exchange is complete by faking another notification.
do_check_false(sendAndCompleteCalled);
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();
// Verify it schedules a sync for the expected interval.
let expectedInterval = SyncScheduler.activeInterval;
do_check_true(SyncScheduler.nextSync - Date.now() <= expectedInterval);
// Signal the end of another sync. We shouldn't be registered anymore,
--- a/services/sync/tests/unit/test_service_attributes.js
+++ b/services/sync/tests/unit/test_service_attributes.js
@@ -1,80 +1,29 @@
+/* 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 {
do_check_true(!!Service.serverURL); // actual value may change
do_check_eq(Service.clusterURL, "");
do_check_eq(Service.userBaseURL, undefined);
do_check_eq(Service.infoURL, undefined);
do_check_eq(Service.storageURL, undefined);
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);
do_check_eq(Service.userBaseURL, undefined);
do_check_eq(Service.storageURL, undefined);
do_check_eq(Service.metaURL, undefined);
Service.serverURL = "http://weave.server/";
@@ -101,20 +50,19 @@ function test_urls() {
Svc.Prefs.set("userURL", "http://weave.user.services/");
do_check_eq(Service.miscAPI, "http://weave.misc.services/1.0/");
do_check_eq(Service.userAPI, "http://weave.user.services/1.0/");
do_check_eq(Service.pwResetURL,
"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
Service.serverURL = Service.serverURL;
do_check_eq(Svc.Prefs.get("clusterURL"), "http://weave.cluster/");
Service.serverURL = "http://different.auth.node/";
do_check_eq(Svc.Prefs.get("serverURL"), "http://different.auth.node/");
@@ -159,13 +107,12 @@ function test_locked() {
// Locking again will return false
do_check_eq(Service.lock(), false);
Service.unlock();
do_check_eq(Service.locked, false);
}
function run_test() {
- test_identities();
test_urls();
test_syncID();
test_locked();
}
--- a/services/sync/tests/unit/test_service_changePassword.js
+++ b/services/sync/tests/unit/test_service_changePassword.js
@@ -1,8 +1,12 @@
+/* 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");
Cu.import("resource://services-sync/log4moz.js");
function run_test() {
initTestLogging("Trace");
@@ -23,53 +27,52 @@ add_test(function test_change_password()
response.setStatusLine(request.httpVersion, statusCode, status);
response.bodyOutputStream.write(body, body.length);
};
}
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({
"/user/1.0/johndoe/password": send(200, "OK", ""),
"/user/1.0/janedoe/password": send(401, "Unauthorized", "Forbidden!")
});
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("");
Services.logins.removeAllLogins();
server.stop(run_next_test);
}
});
--- a/services/sync/tests/unit/test_service_cluster.js
+++ b/services/sync/tests/unit/test_service_cluster.js
@@ -11,17 +11,17 @@ function do_check_throws(func) {
do_check_true(raised);
}
function test_findCluster() {
_("Test Service._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() {
Service._findCluster();
});
server = httpd_setup({
"/user/1.0/johndoe/node/weave": httpd_handler(200, "OK", "http://weave.user.node/"),
@@ -31,33 +31,33 @@ function test_findCluster() {
"/user/1.0/joedoe/node/weave": httpd_handler(500, "Server Error", "Server Error")
});
_("_findCluster() returns the user's cluster node");
let cluster = Service._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();
});
} finally {
Svc.Prefs.resetBranch("");
if (server) {
server.stop(runNextTest);
@@ -69,49 +69,49 @@ function test_findCluster() {
function test_setCluster() {
_("Test Service._setCluster()");
let server = httpd_setup({
"/user/1.0/johndoe/node/weave": httpd_handler(200, "OK", "http://weave.user.node/"),
"/user/1.0/jimdoe/node/weave": httpd_handler(200, "OK", "null")
});
try {
Service.serverURL = TEST_SERVER_URL;
- Service.username = "johndoe";
+ Identity.account = "johndoe";
_("Check initial state.");
do_check_eq(Service.clusterURL, "");
_("Set the cluster URL.");
do_check_true(Service._setCluster());
do_check_eq(Service.clusterURL, "http://weave.user.node/");
_("Setting it again won't make a difference if it's the same one.");
do_check_false(Service._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("");
server.stop(runNextTest);
}
}
function test_updateCluster() {
_("Test Service._updateCluster()");
let server = httpd_setup({
"/user/1.0/johndoe/node/weave": httpd_handler(200, "OK", "http://weave.user.node/"),
"/user/1.0/janedoe/node/weave": httpd_handler(200, "OK", "http://weave.cluster.url/")
});
try {
Service.serverURL = TEST_SERVER_URL;
- Service.username = "johndoe";
+ Identity.account = "johndoe";
_("Check initial state.");
do_check_eq(Service.clusterURL, "");
do_check_eq(Svc.Prefs.get("lastClusterUpdate"), null);
_("Set the cluster URL.");
let before = Date.now();
do_check_true(Service._updateCluster());
@@ -120,17 +120,17 @@ function test_updateCluster() {
do_check_true(lastUpdate >= before);
_("Trying to update the cluster URL within the backoff timeout won't do anything.");
do_check_false(Service._updateCluster());
do_check_eq(Service.clusterURL, "http://weave.user.node/");
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();
do_check_true(Service._updateCluster());
do_check_eq(Service.clusterURL, "http://weave.cluster.url/");
lastUpdate = parseFloat(Svc.Prefs.get("lastClusterUpdate"));
do_check_true(lastUpdate >= before);
--- a/services/sync/tests/unit/test_service_detect_upgrade.js
+++ b/services/sync/tests/unit/test_service_detect_upgrade.js
@@ -1,15 +1,19 @@
+/* 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");
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");
Engines.register(TabEngine);
add_test(function v4_upgrade() {
let passphrase = "abcdeabcdeabcdeabcdeabcdea";
@@ -109,52 +113,52 @@ add_test(function v4_upgrade() {
do_check_true(Weave.Service.isLoggedIn);
Weave.Service.sync();
do_check_true(Weave.Service.isLoggedIn);
let serverDecrypted;
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));
if (should_succeed)
do_check_eq(JSON.stringify(serverDefault), JSON.stringify(localDefault));
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);
_("Update keys on server.");
set_server_keys(["KaaaaaaaaaaaHAtfmuRY0XEJ7LXfFuqvF7opFdBD/MY=",
"aaaaaaaaaaaapxMO6TEWtLIOv9dj6kBAJdzhWDkkkis="]);
_("Checking that we no longer have the latest keys.");
@@ -227,74 +231,67 @@ add_test(function v5_upgrade() {
},
extData: {
weaveLastUsed: 1
}}]}]};
delete Svc.Session;
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();
serverKeys = CollectionKeys.asWBO("crypto", wboName);
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);
}
catch (e) {
_("Exception: " + e);
}
_("Status: " + Status);
do_check_false(Weave.Service.isLoggedIn);
do_check_eq(VERSION_OUT_OF_DATE, Status.sync);
// Clean up.
Weave.Service.startOver();
-
+
} finally {
Weave.Svc.Prefs.resetBranch("");
server.stop(run_next_test);
}
});
function run_test() {
let logger = Log4Moz.repository.rootLogger;
--- a/services/sync/tests/unit/test_service_getStorageInfo.js
+++ b/services/sync/tests/unit/test_service_getStorageInfo.js
@@ -6,18 +6,17 @@ Cu.import("resource://services-sync/rest
Cu.import("resource://services-sync/constants.js");
Cu.import("resource://services-sync/util.js");
let collections = {steam: 65.11328,
petrol: 82.488281,
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;
Log4Moz.repository.getLogger("Sync.Service").level = Log4Moz.Level.Trace;
Log4Moz.repository.getLogger("Sync.StorageRequest").level = Log4Moz.Level.Trace;
initTestLogging();
run_next_test();
@@ -27,18 +26,19 @@ add_test(function test_success() {
let handler = httpd_handler(200, "OK", JSON.stringify(collections));
let server = httpd_setup({"/1.1/johndoe/info/collections": handler});
let request = Service.getStorageInfo("collections", function (error, info) {
do_check_eq(error, null);
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";
do_check_eq(handler.request.getHeader("User-Agent"), expectedUA);
server.stop(run_next_test);
});
do_check_true(request instanceof RESTRequest);
--- a/services/sync/tests/unit/test_service_login.js
+++ b/services/sync/tests/unit/test_service_login.js
@@ -74,53 +74,49 @@ add_test(function test_login_logout() {
_("Try logging in. It won't work because we're not configured yet.");
Service.login();
do_check_eq(Status.service, CLIENT_NOT_CONFIGURED);
do_check_eq(Status.login, LOGIN_FAILED_NO_USERNAME);
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);
do_check_true(Service.isLoggedIn);
_("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);
_("Try again with correct password.");
Service.login("janedoe", "ilovejohn");
do_check_eq(Status.service, STATUS_OK);
do_check_eq(Status.login, LOGIN_SUCCEEDED);
do_check_true(Service.isLoggedIn);
_("Calling login() with parameters when the client is unconfigured sends notification.");
let notified = false;
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);
do_check_eq(Status.login, LOGIN_SUCCEEDED);
do_check_true(Service.isLoggedIn);
_("Logout.");
Service.logout();
@@ -133,19 +129,17 @@ add_test(function test_login_logout() {
} finally {
Svc.Prefs.resetBranch("");
server.stop(run_next_test);
}
});
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.");
let oldLogin = Service.login;
let loginCalled = false;
Service.login = function() {
loginCalled = true;
Status.login = LOGIN_SUCCEEDED;
@@ -197,20 +191,21 @@ add_test(function test_login_on_sync() {
do_check_true(scheduleCalled);
SyncScheduler.scheduleNextSync = scheduleNextSyncF;
// TODO: need better tests around master password prompting. See Bug 620583.
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";
});
let oldClearSyncTriggers = SyncScheduler.clearSyncTriggers;
let oldLockedSync = Service._lockedSync;
let cSTCalled = false;
@@ -228,16 +223,19 @@ add_test(function test_login_on_sync() {
do_check_eq(Service._checkSync(), kSyncMasterPasswordLocked);
_("Sync doesn't proceed and clears triggers if MP is still locked.");
Service.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!
} finally {
Svc.Prefs.resetBranch("");
server.stop(run_next_test);
}
});
--- a/services/sync/tests/unit/test_service_passwordUTF8.js
+++ b/services/sync/tests/unit/test_service_passwordUTF8.js
@@ -57,34 +57,33 @@ function run_test() {
do_test_pending();
let server = httpd_setup({
"/1.1/johndoe/info/collections": login_handling(collectionsHelper.handler),
"/1.1/johndoe/storage/meta/global": upd("meta", new ServerWBO("global").handler()),
"/1.1/johndoe/storage/crypto/keys": upd("crypto", new ServerWBO("keys").handler()),
"/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 {
_("Try to log in with the password.");
server_password = "foobar";
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));
} finally {
server.stop(do_test_finished);
Svc.Prefs.resetBranch("");
}
}
--- a/services/sync/tests/unit/test_service_persistLogin.js
+++ b/services/sync/tests/unit/test_service_persistLogin.js
@@ -1,20 +1,18 @@
Cu.import("resource://services-sync/main.js");
Cu.import("resource://services-sync/util.js");
Cu.import("resource://services-sync/constants.js");
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,
PWDMGR_PASSWORD_REALM);
do_check_eq(logins.length, 0);
logins = Services.logins.findLogins({}, PWDMGR_HOST, null,
PWDMGR_PASSPHRASE_REALM);
do_check_eq(logins.length, 0);
--- a/services/sync/tests/unit/test_service_startOver.js
+++ b/services/sync/tests/unit/test_service_startOver.js
@@ -1,8 +1,11 @@
+/* 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");
Cu.import("resource://services-sync/policies.js");
Cu.import("resource://services-sync/constants.js");
Cu.import("resource://services-sync/util.js");
function BlaEngine() {
@@ -22,19 +25,18 @@ Engines.register(BlaEngine);
function run_test() {
initTestLogging("Trace");
run_next_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;
Service.persistLogin();
// Verify set up.
do_check_eq(Status.checkSetup(), STATUS_OK);
@@ -47,18 +49,18 @@ add_test(function test_resetLocalData()
do_check_eq(Status.service, CLIENT_NOT_CONFIGURED);
});
Service.startOver();
do_check_true(observerCalled);
// 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);
do_check_eq(Status.backoffInterval, 0);
do_check_eq(Status.minimumNextSync, 0);
run_next_test();
});
--- a/services/sync/tests/unit/test_service_startup.js
+++ b/services/sync/tests/unit/test_service_startup.js
@@ -1,35 +1,36 @@
+/* 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");
Cu.import("resource://services-sync/util.js");
Cu.import("resource://services-sync/engines.js");
function run_test() {
_("When imported, Service.onStartup is called");
+ Svc.Prefs.set("registerEngines", "Tab,Bookmarks,Form,History");
+ new SyncTestingInfrastructure();
+
// Test fixtures
- Svc.Prefs.set("registerEngines", "Tab,Bookmarks,Form,History");
- Svc.Prefs.set("username", "johndoe");
+ Identity.username = "johndoe";
Cu.import("resource://services-sync/service.js");
_("Service is enabled.");
do_check_eq(Service.enabled, true);
_("Engines are registered.");
let engines = Engines.getAll();
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);
Observers.add("weave:service:ready", function (subject, data) {
do_check_true(Status.ready);
// Clean up.
Svc.Prefs.resetBranch("");
--- a/services/sync/tests/unit/test_service_sync_401.js
+++ b/services/sync/tests/unit/test_service_sync_401.js
@@ -27,37 +27,33 @@ function run_test() {
"/1.1/johndoe/storage/meta/global": upd("meta", new ServerWBO("global").handler()),
"/1.1/johndoe/info/collections": login_handling(collectionsHelper.handler)
});
const GLOBAL_SCORE = 42;
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));
let threw = false;
Weave.Svc.Obs.add("weave:service:sync:error", function (subject, data) {
threw = true;
});
_("Initial state: We're successfully logged in.");
Weave.Service.login();
do_check_true(Weave.Service.isLoggedIn);
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();
_("Verify that sync() threw an exception.");
do_check_true(threw);
_("We're no longer logged in.");
--- a/services/sync/tests/unit/test_service_sync_remoteSetup.js
+++ b/services/sync/tests/unit/test_service_sync_remoteSetup.js
@@ -1,27 +1,30 @@
+/* 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() {
let logger = Log4Moz.repository.rootLogger;
Log4Moz.repository.rootLogger.addAppender(new Log4Moz.DumpAppender());
let guidSvc = new FakeGUIDService();
let clients = new ServerCollection();
let meta_global = new ServerWBO('global');
let collectionsHelper = track_collections_helper();
let upd = collectionsHelper.with_updated_collection;
let collections = collectionsHelper.collections;
-
+
function wasCalledHandler(wbo) {
let handler = wbo.handler();
return function() {
wbo.wasCalled = true;
handler.apply(this, arguments);
};
}
@@ -58,39 +61,39 @@ function run_test() {
"/1.1/johndoe/storage/meta": upd("meta", wasCalledHandler(metaColl)),
"/1.1/johndoe/info/collections": collectionsHelper.handler
});
try {
_("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;
Weave.Service.clusterURL = TEST_CLUSTER_URL;
Weave.Service.login("johndoe", "ilovejane", syncKey);
do_check_true(Weave.Service.isLoggedIn);
_("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();
_("Checking that remoteSetup returns true.");
do_check_true(Weave.Service._remoteSetup());
_("Verify that the meta record was uploaded.");
@@ -115,51 +118,51 @@ function run_test() {
let metaModified = meta_global.modified;
Weave.Service.sync();
do_check_true(meta_global.wasCalled);
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);
do_check_neq(keysWBO.payload, existingKeysPayload);
// Try to screw up HMAC calculation.
// Re-encrypt keys with a new random keybundle, and upload them to the
// 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);
} finally {
Weave.Svc.Prefs.resetBranch("");
server.stop(do_test_finished);
}
}
--- a/services/sync/tests/unit/test_service_sync_updateEnabledEngines.js
+++ b/services/sync/tests/unit/test_service_sync_updateEnabledEngines.js
@@ -61,29 +61,23 @@ function sync_httpd_setup(handlers) {
let cl = new ServerCollection();
handlers["/1.1/johndoe/storage/clients"] =
upd("clients", cl.handler());
return 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;
}
const PAYLOAD = 42;
function run_test() {
initTestLogging("Trace");
@@ -255,17 +249,17 @@ add_test(function test_enabledRemotely()
});
setUp();
// We need to be very careful how we do this, so that we don't trigger a
// fresh start!
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.");
do_check_false(engine.enabled);
_("Sync.");
Weave.Service.sync();
--- a/services/sync/tests/unit/test_service_verifyLogin.js
+++ b/services/sync/tests/unit/test_service_verifyLogin.js
@@ -53,35 +53,35 @@ function run_test() {
_("Credentials won't check out because we're not configured yet.");
Status.resetSync();
do_check_false(Service.verifyLogin());
do_check_eq(Status.service, CLIENT_NOT_CONFIGURED);
do_check_eq(Status.login, LOGIN_FAILED_NO_USERNAME);
_("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);
_("verifyLogin() has found out the user's cluster URL, though.");
do_check_eq(Service.clusterURL, "http://localhost:8080/api/");
_("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) {
Svc.Obs.remove("weave:service:backoff:interval", observe);
backoffInterval = subject;
});
do_check_false(Service.verifyLogin());
do_check_true(Status.enforceBackoff);
--- a/services/sync/tests/unit/test_service_wipeServer.js
+++ b/services/sync/tests/unit/test_service_wipeServer.js
@@ -26,18 +26,18 @@ FakeCollection.prototype = {
}
};
function setUpTestFixtures() {
let cryptoService = new FakeCryptoService();
Service.serverURL = TEST_SERVER_URL;
Service.clusterURL = TEST_CLUSTER_URL;
- Service.username = "johndoe";
- Service.passphrase = "aabcdeabcdeabcdeabcdeabcde";
+
+ setBasicCredentials("johndoe", null, "aabcdeabcdeabcdeabcdeabcde");
}
function run_test() {
initTestLogging("Trace");
run_next_test();
}
@@ -50,16 +50,17 @@ add_test(function test_wipeServer_list_s
let server = httpd_setup({
"/1.1/johndoe/storage/steam": steam_coll.handler(),
"/1.1/johndoe/storage/diesel": diesel_coll.handler(),
"/1.1/johndoe/storage/petrol": httpd_handler(404, "Not Found")
});
try {
setUpTestFixtures();
+ new SyncTestingInfrastructure("johndoe", "irrelevant", "irrelevant");
_("Confirm initial environment.");
do_check_false(steam_coll.deleted);
do_check_false(diesel_coll.deleted);
_("wipeServer() will happily ignore the non-existent collection and use the timestamp of the last DELETE that was successful.");
let timestamp = Service.wipeServer(["steam", "diesel", "petrol"]);
do_check_eq(timestamp, diesel_coll.timestamp);
@@ -83,16 +84,17 @@ add_test(function test_wipeServer_list_5
let server = httpd_setup({
"/1.1/johndoe/storage/steam": steam_coll.handler(),
"/1.1/johndoe/storage/petrol": httpd_handler(503, "Service Unavailable"),
"/1.1/johndoe/storage/diesel": diesel_coll.handler()
});
try {
setUpTestFixtures();
+ new SyncTestingInfrastructure("johndoe", "irrelevant", "irrelevant");
_("Confirm initial environment.");
do_check_false(steam_coll.deleted);
do_check_false(diesel_coll.deleted);
_("wipeServer() will happily ignore the non-existent collection, delete the 'steam' collection and abort after an receiving an error on the 'petrol' collection.");
let error;
try {
@@ -130,16 +132,17 @@ add_test(function test_wipeServer_all_su
}
let server = httpd_setup({
"/1.1/johndoe/storage": storageHandler
});
setUpTestFixtures();
_("Try deletion.");
+ new SyncTestingInfrastructure("johndoe", "irrelevant", "irrelevant");
let returnedTimestamp = Service.wipeServer();
do_check_true(deleted);
do_check_eq(returnedTimestamp, serverTimestamp);
server.stop(run_next_test);
Svc.Prefs.resetBranch("");
});
@@ -161,16 +164,17 @@ add_test(function test_wipeServer_all_40
}
let server = httpd_setup({
"/1.1/johndoe/storage": storageHandler
});
setUpTestFixtures();
_("Try deletion.");
+ new SyncTestingInfrastructure("johndoe", "irrelevant", "irrelevant");
let returnedTimestamp = Service.wipeServer();
do_check_true(deleted);
do_check_eq(returnedTimestamp, serverTimestamp);
server.stop(run_next_test);
Svc.Prefs.resetBranch("");
});
@@ -189,16 +193,17 @@ add_test(function test_wipeServer_all_50
let server = httpd_setup({
"/1.1/johndoe/storage": storageHandler
});
setUpTestFixtures();
_("Try deletion.");
let error;
try {
+ new SyncTestingInfrastructure("johndoe", "irrelevant", "irrelevant");
Service.wipeServer();
do_throw("Should have thrown!");
} catch (ex) {
error = ex;
}
do_check_eq(error.status, 503);
server.stop(run_next_test);
--- a/services/sync/tests/unit/test_status_checkSetup.js
+++ b/services/sync/tests/unit/test_status_checkSetup.js
@@ -1,45 +1,44 @@
+/* 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);
do_check_eq(Status.login, LOGIN_FAILED_NO_USERNAME);
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();
} finally {
Svc.Prefs.resetBranch("");
}
}
--- a/services/sync/tests/unit/test_syncengine.js
+++ b/services/sync/tests/unit/test_syncengine.js
@@ -90,17 +90,17 @@ function test_toFetch() {
// Read file from disk
toFetch = [Utils.makeGUID(), Utils.makeGUID()];
syncTesting.fakeFilesystem.fakeContents[filename] = JSON.stringify(toFetch);
engine.loadToFetch();
do_check_eq(engine.toFetch.length, 2);
do_check_eq(engine.toFetch[0], toFetch[0]);
do_check_eq(engine.toFetch[1], toFetch[1]);
} finally {
- syncTesting = new SyncTestingInfrastructure(makeSteamEngine);
+ Svc.Prefs.resetBranch("");
}
}
function test_previousFailed() {
_("SyncEngine.previousFailed corresponds to file on disk");
let syncTesting = new SyncTestingInfrastructure();
const filename = "weave/failed/steam.json";
let engine = makeSteamEngine();
@@ -120,17 +120,17 @@ function test_previousFailed() {
// Read file from disk
previousFailed = [Utils.makeGUID(), Utils.makeGUID()];
syncTesting.fakeFilesystem.fakeContents[filename] = JSON.stringify(previousFailed);
engine.loadPreviousFailed();
do_check_eq(engine.previousFailed.length, 2);
do_check_eq(engine.previousFailed[0], previousFailed[0]);
do_check_eq(engine.previousFailed[1], previousFailed[1]);
} finally {
- syncTesting = new SyncTestingInfrastructure(makeSteamEngine);
+ Svc.Prefs.resetBranch("");
}
}
function test_resetClient() {
_("SyncEngine.resetClient resets lastSync and toFetch");
let syncTesting = new SyncTestingInfrastructure();
let engine = makeSteamEngine();
try {
@@ -145,17 +145,16 @@ function test_resetClient() {
engine.previousFailed = [Utils.makeGUID(), Utils.makeGUID(), Utils.makeGUID()];
engine.resetClient();
do_check_eq(engine.lastSync, 0);
do_check_eq(engine.lastSyncLocal, 0);
do_check_eq(engine.toFetch.length, 0);
do_check_eq(engine.previousFailed.length, 0);
} finally {
- syncTesting = new SyncTestingInfrastructure(makeSteamEngine);
Svc.Prefs.resetBranch("");
}
}
function test_wipeServer() {
_("SyncEngine.wipeServer deletes server data and resets the client.");
let syncTesting = new SyncTestingInfrastructure();
Svc.Prefs.set("serverURL", TEST_SERVER_URL);
@@ -177,17 +176,16 @@ function test_wipeServer() {
_("Wipe server data and reset client.");
engine.wipeServer();
do_check_eq(steamCollection.payload, undefined);
do_check_eq(engine.lastSync, 0);
do_check_eq(engine.toFetch.length, 0);
} finally {
server.stop(do_test_finished);
- syncTesting = new SyncTestingInfrastructure(makeSteamEngine);
Svc.Prefs.resetBranch("");
}
}
function run_test() {
test_url_attributes();
test_syncID();
test_lastSync();
--- a/services/sync/tests/unit/test_syncengine_sync.js
+++ b/services/sync/tests/unit/test_syncengine_sync.js
@@ -13,16 +13,23 @@ function makeRotaryEngine() {
function cleanAndGo(server) {
Svc.Prefs.resetBranch("");
Svc.Prefs.set("log.logger.engine.rotary", "Trace");
Records.clearCache();
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();
let contents = {
meta: {global: {engines: {rotary: {version: engine.version,
syncID: engine.syncID}}}},
crypto: {},
rotary: {}
--- a/services/sync/tests/unit/test_syncscheduler.js
+++ b/services/sync/tests/unit/test_syncscheduler.js
@@ -44,24 +44,22 @@ function sync_httpd_setup() {
"/1.1/johndoe/storage/crypto/keys":
upd("crypto", (new ServerWBO("keys")).handler()),
"/1.1/johndoe/storage/clients": upd("clients", clientsColl.handler()),
"/user/1.0/johndoe/node/weave": httpd_handler(200, "OK", "null")
});
}
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;
}
function cleanUpAndGo(server) {
Utils.nextTick(function () {
Service.startOver();
if (server) {
server.stop(run_next_test);
@@ -208,24 +206,26 @@ add_test(function test_masterpassword_lo
});
add_test(function test_calculateBackoff() {
do_check_eq(Status.backoffInterval, 0);
// 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);
cleanUpAndGo();
});
add_test(function test_scheduleNextSync_nowOrPast() {
Svc.Obs.add("weave:service:sync:finish", function onSyncFinish() {
@@ -461,47 +461,47 @@ add_test(function test_autoconnect_nextS
waitForZeroTimer(function () {
do_check_eq(SyncScheduler.nextSync, expectedSync);
do_check_true(SyncScheduler.syncTimer.delay >= expectedInterval);
Svc.Obs.remove("weave:service:login:start", onLoginStart);
cleanUpAndGo();
});
- Service.username = "johndoe";
- Service.password = "ilovejane";
- Service.passphrase = "abcdeabcdeabcdeabcdeabcdea";
+ setBasicCredentials("johndoe", "ilovejane", "abcdeabcdeabcdeabcdeabcdea");
SyncScheduler.delayedAutoConnect(0);
});
add_test(function test_autoconnect_mp_locked() {
let server = sync_httpd_setup();
setUp();
// Pretend user did not unlock master password.
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";
});
// A locked master password will still trigger a sync, but then we'll hit
// MASTER_PASSWORD_LOCKED and hence MASTER_PASSWORD_LOCKED_RETRY_INTERVAL.
Svc.Obs.add("weave:service:login:error", function onLoginError() {
Svc.Obs.remove("weave:service:login:error", onLoginError);
Utils.nextTick(function aLittleBitAfterLoginError() {
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);
});
});
SyncScheduler.delayedAutoConnect(0);
});
@@ -844,20 +844,18 @@ add_test(function test_sync_503_Retry_Af
do_check_true(SyncScheduler.nextSync >= Date.now() + minimumExpectedDelay);
do_check_true(SyncScheduler.syncTimer.delay >= minimumExpectedDelay);
cleanUpAndGo(server);
});
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
Svc.Obs.add("weave:service:login:error", function onLoginError() {
Svc.Obs.remove("weave:service:login:error", onLoginError);
Utils.nextTick(function aLittleBitAfterLoginError() {
do_check_eq(Status.login, LOGIN_FAILED_NETWORK_ERROR);
@@ -888,20 +886,18 @@ add_test(function test_loginError_recove
do_check_eq(Status.checkSetup(), STATUS_OK);
do_check_eq(Status.login, LOGIN_SUCCEEDED);
SyncScheduler.scheduleNextSync(0);
});
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
let server = httpd_setup({
"/1.1/johndoe/info/collections": httpd_handler(401, "Unauthorized")
});
--- a/services/sync/tests/unit/test_syncstoragerequest.js
+++ b/services/sync/tests/unit/test_syncstoragerequest.js
@@ -52,27 +52,26 @@ add_test(function test_user_agent_mobile
server.stop(run_next_test);
});
});
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) {
do_check_eq(error, null);
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);
});
});
/**
* The X-Weave-Timestamp header updates SyncStorageRequest.serverTime.
*/
add_test(function test_weave_timestamp() {
--- a/services/sync/tests/unit/test_upgrade_old_sync_key.js
+++ b/services/sync/tests/unit/test_upgrade_old_sync_key.js
@@ -1,9 +1,13 @@
+/* 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;
// Test upgrade of a dashed old-style sync key.
function run_test() {
const PBKDF2_KEY_BYTES = 16;
initTestLogging("Trace");
@@ -16,21 +20,22 @@ function run_test() {
// Still not a modern passphrase...
do_check_false(Utils.isPassphrase(normalized));
// ... but different.
do_check_neq(normalized, passphrase);
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));
// Now let's verify that it's been derived correctly, from the normalized
// version, and the encoded sync ID.
_("Sync ID: " + Weave.Service.syncID);
let derivedKeyStr =
Utils.derivePresentableKeyFromPassphrase(normalized,
--- a/services/sync/tests/unit/xpcshell.ini
+++ b/services/sync/tests/unit/xpcshell.ini
@@ -1,78 +1,85 @@
[DEFAULT]
head = head_appinfo.js head_helpers.js head_http_server.js
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_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]
+
+# 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]
[test_service_cluster.js]
[test_service_createAccount.js]
[test_service_detect_upgrade.js]
[test_service_getStorageInfo.js]
[test_service_login.js]
@@ -87,45 +94,58 @@ skip-if = os == "win" || os == "android"
# Bug 676978: test hangs on Android (see also testing/xpcshell/xpcshell.ini)
skip-if = os == "android"
[test_service_sync_updateEnabledEngines.js]
# Bug 676978: test hangs on Android (see also testing/xpcshell/xpcshell.ini)
skip-if = os == "android"
[test_service_verifyLogin.js]
[test_service_wipeClient.js]
[test_service_wipeServer.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_syncengine.js]
-[test_syncengine_sync.js]
-# Bug 676978: test hangs on Android (see also testing/xpcshell/xpcshell.ini)
-skip-if = os == "android"
[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]
--- a/services/sync/tps/extensions/tps/modules/sync.jsm
+++ b/services/sync/tps/extensions/tps/modules/sync.jsm
@@ -97,19 +97,19 @@ var TPS = {
SetupSyncAccount: function TPS__SetupSyncAccount() {
try {
let serverURL = prefs.getCharPref('tps.account.serverURL');
if (serverURL) {
Weave.Service.serverURL = serverURL;
}
}
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");
},
Sync: function TPS__Sync(options) {
Logger.logInfo('Mozmill starting sync operation: ' + options);
switch(options) {
case SYNC_WIPE_REMOTE:
Weave.Svc.Prefs.set("firstSync", "wipeRemote");
--- a/services/sync/tps/extensions/tps/modules/tps.jsm
+++ b/services/sync/tps/extensions/tps/modules/tps.jsm
@@ -779,27 +779,27 @@ let TPS =
}
Logger.logInfo("Setting client credentials.");
if (account["admin-secret"]) {
// if admin-secret is specified, we'll dynamically create
// 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");
return;
}
Service.login();
Logger.AssertEqual(Weave.Status.service, Weave.STATUS_OK, "Weave status not OK");