Bug 1207889 - handle the webchannel changepassword command and update the signed in user. r=kitcambridge

This commit is contained in:
Mark Hammond 2016-03-23 12:02:29 +11:00
Родитель c2a8ac0bf2
Коммит cb14fb1dec
5 изменённых файлов: 140 добавлений и 0 удалений

Просмотреть файл

@ -56,6 +56,7 @@ var publicProperties = [
"resendVerificationEmail",
"setSignedInUser",
"signOut",
"updateUserAccountData",
"updateDeviceRegistration",
"whenVerified"
];
@ -522,6 +523,34 @@ FxAccountsInternal.prototype = {
})
},
/**
* Update account data for the currently signed in user.
*
* @param credentials
* The credentials object containing the fields to be updated.
* This object must contain |email| and |uid| fields and they must
* match the currently signed in user.
*/
updateUserAccountData(credentials) {
log.debug("updateUserAccountData called with fields", Object.keys(credentials));
if (logPII) {
log.debug("updateUserAccountData called with data", credentials);
}
let currentAccountState = this.currentAccountState;
return currentAccountState.promiseInitialized.then(() => {
return currentAccountState.getUserAccountData(["email", "uid"]);
}).then(existing => {
if (existing.email != credentials.email || existing.uid != credentials.uid) {
throw new Error("The specified credentials aren't for the current user");
}
// We need to nuke email and uid as storage will complain if we try and
// update them (even when the value is the same)
credentials = Cu.cloneInto(credentials, {}); // clone it first
delete credentials.email;
delete credentials.uid;
return currentAccountState.updateUserAccountData(credentials);
});
},
/**
* returns a promise that fires with the assertion. If there is no verified

Просмотреть файл

@ -4,6 +4,7 @@
"use strict";
this.EXPORTED_SYMBOLS = [
"FxAccountsStorageManagerCanStoreField",
"FxAccountsStorageManager",
];
@ -16,6 +17,15 @@ Cu.import("resource://gre/modules/FxAccountsCommon.js");
Cu.import("resource://gre/modules/osfile.jsm");
Cu.import("resource://services-common/utils.js");
// A helper function so code can check what fields are able to be stored by
// the storage manager without having a reference to a manager instance.
function FxAccountsStorageManagerCanStoreField(fieldName) {
return FXA_PWDMGR_MEMORY_FIELDS.has(fieldName) ||
FXA_PWDMGR_PLAINTEXT_FIELDS.has(fieldName) ||
FXA_PWDMGR_SECURE_FIELDS.has(fieldName);
}
// The storage manager object.
this.FxAccountsStorageManager = function(options = {}) {
this.options = {
filename: options.filename || DEFAULT_STORAGE_FILENAME,

Просмотреть файл

@ -22,6 +22,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "WebChannel",
"resource://gre/modules/WebChannel.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "fxAccounts",
"resource://gre/modules/FxAccounts.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "FxAccountsStorageManagerCanStoreField",
"resource://gre/modules/FxAccountsStorage.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Weave",
"resource://services-sync/main.js");
@ -31,6 +33,7 @@ const COMMAND_LOGIN = "fxaccounts:login";
const COMMAND_LOGOUT = "fxaccounts:logout";
const COMMAND_DELETE = "fxaccounts:delete";
const COMMAND_SYNC_PREFERENCES = "fxaccounts:sync_preferences";
const COMMAND_CHANGE_PASSWORD = "fxaccounts:change_password";
const PREF_LAST_FXA_USER = "identity.fxaccounts.lastSignedInUserHash";
const PREF_SYNC_SHOW_CUSTOMIZATION = "services.sync-setup.ui.showCustomizationDialog";
@ -172,6 +175,9 @@ this.FxAccountsWebChannel.prototype = {
case COMMAND_SYNC_PREFERENCES:
this._helpers.openSyncPreferences(sendingContext.browser, data.entryPoint);
break;
case COMMAND_CHANGE_PASSWORD:
this._helpers.changePassword(data);
break;
default:
log.warn("Unrecognized FxAccountsWebChannel command", command);
break;
@ -275,6 +281,29 @@ this.FxAccountsWebChannelHelpers.prototype = {
});
},
changePassword(credentials) {
// If |credentials| has fields that aren't handled by accounts storage,
// updateUserAccountData will throw - mainly to prevent errors in code
// that hard-codes field names.
// However, in this case the field names aren't really in our control.
// We *could* still insist the server know what fields names are valid,
// but that makes life difficult for the server when Firefox adds new
// features (ie, new fields) - forcing the server to track a map of
// versions to supported field names doesn't buy us much.
// So we just remove field names we know aren't handled.
let newCredentials = {};
for (let name of Object.keys(credentials)) {
if (name == "email" || name == "uid" || FxAccountsStorageManagerCanStoreField(name)) {
newCredentials[name] = credentials[name];
} else {
log.info("changePassword ignoring unsupported field", name);
}
}
this._fxAccounts.updateUserAccountData(newCredentials).catch(err => {
log.error("Failed to update account data on password change", err);
});
},
/**
* Get the hash of account name of the previously signed in account
*/

Просмотреть файл

@ -262,6 +262,58 @@ add_task(function* test_get_signed_in_user_initially_unset() {
do_check_eq(result, null);
});
add_task(function* test_update_account_data() {
_("Check updateUserAccountData does the right thing.");
let account = MakeFxAccounts();
let credentials = {
email: "foo@example.com",
uid: "1234@lcip.org",
assertion: "foobar",
sessionToken: "dead",
kA: "beef",
kB: "cafe",
verified: true
};
yield account.setSignedInUser(credentials);
let newCreds = {
email: credentials.email,
uid: credentials.uid,
assertion: "new_assertion",
}
yield account.updateUserAccountData(newCreds);
do_check_eq((yield account.getSignedInUser()).assertion, "new_assertion",
"new field value was saved");
// but we should fail attempting to change email or uid.
newCreds = {
email: "someoneelse@example.com",
uid: credentials.uid,
assertion: "new_assertion",
}
yield Assert.rejects(account.updateUserAccountData(newCreds));
newCreds = {
email: credentials.email,
uid: "another_uid",
assertion: "new_assertion",
}
yield Assert.rejects(account.updateUserAccountData(newCreds));
// should fail without email or uid.
newCreds = {
assertion: "new_assertion",
}
yield Assert.rejects(account.updateUserAccountData(newCreds));
// and should fail with a field name that's not known by storage.
newCreds = {
email: credentials.email,
uid: "another_uid",
foo: "bar",
}
yield Assert.rejects(account.updateUserAccountData(newCreds));
});
add_task(function* test_getCertificateOffline() {
_("getCertificateOffline()");
let fxa = MakeFxAccounts();

Просмотреть файл

@ -319,6 +319,26 @@ add_test(function test_helpers_open_sync_preferences() {
helpers.openSyncPreferences(mockBrowser, "fxa:verification_complete");
});
add_test(function test_helpers_change_password() {
let updateCalled = false;
let helpers = new FxAccountsWebChannelHelpers({
fxAccounts: {
updateUserAccountData(credentials) {
do_check_true(credentials.hasOwnProperty("email"));
do_check_true(credentials.hasOwnProperty("uid"));
do_check_true(credentials.hasOwnProperty("kA"));
// "foo" isn't a field known by storage, so should be dropped.
do_check_false(credentials.hasOwnProperty("foo"));
updateCalled = true;
return Promise.resolve();
}
}
});
helpers.changePassword({ email: "email", uid: "uid", kA: "kA", foo: "foo" });
do_check_true(updateCalled);
run_next_test();
});
function run_test() {
run_next_test();
}