--- a/browser/base/content/browser-syncui.js
+++ b/browser/base/content/browser-syncui.js
@@ -369,19 +369,20 @@ var gSyncUI = {
otherwise the tooltip reflects the fact that Sync needs to be
(re-)configured.
*/
_updateSyncButtonsTooltip: Task.async(function* () {
if (!gBrowser)
return;
let email;
- try {
- email = Services.prefs.getCharPref("services.sync.username");
- } catch (ex) {}
+ let user = yield fxAccounts.getSignedInUser();
+ if (user) {
+ email = user.email;
+ }
let needsSetup = yield this._needsSetup();
let needsVerification = yield this._needsVerification();
let loginFailed = this.loginFailed();
// This is a little messy as the Sync buttons are 1/2 Sync related and
// 1/2 FxA related - so for some strings we use Sync strings, but for
// others we reach into gFxAccounts for strings.
let tooltiptext;
--- a/browser/base/content/sync/setup.js
+++ b/browser/base/content/sync/setup.js
@@ -215,17 +215,16 @@ var gSyncSetup = {
let feedback;
switch (Weave.Status.login) {
case Weave.LOGIN_FAILED_NETWORK_ERROR:
case Weave.LOGIN_FAILED_SERVER_ERROR:
feedback = server;
break;
case Weave.LOGIN_FAILED_LOGIN_REJECTED:
- case Weave.LOGIN_FAILED_NO_USERNAME:
case Weave.LOGIN_FAILED_NO_PASSWORD:
feedback = password;
break;
case Weave.LOGIN_FAILED_INVALID_PASSPHRASE:
feedback = passphrase;
break;
}
this._setFeedbackMessage(feedback, false, Weave.Status.login);
--- a/services/sync/Weave.js
+++ b/services/sync/Weave.js
@@ -97,32 +97,26 @@ WeaveService.prototype = {
return deferred.promise;
},
/**
* Whether Firefox Accounts is enabled.
*
* @return bool
*/
+ // TODO - Remove this getter and all accessors
get fxAccountsEnabled() {
- try {
- // Old sync guarantees '@' will never appear in the username while FxA
- // uses the FxA email address - so '@' is the flag we use.
- let username = Services.prefs.getCharPref(SYNC_PREFS_BRANCH + "username");
- return !username || username.includes("@");
- } catch (_) {
- return true; // No username == only allow FxA to be configured.
- }
+ // Always return true.
+ return true;
},
/**
* Whether Sync appears to be enabled.
*
- * This returns true if all the Sync preferences for storing account
- * and server configuration are populated.
+ * This returns true if we have an associated FxA account
*
* It does *not* perform a robust check to see if the client is working.
* For that, you'll want to check Weave.Status.checkSetup().
*/
get enabled() {
let prefs = Services.prefs.getBranch(SYNC_PREFS_BRANCH);
return prefs.prefHasUserValue("username");
},
@@ -137,20 +131,19 @@ WeaveService.prototype = {
case "final-ui-startup":
// Force Weave service to load if it hasn't triggered from overlays
this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
this.timer.initWithCallback({
notify: function() {
let isConfigured = false;
// We only load more if it looks like Sync is configured.
- let prefs = Services.prefs.getBranch(SYNC_PREFS_BRANCH);
- if (prefs.prefHasUserValue("username")) {
- // We have a username. So, do a more thorough check. This will
- // import a number of modules and thus increase memory
+ if (this.enabled) {
+ // We have an associated FxAccount. So, do a more thorough check.
+ // This will import a number of modules and thus increase memory
// accordingly. We could potentially copy code performed by
// this check into this file if our above code is yielding too
// many false positives.
Components.utils.import("resource://services-sync/main.js");
isConfigured = Weave.Status.checkSetup() != Weave.CLIENT_NOT_CONFIGURED;
}
let getHistogramById = Services.telemetry.getHistogramById;
getHistogramById("WEAVE_CONFIGURED").add(isConfigured);
--- a/services/sync/modules-testing/utils.js
+++ b/services/sync/modules-testing/utils.js
@@ -22,17 +22,16 @@ this.EXPORTED_SYMBOLS = [
"MockFxaStorageManager",
"AccountState", // from a module import
"sumHistogram",
];
var {utils: Cu} = Components;
Cu.import("resource://services-sync/status.js");
-Cu.import("resource://services-sync/identity.js");
Cu.import("resource://services-common/utils.js");
Cu.import("resource://services-crypto/utils.js");
Cu.import("resource://services-sync/util.js");
Cu.import("resource://services-sync/browserid_identity.js");
Cu.import("resource://testing-common/services/common/logging.js");
Cu.import("resource://testing-common/services/sync/fakeservices.js");
Cu.import("resource://gre/modules/FxAccounts.jsm");
Cu.import("resource://gre/modules/FxAccountsClient.jsm");
@@ -165,33 +164,24 @@ this.makeIdentityConfig = function(overr
token: {
endpoint: null,
duration: 300,
id: "id",
key: "key",
hashed_fxa_uid: "f".repeat(32), // used during telemetry validation
// uid will be set to the username.
}
- },
- sync: {
- // username will come from the top-level username
- password: "whatever",
- syncKey: "abcdeabcdeabcdeabcdeabcdea",
}
};
// Now handle any specified overrides.
if (overrides) {
if (overrides.username) {
result.username = overrides.username;
}
- if (overrides.sync) {
- // TODO: allow just some attributes to be specified
- result.sync = overrides.sync;
- }
if (overrides.fxaccount) {
// TODO: allow just some attributes to be specified
result.fxaccount = overrides.fxaccount;
}
}
return result;
}
@@ -255,54 +245,40 @@ this.configureIdentity = async function(
let config = makeIdentityConfig(identityOverrides, server);
let ns = {};
Cu.import("resource://services-sync/service.js", ns);
if (server) {
ns.Service.serverURL = server.baseURI;
}
- ns.Service._clusterManager = ns.Service.identity.createClusterManager(ns.Service);
-
- if (ns.Service.identity instanceof BrowserIDManager) {
- // do the FxAccounts thang...
-
- // If a server was specified, ensure FxA has a correct cluster URL available.
- if (server && !config.fxaccount.token.endpoint) {
- let ep = server.baseURI;
- if (!ep.endsWith("/")) {
- ep += "/";
- }
- ep += "1.1/" + config.username + "/";
- config.fxaccount.token.endpoint = ep;
+ // If a server was specified, ensure FxA has a correct cluster URL available.
+ if (server && !config.fxaccount.token.endpoint) {
+ let ep = server.baseURI;
+ if (!ep.endsWith("/")) {
+ ep += "/";
}
+ ep += "1.1/" + config.username + "/";
+ config.fxaccount.token.endpoint = ep;
+ }
- configureFxAccountIdentity(ns.Service.identity, config);
- await ns.Service.identity.initializeWithCurrentIdentity();
- // and cheat to avoid requiring each test do an explicit login - give it
- // a cluster URL.
- if (config.fxaccount.token.endpoint) {
- ns.Service.clusterURL = config.fxaccount.token.endpoint;
- }
- return;
+ configureFxAccountIdentity(ns.Service.identity, config);
+ await ns.Service.identity.initializeWithCurrentIdentity();
+ // and cheat to avoid requiring each test do an explicit login - give it
+ // a cluster URL.
+ if (config.fxaccount.token.endpoint) {
+ ns.Service.clusterURL = config.fxaccount.token.endpoint;
}
- // old style identity provider.
- if (server) {
- ns.Service.clusterURL = server.baseURI + "/";
- }
- ns.Service.identity.username = config.username;
- ns.Service._updateCachedURLs();
- setBasicCredentials(config.username, config.sync.password, config.sync.syncKey);
}
-this.SyncTestingInfrastructure = async function(server, username, password) {
+this.SyncTestingInfrastructure = async function(server, username) {
let ns = {};
Cu.import("resource://services-sync/service.js", ns);
- let config = makeIdentityConfig({ username, password });
+ let config = makeIdentityConfig({ username });
await configureIdentity(config, server);
return {
logStats: initTestLogging(),
fakeFilesystem: new FakeFilesystemService({}),
fakeGUIDService: new FakeGUIDService(),
fakeCryptoService: new FakeCryptoService(),
}
}
@@ -318,41 +294,32 @@ this.encryptPayload = function encryptPa
return {
ciphertext: cleartext, // ciphertext == cleartext with fake crypto
IV: "irrelevant",
hmac: fakeSHA256HMAC(cleartext, CryptoUtils.makeHMACKey("")),
};
}
// This helper can be used instead of 'add_test' or 'add_task' to run the
-// specified test function twice - once with the old-style sync identity
-// manager and once with the new-style BrowserID identity manager, to ensure
-// it works in both cases.
+// specified test function with different identity managers.
+// So far we use this with one, the FxA one, but we keep it in case we change
+// idmanagers again.
//
// * The test itself should be passed as 'test' - ie, test code will generally
// pass |this|.
// * The test function is a regular test function - although note that it must
// be a generator - async operations should yield them, and run_next_test
// mustn't be called.
this.add_identity_test = function(test, testFunction) {
function note(what) {
let msg = "running test " + testFunction.name + " with " + what + " identity manager";
test.do_print(msg);
}
let ns = {};
Cu.import("resource://services-sync/service.js", ns);
- // one task for the "old" identity manager.
- test.add_task(async function() {
- note("sync");
- let oldIdentity = Status._authManager;
- ensureLegacyIdentityManager();
- await testFunction();
- Status.__authManager = ns.Service.identity = oldIdentity;
- });
- // another task for the FxAccounts identity manager.
test.add_task(async function() {
note("FxAccounts");
let oldIdentity = Status._authManager;
Status.__authManager = ns.Service.identity = new BrowserIDManager();
await testFunction();
Status.__authManager = ns.Service.identity = oldIdentity;
});
}
--- a/services/sync/modules/browserid_identity.js
+++ b/services/sync/modules/browserid_identity.js
@@ -8,23 +8,21 @@ this.EXPORTED_SYMBOLS = ["BrowserIDManag
var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/Log.jsm");
Cu.import("resource://services-common/async.js");
Cu.import("resource://services-common/utils.js");
Cu.import("resource://services-common/tokenserverclient.js");
Cu.import("resource://services-crypto/utils.js");
-Cu.import("resource://services-sync/identity.js");
Cu.import("resource://services-sync/util.js");
Cu.import("resource://services-common/tokenserverclient.js");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://services-sync/constants.js");
Cu.import("resource://gre/modules/Promise.jsm");
-Cu.import("resource://services-sync/stages/cluster.js");
Cu.import("resource://gre/modules/FxAccounts.jsm");
// Lazy imports to prevent unnecessary load on startup.
XPCOMUtils.defineLazyModuleGetter(this, "Weave",
"resource://services-sync/main.js");
XPCOMUtils.defineLazyModuleGetter(this, "BulkKeyBundle",
"resource://services-sync/keys.js");
@@ -84,18 +82,16 @@ this.BrowserIDManager = function Browser
this._tokenServerClient = new TokenServerClient();
this._tokenServerClient.observerPrefix = "weave:service";
// will be a promise that resolves when we are ready to authenticate
this.whenReadyToAuthenticate = null;
this._log = log;
};
this.BrowserIDManager.prototype = {
- __proto__: IdentityManager.prototype,
-
_fxaService: null,
_tokenServerClient: null,
// https://docs.services.mozilla.com/token/apis.html
_token: null,
_signedInUser: null, // the signedinuser we got from FxAccounts.
// null if no error, otherwise a LOGIN_FAILED_* value that indicates why
// we failed to authenticate (but note it might not be an actual
@@ -133,29 +129,16 @@ this.BrowserIDManager.prototype = {
deviceID() {
return this._signedInUser && this._signedInUser.deviceId;
},
initialize() {
for (let topic of OBSERVER_TOPICS) {
Services.obs.addObserver(this, topic, false);
}
- // and a background fetch of account data just so we can set this.account,
- // so we have a username available before we've actually done a login.
- // XXX - this is actually a hack just for tests and really shouldn't be
- // necessary. Also, you'd think it would be safe to allow this.account to
- // be set to null when there's no user logged in, but argue with the test
- // suite, not with me :)
- this._fxaService.getSignedInUser().then(accountData => {
- if (accountData) {
- this.account = accountData.email;
- }
- }).catch(err => {
- // As above, this is only for tests so it is safe to ignore.
- });
},
/**
* Ensure the user is logged in. Returns a promise that resolves when
* the user is logged in, or is rejected if the login attempt has failed.
*/
ensureLoggedIn() {
if (!this._shouldHaveSyncKeyBundle && this.whenReadyToAuthenticate) {
@@ -220,24 +203,23 @@ this.BrowserIDManager.prototype = {
// user completes a force authentication, so we should make
// sure all credentials are reset before proceeding.
this.resetCredentials();
this._authFailureReason = null;
return this._fxaService.getSignedInUser().then(accountData => {
if (!accountData) {
this._log.info("initializeWithCurrentIdentity has no user logged in");
- this.account = null;
// and we are as ready as we can ever be for auth.
this._shouldHaveSyncKeyBundle = true;
this.whenReadyToAuthenticate.reject("no user is logged in");
return;
}
- this.account = accountData.email;
+ this.username = accountData.email;
this._updateSignedInUser(accountData);
// The user must be verified before we can do anything at all; we kick
// this and the rest of initialization off in the background (ie, we
// don't return the promise)
this._log.info("Waiting for user to be verified.");
this._fxaService.whenVerified(accountData).then(accountData => {
this._updateSignedInUser(accountData);
this._log.info("Starting fetch for key bundle.");
@@ -360,89 +342,65 @@ this.BrowserIDManager.prototype = {
_now() {
return this._fxaService.now()
},
get _localtimeOffsetMsec() {
return this._fxaService.localtimeOffsetMsec;
},
- usernameFromAccount(val) {
- // we don't differentiate between "username" and "account"
- return val;
+ get syncKeyBundle() {
+ return this._syncKeyBundle;
},
- /**
- * Obtains the HTTP Basic auth password.
- *
- * Returns a string if set or null if it is not set.
- */
- get basicPassword() {
- this._log.error("basicPassword getter should be not used in BrowserIDManager");
- return null;
- },
-
- /**
- * Set the HTTP basic password to use.
- *
- * Changes will not persist unless persistSyncCredentials() is called.
- */
- set basicPassword(value) {
- throw new Error("basicPassword setter should be not used in BrowserIDManager");
+ get username() {
+ return Svc.Prefs.get("username", null);
},
/**
- * Obtain the Sync Key.
+ * Set the username value.
*
- * 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.
+ * Changing the username has the side-effect of wiping credentials.
*/
- get syncKey() {
- if (this.syncKeyBundle) {
- // TODO: This is probably fine because the code shouldn't be
- // using the sync key directly (it should use the sync key
- // bundle), but I don't like it. We should probably refactor
- // code that is inspecting this to not do validation on this
- // field directly and instead call a isSyncKeyValid() function
- // that we can override.
- return "99999999999999999999999999";
+ set username(value) {
+ if (value) {
+ value = value.toLowerCase();
+
+ if (value == this.username) {
+ return;
+ }
+
+ Svc.Prefs.set("username", value);
+ } else {
+ Svc.Prefs.reset("username");
}
- return null;
- },
- set syncKey(value) {
- throw "syncKey setter should be not used in BrowserIDManager";
- },
-
- get syncKeyBundle() {
- return this._syncKeyBundle;
+ // 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.resetCredentials();
},
/**
* Resets/Drops all credentials we hold for the current user.
*/
resetCredentials() {
- this.resetSyncKey();
+ this.resetSyncKeyBundle();
this._token = null;
this._hashedUID = null;
// The cluster URL comes from the token, so resetting it to empty will
// force Sync to not accidentally use a value from an earlier token.
Weave.Service.clusterURL = null;
},
/**
- * Resets/Drops the sync key we hold for the current user.
+ * Resets/Drops the sync key bundle we hold for the current user.
*/
- resetSyncKey() {
- this._syncKey = null;
+ resetSyncKeyBundle() {
this._syncKeyBundle = null;
- this._syncKeyUpdated = true;
this._shouldHaveSyncKeyBundle = false;
},
/**
* Pre-fetches any information that might help with migration away from this
* identity. Called after every sync and is really just an optimization that
* allows us to avoid a network request for when we actually need the
* migration info.
@@ -454,29 +412,42 @@ this.BrowserIDManager.prototype = {
/**
* Return credentials hosts for this identity only.
*/
_getSyncCredentialsHosts() {
return Utils.getSyncCredentialsHostsFxA();
},
/**
+ * Deletes Sync credentials from the password manager.
+ */
+ deleteSyncCredentials() {
+ for (let host of this._getSyncCredentialsHosts()) {
+ let logins = Services.logins.findLogins({}, host, "", "");
+ for (let login of logins) {
+ Services.logins.removeLogin(login);
+ }
+ }
+ },
+
+ /**
* The current state of the auth credentials.
*
* This essentially validates that enough credentials are available to use
* Sync. It doesn't check we have all the keys we need as the master-password
* may have been locked when we tried to get them - we rely on
* unlockAndVerifyAuthState to check that for us.
*/
get currentAuthState() {
if (this._authFailureReason) {
this._log.info("currentAuthState returning " + this._authFailureReason +
" due to previous failure");
return this._authFailureReason;
}
+
// TODO: need to revisit this. Currently this isn't ready to go until
// both the username and syncKeyBundle are both configured and having no
// username seems to make things fail fast so that's good.
if (!this.username) {
return LOGIN_FAILED_NO_USERNAME;
}
return STATUS_OK;
@@ -796,21 +767,49 @@ this.BrowserIDManager.prototype = {
return LOGIN_FAILED_NETWORK_ERROR;
},
};
/* An implementation of the ClusterManager for this identity
*/
function BrowserIDClusterManager(service) {
- ClusterManager.call(this, service);
+ this._log = log;
+ this.service = service;
}
BrowserIDClusterManager.prototype = {
- __proto__: ClusterManager.prototype,
+ get identity() {
+ return this.service.identity;
+ },
+
+ /**
+ * Determine the cluster for the current user and update state.
+ */
+ setCluster() {
+ // Make sure we didn't get some unexpected response for the cluster.
+ let cluster = this._findCluster();
+ this._log.debug("Cluster value = " + cluster);
+ if (cluster == null) {
+ return false;
+ }
+
+ // Convert from the funky "String object with additional properties" that
+ // resource.js returns to a plain-old string.
+ cluster = cluster.toString();
+ // Don't update stuff if we already have the right cluster
+ if (cluster == this.service.clusterURL) {
+ return false;
+ }
+
+ this._log.debug("Setting cluster to " + cluster);
+ this.service.clusterURL = cluster;
+
+ return true;
+ },
_findCluster() {
let endPointFromIdentityToken = function() {
// The only reason (in theory ;) that we can end up with a null token
// is when this.identity._canFetchKeys() returned false. In turn, this
// should only happen if the master-password is locked or the credentials
// storage is screwed, and in those cases we shouldn't have started
// syncing so shouldn't get here anyway.
--- a/services/sync/modules/engines.js
+++ b/services/sync/modules/engines.js
@@ -14,17 +14,16 @@ this.EXPORTED_SYMBOLS = [
var {classes: Cc, interfaces: Ci, results: Cr, utils: Cu} = Components;
Cu.import("resource://services-common/async.js");
Cu.import("resource://gre/modules/JSONFile.jsm");
Cu.import("resource://gre/modules/Log.jsm");
Cu.import("resource://gre/modules/osfile.jsm");
Cu.import("resource://services-common/observers.js");
Cu.import("resource://services-sync/constants.js");
-Cu.import("resource://services-sync/identity.js");
Cu.import("resource://services-sync/record.js");
Cu.import("resource://services-sync/resource.js");
Cu.import("resource://services-sync/util.js");
XPCOMUtils.defineLazyModuleGetter(this, "fxAccounts",
"resource://gre/modules/FxAccounts.jsm");
/*
deleted file mode 100644
--- a/services/sync/modules/identity.js
+++ /dev/null
@@ -1,604 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-this.EXPORTED_SYMBOLS = ["IdentityManager"];
-
-var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Promise.jsm");
-Cu.import("resource://services-sync/constants.js");
-Cu.import("resource://gre/modules/Log.jsm");
-Cu.import("resource://services-sync/util.js");
-Cu.import("resource://services-common/async.js");
-
-// Lazy import to prevent unnecessary load on startup.
-for (let symbol of ["BulkKeyBundle", "SyncKeyBundle"]) {
- XPCOMUtils.defineLazyModuleGetter(this, symbol,
- "resource://services-sync/keys.js",
- symbol);
-}
-
-/**
- * Manages "legacy" identity and authentication for Sync.
- * See browserid_identity for the Firefox Accounts based identity manager.
- *
- * 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.
- *
- * 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.
- */
-this.IdentityManager = function IdentityManager() {
- this._log = Log.repository.getLogger("Sync.Identity");
- this._log.Level = Log.Level[Svc.Prefs.get("log.logger.identity")];
-
- 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,
-
- /**
- * Initialize the identity provider.
- */
- initialize() {
- // Nothing to do for this identity provider.
- },
-
- finalize() {
- // Nothing to do for this identity provider.
- },
-
- /**
- * Called whenever Service.logout() is called.
- */
- logout() {
- // nothing to do for this identity provider.
- },
-
- /**
- * Ensure the user is logged in. Returns a promise that resolves when
- * the user is logged in, or is rejected if the login attempt has failed.
- */
- ensureLoggedIn() {
- // nothing to do for this identity provider
- return Promise.resolve();
- },
-
- get account() {
- return Svc.Prefs.get("account", this.username);
- },
-
- /**
- * 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 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.resetCredentials();
- },
-
- /**
- * Resets/Drops all credentials we hold for the current user.
- */
- resetCredentials() {
- this.basicPassword = null;
- this.resetSyncKey();
- },
-
- /**
- * Resets/Drops the sync key we hold for the current user.
- */
- resetSyncKey() {
- this.syncKey = null;
- // syncKeyBundle cleared as a result of setting syncKey.
- },
-
- /**
- * Obtains the HTTP Basic auth password.
- *
- * Returns a string if set or null if it is not set.
- */
- get basicPassword() {
- if (this._basicPasswordAllowLookup) {
- // We need a username to find the credentials.
- let username = this.username;
- if (!username) {
- return null;
- }
-
- for (let login of this._getLogins(PWDMGR_PASSWORD_REALM)) {
- if (login.username.toLowerCase() == username) {
- // It should already be UTF-8 encoded, but we don't take any chances.
- this._basicPassword = Utils.encodeUTF8(login.password);
- }
- }
-
- this._basicPasswordAllowLookup = false;
- }
-
- return this._basicPassword;
- },
-
- /**
- * Set the HTTP basic password to use.
- *
- * Changes will not persist unless persistSyncCredentials() is called.
- */
- set basicPassword(value) {
- // Wiping out value.
- if (!value) {
- this._log.info("Basic password has no value. Removing.");
- this._basicPassword = null;
- this._basicPasswordUpdated = true;
- this._basicPasswordAllowLookup = false;
- return;
- }
-
- let username = this.username;
- if (!username) {
- throw new Error("basicPassword cannot be set before username.");
- }
-
- this._log.info("Basic password being updated.");
- this._basicPassword = Utils.encodeUTF8(value);
- this._basicPasswordUpdated = true;
- },
-
- /**
- * 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 (let login of this._getLogins(PWDMGR_PASSPHRASE_REALM)) {
- if (login.username.toLowerCase() == username) {
- this._syncKey = login.password;
- }
- }
-
- this._syncKeyAllowLookup = false;
- }
-
- return this._syncKey;
- },
-
- /**
- * Set the active Sync Key.
- *
- * If being set to null, the Sync Key and its derived SyncKeyBundle are
- * removed. However, the Sync Key won't be deleted from the password manager
- * until persistSyncCredentials() is called.
- *
- * If a value is provided, it should be a 26 or 32 character "friendly"
- * Base32 string for which Utils.isPassphrase() returns true.
- *
- * A side-effect of setting the Sync Key is that a SyncKeyBundle is
- * generated. For historical reasons, this will silently error out if the
- * value is not a proper Sync Key (!Utils.isPassphrase()). This should be
- * fixed in the future (once service.js is more sane) to throw if the passed
- * value is not valid.
- */
- set syncKey(value) {
- if (!value) {
- this._log.info("Sync Key has no value. Deleting.");
- this._syncKey = null;
- this._syncKeyBundle = null;
- this._syncKeyUpdated = true;
- return;
- }
-
- if (!this.username) {
- throw new Error("syncKey cannot be set before username.");
- }
-
- this._log.info("Sync Key being updated.");
- this._syncKey = value;
-
- // Clear any cached Sync Key Bundle and regenerate it.
- this._syncKeyBundle = null;
-
- 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("Failed to create sync bundle", 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;
- }
-
- // If we have a Sync Key but no bundle, bundle creation failed, which
- // implies a bad Sync Key.
- if (!this.syncKeyBundle) {
- return LOGIN_FAILED_INVALID_PASSPHRASE;
- }
-
- return STATUS_OK;
- },
-
- /**
- * Verify the current auth state, unlocking the master-password if necessary.
- *
- * Returns a promise that resolves with the current auth state after
- * attempting to unlock.
- */
- unlockAndVerifyAuthState() {
- // Try to fetch the passphrase - this will prompt for MP unlock as a
- // side-effect...
- try {
- this.syncKey;
- } catch (ex) {
- this._log.debug("Fetching passphrase threw " + ex +
- "; assuming master password locked.");
- return Promise.resolve(MASTER_PASSWORD_LOCKED);
- }
- return Promise.resolve(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(force) {
- if (this._basicPasswordUpdated || force) {
- if (this._basicPassword) {
- this._setLogin(PWDMGR_PASSWORD_REALM, this.username,
- this._basicPassword);
- } else {
- for (let login of this._getLogins(PWDMGR_PASSWORD_REALM)) {
- Services.logins.removeLogin(login);
- }
- }
-
- this._basicPasswordUpdated = false;
- }
-
- if (this._syncKeyUpdated || force) {
- if (this._syncKey) {
- this._setLogin(PWDMGR_PASSPHRASE_REALM, this.username, this._syncKey);
- } else {
- for (let login of this._getLogins(PWDMGR_PASSPHRASE_REALM)) {
- Services.logins.removeLogin(login);
- }
- }
-
- this._syncKeyUpdated = false;
- }
-
- },
-
- /**
- * Deletes the Sync Key from the system.
- */
- deleteSyncKey: function deleteSyncKey() {
- this.syncKey = null;
- this.persistCredentials();
- },
-
- hasBasicCredentials: function hasBasicCredentials() {
- // Because JavaScript.
- return this.username && this.basicPassword && true;
- },
-
- /**
- * Pre-fetches any information that might help with migration away from this
- * identity. Called after every sync and is really just an optimization that
- * allows us to avoid a network request for when we actually need the
- * migration info.
- */
- prefetchMigrationSentinel(service) {
- // Try and fetch the migration sentinel - it will end up in the recordManager
- // cache.
- try {
- service.recordManager.get(service.storageURL + "meta/fxa_credentials");
- } catch (ex) {
- if (Async.isShutdownException(ex)) {
- throw ex;
- }
- this._log.warn("Failed to pre-fetch the migration sentinel", ex);
- }
- },
-
- /**
- * 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 (let login of 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);
- },
-
- /**
- * Return credentials hosts for this identity only.
- */
- _getSyncCredentialsHosts() {
- return Utils.getSyncCredentialsHostsLegacy();
- },
-
- /**
- * Deletes Sync credentials from the password manager.
- */
- deleteSyncCredentials: function deleteSyncCredentials() {
- for (let host of this._getSyncCredentialsHosts()) {
- let logins = Services.logins.findLogins({}, host, "", "");
- for (let login of 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));
- },
-
- createClusterManager(service) {
- Cu.import("resource://services-sync/stages/cluster.js");
- return new ClusterManager(service);
- },
-
- offerSyncOptions() {
- // Do nothing for Sync 1.1.
- return {accepted: true};
- },
-
- // Tell Sync what the login status should be if it saw a 401 fetching
- // info/collections as part of login verification (typically immediately
- // after login.)
- // In our case it means an authoritative "password is incorrect".
- loginStatusFromVerification404() {
- return LOGIN_FAILED_LOGIN_REJECTED;
- }
-
-};
--- a/services/sync/modules/policies.js
+++ b/services/sync/modules/policies.js
@@ -30,37 +30,29 @@ function getThrottledIntervalPreference(
this.SyncScheduler = function SyncScheduler(service) {
this.service = service;
this.init();
}
SyncScheduler.prototype = {
_log: Log.repository.getLogger("Sync.SyncScheduler"),
- _fatalLoginStatus: [LOGIN_FAILED_NO_USERNAME,
- LOGIN_FAILED_NO_PASSPHRASE,
+ _fatalLoginStatus: [LOGIN_FAILED_NO_PASSPHRASE,
LOGIN_FAILED_INVALID_PASSPHRASE,
LOGIN_FAILED_LOGIN_REJECTED],
/**
* The nsITimer object that schedules the next sync. See scheduleNextSync().
*/
syncTimer: null,
setDefaults: function setDefaults() {
this._log.trace("Setting SyncScheduler policy values to defaults.");
- let service = Cc["@mozilla.org/weave/service;1"]
- .getService(Ci.nsISupports)
- .wrappedJSObject;
-
- let part = service.fxAccountsEnabled ? "fxa" : "sync11";
- let prefSDInterval = "scheduler." + part + ".singleDeviceInterval";
- this.singleDeviceInterval = getThrottledIntervalPreference(prefSDInterval);
-
+ this.singleDeviceInterval = getThrottledIntervalPreference("scheduler.fxa.singleDeviceInterval");
this.idleInterval = getThrottledIntervalPreference("scheduler.idleInterval");
this.activeInterval = getThrottledIntervalPreference("scheduler.activeInterval");
this.immediateInterval = getThrottledIntervalPreference("scheduler.immediateInterval");
this.eolInterval = getThrottledIntervalPreference("scheduler.eolInterval");
// A user is non-idle on startup by default.
this.idle = false;
--- a/services/sync/modules/service.js
+++ b/services/sync/modules/service.js
@@ -16,20 +16,20 @@ const CLUSTER_BACKOFF = 5 * 60 * 1000; /
const PBKDF2_KEY_BYTES = 16;
const CRYPTO_COLLECTION = "crypto";
const KEYS_WBO = "keys";
Cu.import("resource://gre/modules/Preferences.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Log.jsm");
+Cu.import("resource://services-common/async.js");
Cu.import("resource://services-sync/constants.js");
Cu.import("resource://services-sync/engines.js");
Cu.import("resource://services-sync/engines/clients.js");
-Cu.import("resource://services-sync/identity.js");
Cu.import("resource://services-sync/policies.js");
Cu.import("resource://services-sync/record.js");
Cu.import("resource://services-sync/resource.js");
Cu.import("resource://services-sync/rest.js");
Cu.import("resource://services-sync/stages/enginesync.js");
Cu.import("resource://services-sync/stages/declined.js");
Cu.import("resource://services-sync/status.js");
Cu.import("resource://services-sync/telemetry.js");
@@ -149,17 +149,17 @@ Sync11Service.prototype = {
if (!this._clusterManager) {
return null;
}
return this._clusterManager.getUserBaseURL();
},
_updateCachedURLs: function _updateCachedURLs() {
// Nothing to cache yet if we don't have the building blocks
- if (!this.clusterURL || !this.identity.username) {
+ if (!this.clusterURL) {
// Also reset all other URLs used by Sync to ensure we aren't accidentally
// using one cached earlier - if there's no cluster URL any cached ones
// are invalid.
this.infoURL = undefined;
this.storageURL = undefined;
this.metaURL = undefined;
this.cryptoKeysURL = undefined;
return;
@@ -298,24 +298,16 @@ Sync11Service.prototype = {
},
/**
* Prepare to initialize the rest of Weave after waiting a little bit
*/
onStartup: function onStartup() {
this._migratePrefs();
- // Status is instantiated before us and is the first to grab an instance of
- // the IdentityManager. We use that instance because IdentityManager really
- // needs to be a singleton. Ideally, the longer-lived object would spawn
- // this service instance.
- if (!Status || !Status._authManager) {
- throw new Error("Status or Status._authManager not initialized.");
- }
-
this.status = Status;
this.identity = Status._authManager;
this.collectionKeys = new CollectionKeyManager();
this.errorHandler = new ErrorHandler(this);
this._log = Log.repository.getLogger("Sync.Service");
this._log.level =
@@ -544,31 +536,19 @@ Sync11Service.prototype = {
}
return info;
},
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.
-
- if (!this.identity.syncKey) {
- this.status.login = LOGIN_FAILED_NO_PASSPHRASE;
- this.status.sync = CREDENTIALS_CHANGED;
- return false;
- }
-
let syncKeyBundle = this.identity.syncKeyBundle;
if (!syncKeyBundle) {
- this._log.error("Sync Key Bundle not set. Invalid Sync Key?");
-
- this.status.login = LOGIN_FAILED_INVALID_PASSPHRASE;
+ this.status.login = LOGIN_FAILED_NO_PASSPHRASE;
this.status.sync = CREDENTIALS_CHANGED;
return false;
}
try {
if (!infoResponse)
infoResponse = this._fetchInfo(); // Will throw an exception on failure.
@@ -694,17 +674,17 @@ Sync11Service.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.identity.syncKey) {
+ if (!this.identity.syncKeyBundle) {
this._log.warn("No passphrase in verifyLogin.");
this.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(test)) {
@@ -802,40 +782,16 @@ Sync11Service.prototype = {
}
let keysChanged = this.handleFetchedKeys(this.identity.syncKeyBundle,
cryptoKeys, true);
if (keysChanged) {
this._log.info("Downloaded keys differed, as expected.");
}
},
- changePassphrase: function changePassphrase(newphrase) {
- return this._catch(function doChangePasphrase() {
- /* Wipe. */
- this.wipeServer();
-
- this.logout();
-
- /* Set this so UI is updated on next run. */
- this.identity.syncKey = newphrase;
- this.persistLogin();
-
- /* We need to re-encrypt everything, so reset. */
- this.resetClient();
- this.collectionKeys.clear();
-
- /* Login and sync. This also generates new keys. */
- this.sync();
-
- Svc.Obs.notify("weave:service:change-passphrase", true);
-
- return true;
- })();
- },
-
startOver: function startOver() {
this._log.trace("Invoking Service.startOver.");
Svc.Obs.notify("weave:engine:stop-tracking");
this.status.resetSync();
// 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.
@@ -850,17 +806,17 @@ Sync11Service.prototype = {
} else {
this._log.debug("Skipping client data removal: no cluster URL.");
}
// 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).
this._log.info("Service.startOver dropping sync key and logging out.");
- this.identity.resetSyncKey();
+ this.identity.resetSyncKeyBundle();
this.status.login = LOGIN_FAILED_NO_PASSPHRASE;
this.logout();
Svc.Obs.notify("weave:service:start-over");
// Reset all engines and clear keys.
this.resetClient();
this.collectionKeys.clear();
this.status.resetBackoff();
@@ -886,77 +842,51 @@ Sync11Service.prototype = {
return;
}
try {
this.identity.finalize();
// an observer so the FxA migration code can take some action before
// the new identity is created.
Svc.Obs.notify("weave:service:start-over:init-identity");
- this.identity.username = "";
this.status.__authManager = null;
this.identity = Status._authManager;
this._clusterManager = this.identity.createClusterManager(this);
Svc.Obs.notify("weave:service:start-over:finish");
} catch (err) {
this._log.error("startOver failed to re-initialize the identity manager: " + err);
// Still send the observer notification so the current state is
// reflected in the UI.
Svc.Obs.notify("weave:service:start-over:finish");
}
},
- persistLogin: function persistLogin() {
- try {
- this.identity.persistCredentials(true);
- } catch (ex) {
- this._log.info("Unable to persist credentials: " + ex);
- }
- },
-
- login: function login(username, password, passphrase) {
+ login: function login() {
function onNotify() {
this._loggedIn = false;
if (Services.io.offline) {
this.status.login = LOGIN_FAILED_NETWORK_ERROR;
throw "Application is offline, login should not be called";
}
- let initialStatus = this._checkSetup();
- 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.";
}
// Ask the identity manager to explicitly login now.
this._log.info("Logging in the user.");
let cb = Async.makeSpinningCallback();
this.identity.ensureLoggedIn().then(
() => cb(null),
err => cb(err || "ensureLoggedIn failed")
);
// Just let any errors bubble up - they've more context than we do!
cb.wait();
- // Calling login() with parameters when the client was
- // previously not configured means setup was completed.
- if (initialStatus == CLIENT_NOT_CONFIGURED
- && (username || password || passphrase)) {
- Svc.Obs.notify("weave:service:setup-complete");
- }
this._updateCachedURLs();
this._log.info("User logged in successfully - verifying login.");
if (!this.verifyLogin()) {
// verifyLogin sets the failure states here.
throw "Login failed: " + this.status.login;
}
@@ -1120,40 +1050,30 @@ Sync11Service.prototype = {
} else if (meta.payload.syncID != this.syncID) {
this._log.info("Sync IDs differ. Local is " + this.syncID + ", remote is " + meta.payload.syncID);
this.resetClient();
this.collectionKeys.clear();
this.syncID = meta.payload.syncID;
this._log.debug("Clear cached values and take syncId: " + this.syncID);
- if (!this.upgradeSyncKey(meta.payload.syncID)) {
- this._log.warn("Failed to upgrade sync key. Failing remote setup.");
- return false;
- }
-
if (!this.verifyAndFetchSymmetricKeys(infoResponse)) {
this._log.warn("Failed to fetch symmetric keys. Failing remote setup.");
return false;
}
// bug 545725 - re-verify creds and fail sanely
if (!this.verifyLogin()) {
this.status.sync = CREDENTIALS_CHANGED;
this._log.info("Credentials have changed, aborting sync and forcing re-login.");
return false;
}
return true;
}
- if (!this.upgradeSyncKey(meta.payload.syncID)) {
- this._log.warn("Failed to upgrade sync key. Failing remote setup.");
- return false;
- }
-
if (!this.verifyAndFetchSymmetricKeys(infoResponse)) {
this._log.warn("Failed to fetch symmetric keys. Failing remote setup.");
return false;
}
return true;
},
@@ -1368,63 +1288,20 @@ Sync11Service.prototype = {
this.recordManager.set(collectionURL, cryptoWrapper);
} catch (ex) {
this._log.error("Failed to set the migration sentinel: ${}", ex);
return Promise.resolve(false);
}
return Promise.resolve(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.identity.syncKey;
-
- if (!p) {
- return false;
- }
-
- // 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 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.identity.syncKey = k;
- this._log.info("Saving upgraded sync key...");
- this.persistLogin();
- this._log.info("Done saving.");
- return true;
- },
-
_freshStart: function _freshStart() {
- this._log.info("Fresh start. Resetting client and considering key upgrade.");
+ this._log.info("Fresh start. Resetting client.");
this.resetClient();
this.collectionKeys.clear();
- this.upgradeSyncKey(this.syncID);
// Wipe the server.
this.wipeServer();
// Upload a new meta/global record.
let meta = new WBORecord("meta", "global");
meta.payload.syncID = this.syncID;
meta.payload.storageVersion = STORAGE_VERSION;
@@ -1523,19 +1400,16 @@ Sync11Service.prototype = {
}
// Fully wipe each engine if it's able to decrypt data
for (let engine of engines) {
if (engine.canDecrypt()) {
engine.wipeClient();
}
}
-
- // Save the password/passphrase just in-case they aren't restored by sync
- this.persistLogin();
},
/**
* Wipe all remote user data by wiping the server then telling each remote
* client to wipe itself.
*
* @param engines [optional]
* Array of engine names to wipe. If not given, all engines are used.
deleted file mode 100644
--- a/services/sync/modules/stages/cluster.js
+++ /dev/null
@@ -1,113 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-this.EXPORTED_SYMBOLS = ["ClusterManager"];
-
-var {utils: Cu} = Components;
-
-Cu.import("resource://gre/modules/Log.jsm");
-Cu.import("resource://services-sync/constants.js");
-Cu.import("resource://services-sync/policies.js");
-Cu.import("resource://services-sync/util.js");
-
-/**
- * Contains code for managing the Sync cluster we are in.
- */
-this.ClusterManager = function ClusterManager(service) {
- this._log = Log.repository.getLogger("Sync.Service");
- this._log.level = Log.Level[Svc.Prefs.get("log.logger.service.main")];
-
- this.service = service;
-}
-ClusterManager.prototype = {
- get identity() {
- return this.service.identity;
- },
-
- /**
- * Obtain the cluster for the current user.
- *
- * Returns the string URL of the cluster or null on error.
- */
- _findCluster: function _findCluster() {
- this._log.debug("Finding cluster for user " + this.identity.username);
-
- // This should ideally use UserAPI10Client but the legacy hackiness is
- // strong with this code.
- let fail;
- let url = this.service.userAPIURI + this.identity.username + "/node/weave";
- let res = this.service.resource(url);
- try {
- let node = res.get();
- switch (node.status) {
- case 400:
- this.service.status.login = LOGIN_FAILED_LOGIN_REJECTED;
- fail = "Find cluster denied: " + this.service.errorHandler.errorStr(node);
- break;
- case 404:
- this._log.debug("Using serverURL as data cluster (multi-cluster support disabled)");
- return this.service.serverURL;
- case 0:
- case 200:
- if (node == "null") {
- node = null;
- }
- this._log.trace("_findCluster successfully returning " + node);
- return node;
- default:
- this.service.errorHandler.checkServerError(node);
- fail = "Unexpected response code: " + node.status;
- break;
- }
- } catch (e) {
- this._log.debug("Network error on findCluster");
- this.service.status.login = LOGIN_FAILED_NETWORK_ERROR;
- this.service.errorHandler.checkServerError(e);
- fail = e;
- }
- throw fail;
- },
-
- /**
- * Determine the cluster for the current user and update state.
- */
- setCluster: function setCluster() {
- // Make sure we didn't get some unexpected response for the cluster.
- let cluster = this._findCluster();
- this._log.debug("Cluster value = " + cluster);
- if (cluster == null) {
- return false;
- }
-
- // Convert from the funky "String object with additional properties" that
- // resource.js returns to a plain-old string.
- cluster = cluster.toString();
- // Don't update stuff if we already have the right cluster
- if (cluster == this.service.clusterURL) {
- return false;
- }
-
- this._log.debug("Setting cluster to " + cluster);
- this.service.clusterURL = cluster;
-
- return true;
- },
-
- getUserBaseURL: function getUserBaseURL() {
- // Legacy Sync and FxA Sync construct the userBaseURL differently. Legacy
- // Sync appends path components onto an empty path, and in FxA Sync, the
- // token server constructs this for us in an opaque manner. Since the
- // cluster manager already sets the clusterURL on Service and also has
- // access to the current identity, we added this functionality here.
-
- // If the clusterURL hasn't been set, the userBaseURL shouldn't be set
- // either. Some tests expect "undefined" to be returned here.
- if (!this.service.clusterURL) {
- return undefined;
- }
- let storageAPI = this.service.clusterURL + SYNC_API_VERSION + "/";
- return storageAPI + this.identity.username + "/";
- }
-};
-Object.freeze(ClusterManager.prototype);
--- a/services/sync/modules/status.js
+++ b/services/sync/modules/status.js
@@ -6,35 +6,30 @@ this.EXPORTED_SYMBOLS = ["Status"];
var Cc = Components.classes;
var Ci = Components.interfaces;
var Cr = Components.results;
var Cu = Components.utils;
Cu.import("resource://services-sync/constants.js");
Cu.import("resource://gre/modules/Log.jsm");
-Cu.import("resource://services-sync/identity.js");
Cu.import("resource://services-sync/browserid_identity.js");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://services-common/async.js");
this.Status = {
_log: Log.repository.getLogger("Sync.Status"),
__authManager: null,
ready: false,
get _authManager() {
if (this.__authManager) {
return this.__authManager;
}
- let service = Components.classes["@mozilla.org/weave/service;1"]
- .getService(Components.interfaces.nsISupports)
- .wrappedJSObject;
- let idClass = service.fxAccountsEnabled ? BrowserIDManager : IdentityManager;
- this.__authManager = new idClass();
+ this.__authManager = new BrowserIDManager();
this.__authManager.initialize();
return this.__authManager;
},
get service() {
return this._service;
},
--- a/services/sync/moz.build
+++ b/services/sync/moz.build
@@ -19,17 +19,16 @@ EXTRA_COMPONENTS += [
EXTRA_JS_MODULES['services-sync'] += [
'modules/addonsreconciler.js',
'modules/addonutils.js',
'modules/bookmark_validator.js',
'modules/browserid_identity.js',
'modules/collection_validator.js',
'modules/engines.js',
'modules/FxaMigrator.jsm',
- 'modules/identity.js',
'modules/keys.js',
'modules/main.js',
'modules/policies.js',
'modules/record.js',
'modules/resource.js',
'modules/rest.js',
'modules/service.js',
'modules/status.js',
@@ -54,17 +53,16 @@ EXTRA_JS_MODULES['services-sync'].engine
'modules/engines/forms.js',
'modules/engines/history.js',
'modules/engines/passwords.js',
'modules/engines/prefs.js',
'modules/engines/tabs.js',
]
EXTRA_JS_MODULES['services-sync'].stages += [
- 'modules/stages/cluster.js',
'modules/stages/declined.js',
'modules/stages/enginesync.js',
]
TESTING_JS_MODULES.services.sync += [
'modules-testing/fakeservices.js',
'modules-testing/fxa_utils.js',
'modules-testing/rotaryengine.js',
--- a/services/sync/services-sync.js
+++ b/services/sync/services-sync.js
@@ -14,17 +14,16 @@ pref("services.sync.sendVersionInfo", tr
pref("services.sync.scheduler.eolInterval", 604800); // 1 week
pref("services.sync.scheduler.idleInterval", 3600); // 1 hour
pref("services.sync.scheduler.activeInterval", 600); // 10 minutes
pref("services.sync.scheduler.immediateInterval", 90); // 1.5 minutes
pref("services.sync.scheduler.idleTime", 300); // 5 minutes
pref("services.sync.scheduler.fxa.singleDeviceInterval", 3600); // 1 hour
-pref("services.sync.scheduler.sync11.singleDeviceInterval", 86400); // 1 day
pref("services.sync.errorhandler.networkFailureReportTimeout", 1209600); // 2 weeks
pref("services.sync.engine.addons", true);
pref("services.sync.engine.bookmarks", true);
pref("services.sync.engine.history", true);
pref("services.sync.engine.passwords", true);
pref("services.sync.engine.prefs", true);
deleted file mode 100644
--- a/services/sync/tests/unit/test_identity_manager.js
+++ /dev/null
@@ -1,284 +0,0 @@
-/* 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/util.js");
-
-var identity = new IdentityManager();
-
-function run_test() {
- initTestLogging("Trace");
- Log.repository.getLogger("Sync.Identity").level = Log.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_changes() {
- _("Ensure changes to Sync Key have appropriate side-effects.");
-
- let im = new IdentityManager();
- let sk1 = Utils.generatePassphrase();
- let sk2 = Utils.generatePassphrase();
-
- im.account = "johndoe";
- do_check_eq(im.syncKey, null);
- do_check_eq(im.syncKeyBundle, null);
-
- im.syncKey = sk1;
- do_check_neq(im.syncKeyBundle, null);
-
- let ek1 = im.syncKeyBundle.encryptionKeyB64;
- let hk1 = im.syncKeyBundle.hmacKeyB64;
-
- // Change the Sync Key and ensure the Sync Key Bundle is updated.
- im.syncKey = sk2;
- let ek2 = im.syncKeyBundle.encryptionKeyB64;
- let hk2 = im.syncKeyBundle.hmacKeyB64;
-
- do_check_neq(ek1, ek2);
- do_check_neq(hk1, hk2);
-
- im.account = null;
-
- run_next_test();
-});
-
-add_test(function test_current_auth_state() {
- _("Ensure current auth state is reported properly.");
-
- let im = new IdentityManager();
- do_check_eq(im.currentAuthState, LOGIN_FAILED_NO_USERNAME);
-
- im.account = "johndoe";
- do_check_eq(im.currentAuthState, LOGIN_FAILED_NO_PASSWORD);
-
- im.basicPassword = "ilovejane";
- do_check_eq(im.currentAuthState, LOGIN_FAILED_NO_PASSPHRASE);
-
- im.syncKey = "foobar";
- do_check_eq(im.currentAuthState, LOGIN_FAILED_INVALID_PASSPHRASE);
-
- im.syncKey = null;
- do_check_eq(im.currentAuthState, LOGIN_FAILED_NO_PASSPHRASE);
-
- im.syncKey = Utils.generatePassphrase();
- do_check_eq(im.currentAuthState, STATUS_OK);
-
- im.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_load_modules.js
+++ b/services/sync/tests/unit/test_load_modules.js
@@ -11,25 +11,23 @@ const modules = [
"engines/clients.js",
"engines/extension-storage.js",
"engines/forms.js",
"engines/history.js",
"engines/passwords.js",
"engines/prefs.js",
"engines/tabs.js",
"engines.js",
- "identity.js",
"keys.js",
"main.js",
"policies.js",
"record.js",
"resource.js",
"rest.js",
"service.js",
- "stages/cluster.js",
"stages/declined.js",
"stages/enginesync.js",
"status.js",
"util.js",
];
const testingModules = [
"fakeservices.js",
--- a/services/sync/tests/unit/xpcshell.ini
+++ b/services/sync/tests/unit/xpcshell.ini
@@ -49,17 +49,16 @@ tags = addons
[test_resource_ua.js]
[test_syncstoragerequest.js]
# Generic Sync types.
[test_browserid_identity.js]
[test_collection_inc_get.js]
[test_collection_getBatched.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]
--- a/tools/lint/eslint/modules.json
+++ b/tools/lint/eslint/modules.json
@@ -30,17 +30,16 @@
"clients.js": ["ClientEngine", "ClientsRec"],
"CloudSyncAdapters.jsm": ["Adapters"],
"CloudSyncBookmarks.jsm": ["Bookmarks"],
"CloudSyncBookmarksFolderCache.jsm": ["FolderCache"],
"CloudSyncEventSource.jsm": ["EventSource"],
"CloudSyncLocal.jsm": ["Local"],
"CloudSyncPlacesWrapper.jsm": ["PlacesWrapper"],
"CloudSyncTabs.jsm": ["Tabs"],
- "cluster.js": ["ClusterManager"],
"collection_validator.js": ["CollectionValidator", "CollectionProblemData"],
"Console.jsm": ["console", "ConsoleAPI"],
"Constants.jsm": ["Roles", "Events", "Relations", "Filters", "States", "Prefilters"],
"ContactDB.jsm": ["ContactDB", "DB_NAME", "STORE_NAME", "SAVED_GETALL_STORE_NAME", "REVISION_STORE", "DB_VERSION"],
"content-server.jsm": ["init"],
"content.jsm": ["registerContentFrame"],
"ContentCrashHandlers.jsm": ["TabCrashHandler", "PluginCrashReporter", "UnsubmittedCrashHandler"],
"ContentObservers.jsm": [],
@@ -100,17 +99,16 @@
"GMPUtils.jsm": ["GMP_PLUGIN_IDS", "GMPPrefs", "GMPUtils", "OPEN_H264_ID", "WIDEVINE_ID"],
"hawkclient.js": ["HawkClient"],
"hawkrequest.js": ["HAWKAuthenticatedRESTRequest", "deriveHawkCredentials"],
"HelperApps.jsm": ["App", "HelperApps"],
"history.js": ["HistoryEngine", "HistoryRec"],
"history.jsm": ["HistoryEntry", "DumpHistory"],
"Http.jsm": ["httpRequest", "percentEncode"],
"httpd.js": ["HTTP_400", "HTTP_401", "HTTP_402", "HTTP_403", "HTTP_404", "HTTP_405", "HTTP_406", "HTTP_407", "HTTP_408", "HTTP_409", "HTTP_410", "HTTP_411", "HTTP_412", "HTTP_413", "HTTP_414", "HTTP_415", "HTTP_417", "HTTP_500", "HTTP_501", "HTTP_502", "HTTP_503", "HTTP_504", "HTTP_505", "HttpError", "HttpServer"],
- "identity.js": ["IdentityManager"],
"import_module.jsm": ["MODULE_IMPORTED", "MODULE_URI", "SUBMODULE_IMPORTED", "same_scope", "SUBMODULE_IMPORTED_TO_SCOPE"],
"import_sub_module.jsm": ["SUBMODULE_IMPORTED", "test_obj"],
"InlineSpellChecker.jsm": ["InlineSpellChecker", "SpellCheckHelper"],
"JNI.jsm": ["JNI", "android_log"],
"Jsbeautify.jsm": ["jsBeautify"],
"jsdebugger.jsm": ["addDebuggerToGlobal"],
"json2.js": ["JSON"],
"keys.js": ["BulkKeyBundle", "SyncKeyBundle"],