Bug 1296767 part 3 - Make BrowserID the only Sync identity manager. r=markh

MozReview-Commit-ID: IC7kRjgtPp8

--HG--
extra : rebase_source : ec10c24400afa4b1c1fd3840f31a9d57d498ec8a
This commit is contained in:
Edouard Oger 2017-01-13 15:02:21 -05:00
parent eb0c42bf7a
commit 49552f7919
17 changed files with 115 additions and 1305 deletions

View File

@ -374,9 +374,10 @@ var gSyncUI = {
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();

View File

@ -220,7 +220,6 @@ var gSyncSetup = {
feedback = server;
break;
case Weave.LOGIN_FAILED_LOGIN_REJECTED:
case Weave.LOGIN_FAILED_NO_USERNAME:
case Weave.LOGIN_FAILED_NO_PASSWORD:
feedback = password;
break;

View File

@ -102,22 +102,16 @@ WeaveService.prototype = {
*
* @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().
@ -142,10 +136,9 @@ WeaveService.prototype = {
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.

View File

@ -27,7 +27,6 @@ this.EXPORTED_SYMBOLS = [
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");
@ -170,11 +169,6 @@ this.makeIdentityConfig = function(overrides) {
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",
}
};
@ -183,10 +177,6 @@ this.makeIdentityConfig = function(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;
@ -260,44 +250,30 @@ this.configureIdentity = async function(identityOverrides, 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(),
@ -323,9 +299,9 @@ this.encryptPayload = function encryptPayload(cleartext) {
}
// 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|.
@ -339,15 +315,6 @@ this.add_identity_test = function(test, testFunction) {
}
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;

View File

@ -13,13 +13,11 @@ 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.
@ -89,8 +87,6 @@ this.BrowserIDManager = function BrowserIDManager() {
};
this.BrowserIDManager.prototype = {
__proto__: IdentityManager.prototype,
_fxaService: null,
_tokenServerClient: null,
// https://docs.services.mozilla.com/token/apis.html
@ -138,19 +134,6 @@ this.BrowserIDManager.prototype = {
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.
});
},
/**
@ -225,14 +208,13 @@ this.BrowserIDManager.prototype = {
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
@ -365,65 +347,43 @@ this.BrowserIDManager.prototype = {
return this._fxaService.localtimeOffsetMsec;
},
usernameFromAccount(val) {
// we don't differentiate between "username" and "account"
return val;
},
/**
* 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");
},
/**
* 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.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";
}
return null;
},
set syncKey(value) {
throw "syncKey setter should be not used in BrowserIDManager";
},
get syncKeyBundle() {
return this._syncKeyBundle;
},
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.resetSyncKey();
this.resetSyncKeyBundle();
this._token = null;
this._hashedUID = null;
// The cluster URL comes from the token, so resetting it to empty will
@ -432,12 +392,10 @@ this.BrowserIDManager.prototype = {
},
/**
* 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;
},
@ -458,6 +416,18 @@ this.BrowserIDManager.prototype = {
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.
*
@ -472,6 +442,7 @@ this.BrowserIDManager.prototype = {
" 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.
@ -801,11 +772,39 @@ this.BrowserIDManager.prototype = {
*/
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() {

View File

@ -19,7 +19,6 @@ 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");

View File

@ -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;
}
};

View File

@ -35,8 +35,7 @@ this.SyncScheduler = function SyncScheduler(service) {
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],
@ -48,14 +47,7 @@ SyncScheduler.prototype = {
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");

View File

@ -21,10 +21,10 @@ 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");
@ -154,7 +154,7 @@ Sync11Service.prototype = {
_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.
@ -303,14 +303,6 @@ Sync11Service.prototype = {
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();
@ -549,21 +541,9 @@ Sync11Service.prototype = {
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;
}
@ -699,7 +679,7 @@ Sync11Service.prototype = {
// We have no way of verifying the passphrase right now,
// so wait until remoteSetup to do so.
// Just make the most trivial checks.
if (!this.identity.syncKey) {
if (!this.identity.syncKeyBundle) {
this._log.warn("No passphrase in verifyLogin.");
this.status.login = LOGIN_FAILED_NO_PASSPHRASE;
return false;
@ -807,30 +787,6 @@ Sync11Service.prototype = {
}
},
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");
@ -855,7 +811,7 @@ Sync11Service.prototype = {
// 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");
@ -891,7 +847,6 @@ Sync11Service.prototype = {
// 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);
@ -904,15 +859,7 @@ Sync11Service.prototype = {
}
},
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) {
@ -920,17 +867,6 @@ Sync11Service.prototype = {
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.";
}
@ -946,12 +882,6 @@ Sync11Service.prototype = {
// 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.");
@ -1125,11 +1055,6 @@ Sync11Service.prototype = {
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;
@ -1144,11 +1069,6 @@ Sync11Service.prototype = {
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;
@ -1373,53 +1293,10 @@ Sync11Service.prototype = {
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();
@ -1528,9 +1405,6 @@ Sync11Service.prototype = {
engine.wipeClient();
}
}
// Save the password/passphrase just in-case they aren't restored by sync
this.persistLogin();
},
/**

View File

@ -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);

View File

@ -11,7 +11,6 @@ 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");
@ -25,11 +24,7 @@ this.Status = {
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;
},

View File

@ -24,7 +24,6 @@ EXTRA_JS_MODULES['services-sync'] += [
'modules/collection_validator.js',
'modules/engines.js',
'modules/FxaMigrator.jsm',
'modules/identity.js',
'modules/keys.js',
'modules/main.js',
'modules/policies.js',
@ -59,7 +58,6 @@ EXTRA_JS_MODULES['services-sync'].engines += [
]
EXTRA_JS_MODULES['services-sync'].stages += [
'modules/stages/cluster.js',
'modules/stages/declined.js',
'modules/stages/enginesync.js',
]

View File

@ -19,7 +19,6 @@ 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

View File

@ -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();
});

View File

@ -16,7 +16,6 @@ const modules = [
"engines/prefs.js",
"engines/tabs.js",
"engines.js",
"identity.js",
"keys.js",
"main.js",
"policies.js",
@ -24,7 +23,6 @@ const modules = [
"resource.js",
"rest.js",
"service.js",
"stages/cluster.js",
"stages/declined.js",
"stages/enginesync.js",
"status.js",

View File

@ -54,7 +54,6 @@ tags = addons
[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]

View File

@ -35,7 +35,6 @@
"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"],
@ -105,7 +104,6 @@
"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"],